Skip to content

Commit 536a204

Browse files
feat: added the postprocessor option (#518)
1 parent 31685f7 commit 536a204

13 files changed

+241
-40
lines changed

.cspell.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@
3636
"vspace",
3737
"jsbeautify",
3838
"Gitter",
39-
"commitlint"
39+
"commitlint",
40+
"postprocessor",
41+
"eslintcache"
4042
],
41-
4243
"ignorePaths": [
4344
"CHANGELOG.md",
4445
"package.json",

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ logs
88
*.log
99
npm-debug.log*
1010
.eslintcache
11+
.cspellcache
1112
/coverage
1213
/dist
1314
/local

README.md

Lines changed: 81 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ module.exports = {
6363

6464
- **[`sources`](#sources)**
6565
- **[`preprocessor`](#preprocessor)**
66+
- **[`postprocessor`](#postprocessor)**
6667
- **[`minimize`](#minimize)**
6768
- **[`esModule`](#esmodule)**
6869

@@ -490,10 +491,7 @@ module.exports = {
490491
Type:
491492

492493
```ts
493-
type preprocessor = (
494-
content: string | Buffer,
495-
loaderContext: LoaderContext,
496-
) => HTMLElement;
494+
type preprocessor = (content: string, loaderContext: LoaderContext) => string;
497495
```
498496

499497
Default: `undefined`
@@ -591,6 +589,85 @@ module.exports = {
591589
};
592590
```
593591

592+
### `postprocessor`
593+
594+
Type:
595+
596+
```ts
597+
type postprocessor = (content: string, loaderContext: LoaderContext) => string;
598+
```
599+
600+
Default: `undefined`
601+
602+
Allows post-processing of content after replacing all attributes (like `src`/`srcset`/etc).
603+
604+
**file.html**
605+
606+
```html
607+
<img src="image.png" />
608+
<img src="<%= 'Hello ' + (1+1) %>" />
609+
<img src="<%= require('./image.png') %>" />
610+
<img src="<%= new URL('./image.png', import.meta.url) %>" />
611+
<div><%= require('./gallery.html').default %></div>
612+
```
613+
614+
#### `function`
615+
616+
You can set the `postprocessor` option as a `function` instance.
617+
618+
**webpack.config.js**
619+
620+
```js
621+
const Handlebars = require("handlebars");
622+
623+
module.exports = {
624+
module: {
625+
rules: [
626+
{
627+
test: /\.html$/i,
628+
loader: "html-loader",
629+
options: {
630+
postprocessor: (content, loaderContext) => {
631+
return content.replace(/<%=/g, '" +').replace(/%>/g, '+ "');
632+
},
633+
},
634+
},
635+
],
636+
},
637+
};
638+
```
639+
640+
You can also set the `postprocessor` option as an asynchronous function instance.
641+
642+
For example:
643+
644+
**webpack.config.js**
645+
646+
```js
647+
const Handlebars = require("handlebars");
648+
649+
module.exports = {
650+
module: {
651+
rules: [
652+
{
653+
test: /\.hbs$/i,
654+
loader: "html-loader",
655+
options: {
656+
postprocessor: async (content, loaderContext) => {
657+
const value = await getValue();
658+
659+
return content
660+
.replace(/<%=/g, '" +')
661+
.replace(/%>/g, '+ "')
662+
.replace("my-value", value);
663+
},
664+
},
665+
},
666+
],
667+
},
668+
};
669+
```
670+
594671
### `minimize`
595672

596673
Type:

src/index.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,18 @@ export default async function loader(content) {
4141
plugins.push(minimizerPlugin({ minimize: options.minimize, errors }));
4242
}
4343

44-
const { html } = await pluginRunner(plugins).process(content);
44+
let { html } = await pluginRunner(plugins).process(content);
45+
46+
html = JSON.stringify(html)
47+
// Invalid in JavaScript but valid HTML
48+
.replace(/[\u2028\u2029]/g, (str) =>
49+
str === "\u2029" ? "\\u2029" : "\\u2028",
50+
);
51+
52+
if (options.postprocessor) {
53+
// eslint-disable-next-line no-param-reassign
54+
html = await options.postprocessor(html, this);
55+
}
4556

4657
for (const error of errors) {
4758
this.emitError(error instanceof Error ? error : new Error(error));

src/options.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@
4545
"description": "Allows pre-processing of content before handling.",
4646
"link": "https://github.com/webpack-contrib/html-loader#preprocessor"
4747
},
48+
"postprocessor": {
49+
"instanceof": "Function",
50+
"description": "Allows post-processing of content before handling.",
51+
"link": "https://github.com/webpack-contrib/html-loader#postprocessor"
52+
},
4853
"sources": {
4954
"anyOf": [
5055
{ "type": "boolean" },

src/utils.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,6 +1174,7 @@ function getSourcesOption(rawOptions) {
11741174
export function normalizeOptions(rawOptions, loaderContext) {
11751175
return {
11761176
preprocessor: rawOptions.preprocessor,
1177+
postprocessor: rawOptions.postprocessor,
11771178
sources: getSourcesOption(rawOptions),
11781179
minimize: getMinimizeOption(rawOptions, loaderContext),
11791180
esModule:
@@ -1251,12 +1252,7 @@ export function getImportCode(html, loaderContext, imports, options) {
12511252
}
12521253

12531254
export function getModuleCode(html, replacements) {
1254-
let code = JSON.stringify(html)
1255-
// Invalid in JavaScript but valid HTML
1256-
.replace(/[\u2028\u2029]/g, (str) =>
1257-
str === "\u2029" ? "\\u2029" : "\\u2028",
1258-
);
1259-
1255+
let code = html;
12601256
let replacersCode = "";
12611257

12621258
for (const item of replacements) {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`'postprocess' option should work with async "postprocessor" function option: errors 1`] = `[]`;
4+
5+
exports[`'postprocess' option should work with async "postprocessor" function option: module 1`] = `
6+
"// Imports
7+
import ___HTML_LOADER_GET_SOURCE_FROM_IMPORT___ from "../../src/runtime/getUrl.js";
8+
var ___HTML_LOADER_IMPORT_0___ = new URL("./image.png", import.meta.url);
9+
// Module
10+
var ___HTML_LOADER_REPLACEMENT_0___ = ___HTML_LOADER_GET_SOURCE_FROM_IMPORT___(___HTML_LOADER_IMPORT_0___);
11+
var code = "<div>\\n <p>{{firstname}} {{lastname}}</p>\\n <img src=\\"" + ___HTML_LOADER_REPLACEMENT_0___ + "\\" alt=\\"alt\\" />\\n<div>\\n";
12+
// Exports
13+
export default code;"
14+
`;
15+
16+
exports[`'postprocess' option should work with async "postprocessor" function option: result 1`] = `
17+
"<div>
18+
<p>{{firstname}} {{lastname}}</p>
19+
<img src="replaced_file_protocol_/webpack/public/path/image.png" alt="alt" />
20+
<div>
21+
"
22+
`;
23+
24+
exports[`'postprocess' option should work with async "postprocessor" function option: warnings 1`] = `[]`;
25+
26+
exports[`'postprocess' option should work with the "postprocessor" option: errors 1`] = `[]`;
27+
28+
exports[`'postprocess' option should work with the "postprocessor" option: module 1`] = `
29+
"// Imports
30+
import ___HTML_LOADER_GET_SOURCE_FROM_IMPORT___ from "../../src/runtime/getUrl.js";
31+
var ___HTML_LOADER_IMPORT_0___ = new URL("./image.png", import.meta.url);
32+
// Module
33+
var ___HTML_LOADER_REPLACEMENT_0___ = ___HTML_LOADER_GET_SOURCE_FROM_IMPORT___(___HTML_LOADER_IMPORT_0___);
34+
var code = "<img src=\\"" + ___HTML_LOADER_REPLACEMENT_0___ + "\\">\\n<img src=\\"" + 'Hello ' + (1+1) + "\\">\\n<img src=\\"" + require('./image.png') + "\\">\\n<img src=\\"" + new URL('./image.png', import.meta.url) + "\\">\\n<div>" + require('./gallery.html').default + "</div>\\n<!--Works fine, but need improve testing <div>< %= (await import('./gallery.html')).default % ></div>-->\\n";
35+
// Exports
36+
export default code;"
37+
`;
38+
39+
exports[`'postprocess' option should work with the "postprocessor" option: result 1`] = `
40+
"<img src="replaced_file_protocol_/webpack/public/path/image.png">
41+
<img src="Hello 2">
42+
<img src="/webpack/public/path/image.png">
43+
<img src="replaced_file_protocol_/webpack/public/path/image.png">
44+
<div><h2>Gallery</h2></div>
45+
<!--Works fine, but need improve testing <div>< %= (await import('./gallery.html')).default % ></div>-->
46+
"
47+
`;
48+
49+
exports[`'postprocess' option should work with the "postprocessor" option: warnings 1`] = `[]`;
Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`'process' option should work with Async "preprocessor" Function option: errors 1`] = `[]`;
3+
exports[`'preprocess' option should work with async "preprocessor" function option: errors 1`] = `[]`;
44

5-
exports[`'process' option should work with Async "preprocessor" Function option: module 1`] = `
5+
exports[`'preprocess' option should work with async "preprocessor" function option: module 1`] = `
66
"// Imports
77
import ___HTML_LOADER_GET_SOURCE_FROM_IMPORT___ from "../../src/runtime/getUrl.js";
88
var ___HTML_LOADER_IMPORT_0___ = new URL("./image.png", import.meta.url);
@@ -13,19 +13,19 @@ var code = "<div>\\n <p>Alexander Krasnoyarov</p>\\n <img src=\\"" + ___HTML_L
1313
export default code;"
1414
`;
1515
16-
exports[`'process' option should work with Async "preprocessor" Function option: result 1`] = `
16+
exports[`'preprocess' option should work with async "preprocessor" function option: result 1`] = `
1717
"<div>
1818
<p>Alexander Krasnoyarov</p>
1919
<img src="replaced_file_protocol_/webpack/public/path/image.png" alt="alt" />
2020
<div>
2121
"
2222
`;
2323
24-
exports[`'process' option should work with Async "preprocessor" Function option: warnings 1`] = `[]`;
24+
exports[`'preprocess' option should work with async "preprocessor" function option: warnings 1`] = `[]`;
2525
26-
exports[`'process' option should work with the "preprocessor" option #2: errors 1`] = `[]`;
26+
exports[`'preprocess' option should work with the "preprocessor" option #2: errors 1`] = `[]`;
2727
28-
exports[`'process' option should work with the "preprocessor" option #2: module 1`] = `
28+
exports[`'preprocess' option should work with the "preprocessor" option #2: module 1`] = `
2929
"// Imports
3030
import ___HTML_LOADER_GET_SOURCE_FROM_IMPORT___ from "../../src/runtime/getUrl.js";
3131
var ___HTML_LOADER_IMPORT_0___ = new URL("./image.png.webp", import.meta.url);
@@ -38,16 +38,16 @@ var code = "<picture><source type=\\"image/webp\\" srcset=\\"" + ___HTML_LOADER_
3838
export default code;"
3939
`;
4040
41-
exports[`'process' option should work with the "preprocessor" option #2: result 1`] = `
41+
exports[`'preprocess' option should work with the "preprocessor" option #2: result 1`] = `
4242
"<picture><source type="image/webp" srcset="replaced_file_protocol_/webpack/public/path/image.png.webp"><img src="replaced_file_protocol_/webpack/public/path/image.png"></picture>
4343
"
4444
`;
4545
46-
exports[`'process' option should work with the "preprocessor" option #2: warnings 1`] = `[]`;
46+
exports[`'preprocess' option should work with the "preprocessor" option #2: warnings 1`] = `[]`;
4747
48-
exports[`'process' option should work with the "preprocessor" option: errors 1`] = `[]`;
48+
exports[`'preprocess' option should work with the "preprocessor" option: errors 1`] = `[]`;
4949
50-
exports[`'process' option should work with the "preprocessor" option: module 1`] = `
50+
exports[`'preprocess' option should work with the "preprocessor" option: module 1`] = `
5151
"// Imports
5252
import ___HTML_LOADER_GET_SOURCE_FROM_IMPORT___ from "../../src/runtime/getUrl.js";
5353
var ___HTML_LOADER_IMPORT_0___ = new URL("./image.png", import.meta.url);
@@ -58,19 +58,19 @@ var code = "<div>\\n <p>Alexander Krasnoyarov</p>\\n <img src=\\"" + ___HTML_L
5858
export default code;"
5959
`;
6060
61-
exports[`'process' option should work with the "preprocessor" option: result 1`] = `
61+
exports[`'preprocess' option should work with the "preprocessor" option: result 1`] = `
6262
"<div>
6363
<p>Alexander Krasnoyarov</p>
6464
<img src="replaced_file_protocol_/webpack/public/path/image.png" alt="alt" />
6565
<div>
6666
"
6767
`;
6868
69-
exports[`'process' option should work with the "preprocessor" option: warnings 1`] = `[]`;
69+
exports[`'preprocess' option should work with the "preprocessor" option: warnings 1`] = `[]`;
7070
71-
exports[`'process' option should work with the Async "preprocessor" Function option #2: errors 1`] = `[]`;
71+
exports[`'preprocess' option should work with the async "preprocessor" function option #2: errors 1`] = `[]`;
7272
73-
exports[`'process' option should work with the Async "preprocessor" Function option #2: module 1`] = `
73+
exports[`'preprocess' option should work with the async "preprocessor" function option #2: module 1`] = `
7474
"// Imports
7575
import ___HTML_LOADER_GET_SOURCE_FROM_IMPORT___ from "../../src/runtime/getUrl.js";
7676
var ___HTML_LOADER_IMPORT_0___ = new URL("./image.png.webp", import.meta.url);
@@ -83,9 +83,9 @@ var code = "<picture><source type=\\"image/webp\\" srcset=\\"" + ___HTML_LOADER_
8383
export default code;"
8484
`;
8585
86-
exports[`'process' option should work with the Async "preprocessor" Function option #2: result 1`] = `
86+
exports[`'preprocess' option should work with the async "preprocessor" function option #2: result 1`] = `
8787
"<picture><source type="image/webp" srcset="replaced_file_protocol_/webpack/public/path/image.png.webp"><img src="replaced_file_protocol_/webpack/public/path/image.png"></picture>
8888
"
8989
`;
9090
91-
exports[`'process' option should work with the Async "preprocessor" Function option #2: warnings 1`] = `[]`;
91+
exports[`'preprocess' option should work with the async "preprocessor" function option #2: warnings 1`] = `[]`;

test/__snapshots__/validate-options.test.js.snap

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -95,47 +95,47 @@ exports[`validate options should throw an error on the "sources" option with "tr
9595
exports[`validate options should throw an error on the "unknown" option with "/test/" value 1`] = `
9696
"Invalid options object. HTML Loader has been initialized using an options object that does not match the API schema.
9797
- options has an unknown property 'unknown'. These properties are valid:
98-
object { preprocessor?, sources?, minimize?, esModule? }"
98+
object { preprocessor?, postprocessor?, sources?, minimize?, esModule? }"
9999
`;
100100
101101
exports[`validate options should throw an error on the "unknown" option with "[]" value 1`] = `
102102
"Invalid options object. HTML Loader has been initialized using an options object that does not match the API schema.
103103
- options has an unknown property 'unknown'. These properties are valid:
104-
object { preprocessor?, sources?, minimize?, esModule? }"
104+
object { preprocessor?, postprocessor?, sources?, minimize?, esModule? }"
105105
`;
106106
107107
exports[`validate options should throw an error on the "unknown" option with "{"foo":"bar"}" value 1`] = `
108108
"Invalid options object. HTML Loader has been initialized using an options object that does not match the API schema.
109109
- options has an unknown property 'unknown'. These properties are valid:
110-
object { preprocessor?, sources?, minimize?, esModule? }"
110+
object { preprocessor?, postprocessor?, sources?, minimize?, esModule? }"
111111
`;
112112
113113
exports[`validate options should throw an error on the "unknown" option with "{}" value 1`] = `
114114
"Invalid options object. HTML Loader has been initialized using an options object that does not match the API schema.
115115
- options has an unknown property 'unknown'. These properties are valid:
116-
object { preprocessor?, sources?, minimize?, esModule? }"
116+
object { preprocessor?, postprocessor?, sources?, minimize?, esModule? }"
117117
`;
118118
119119
exports[`validate options should throw an error on the "unknown" option with "1" value 1`] = `
120120
"Invalid options object. HTML Loader has been initialized using an options object that does not match the API schema.
121121
- options has an unknown property 'unknown'. These properties are valid:
122-
object { preprocessor?, sources?, minimize?, esModule? }"
122+
object { preprocessor?, postprocessor?, sources?, minimize?, esModule? }"
123123
`;
124124
125125
exports[`validate options should throw an error on the "unknown" option with "false" value 1`] = `
126126
"Invalid options object. HTML Loader has been initialized using an options object that does not match the API schema.
127127
- options has an unknown property 'unknown'. These properties are valid:
128-
object { preprocessor?, sources?, minimize?, esModule? }"
128+
object { preprocessor?, postprocessor?, sources?, minimize?, esModule? }"
129129
`;
130130
131131
exports[`validate options should throw an error on the "unknown" option with "test" value 1`] = `
132132
"Invalid options object. HTML Loader has been initialized using an options object that does not match the API schema.
133133
- options has an unknown property 'unknown'. These properties are valid:
134-
object { preprocessor?, sources?, minimize?, esModule? }"
134+
object { preprocessor?, postprocessor?, sources?, minimize?, esModule? }"
135135
`;
136136
137137
exports[`validate options should throw an error on the "unknown" option with "true" value 1`] = `
138138
"Invalid options object. HTML Loader has been initialized using an options object that does not match the API schema.
139139
- options has an unknown property 'unknown'. These properties are valid:
140-
object { preprocessor?, sources?, minimize?, esModule? }"
140+
object { preprocessor?, postprocessor?, sources?, minimize?, esModule? }"
141141
`;

test/fixtures/gallery.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<h2>Gallery</h2>

test/fixtures/postprocessor.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<img src="image.png">
2+
<img src="<%= 'Hello ' + (1+1) %>">
3+
<img src="<%= require('./image.png') %>">
4+
<img src="<%= new URL('./image.png', import.meta.url) %>">
5+
<div><%= require('./gallery.html').default %></div>
6+
<!--Works fine, but need improve testing <div>< %= (await import('./gallery.html')).default % ></div>-->

0 commit comments

Comments
 (0)