Skip to content

Commit bb446b1

Browse files
feat(html): add HTML module support (html/experimental)
1 parent 0ccb3b7 commit bb446b1

16 files changed

+527
-2
lines changed

lib/Compilation.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ class Compilation extends Tapable {
220220
this.requestShortener
221221
);
222222
this.moduleTemplates = {
223+
html: new ModuleTemplate(this.outputOptions),
223224
javascript: new ModuleTemplate(this.runtimeTemplate),
224225
webassembly: new ModuleTemplate(this.runtimeTemplate)
225226
};
@@ -1673,6 +1674,7 @@ class Compilation extends Tapable {
16731674
if (!aEntry && bEntry) return -1;
16741675
return 0;
16751676
});
1677+
16761678
for (let i = 0; i < chunks.length; i++) {
16771679
const chunk = chunks[i];
16781680
const chunkHash = createHash(hashFunction);

lib/Template.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,4 +202,43 @@ module.exports = class Template {
202202
}
203203
return source;
204204
}
205+
206+
static renderHTMLChunk(chunk, filterFn, moduleTemplate, dependencyTemplates) {
207+
// if (!prefix) prefix = "";
208+
209+
const result = new ConcatSource();
210+
211+
const modules = chunk.getModules().filter(filterFn);
212+
const removedModules = chunk.removedModules;
213+
214+
const sources = modules.map(module => {
215+
return {
216+
id: module.id,
217+
source: moduleTemplate.render(module, dependencyTemplates, {
218+
chunk
219+
})
220+
};
221+
});
222+
223+
if (removedModules && removedModules.length > 0) {
224+
for (const id of removedModules) {
225+
sources.push({
226+
id: id,
227+
source: "<!-- HTML Module removed -->"
228+
});
229+
}
230+
}
231+
sources.sort().reduceRight((result, module, idx) => {
232+
if (idx !== 0) result.add("\n");
233+
234+
result.add(`\n<!-- ${module.id} -->\n`);
235+
result.add(module.source);
236+
237+
return result;
238+
}, result);
239+
240+
result.add("\n" /* + prefix */);
241+
242+
return result;
243+
}
205244
};

lib/WebpackOptionsApply.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,14 @@
66

77
const OptionsApply = require("./OptionsApply");
88

9+
// Module Types
910
const JavascriptModulesPlugin = require("./JavascriptModulesPlugin");
1011
const JsonModulesPlugin = require("./JsonModulesPlugin");
1112
const WebAssemblyModulesPlugin = require("./WebAssemblyModulesPlugin");
13+
const HTMLModulesPlugin = require("./html/HTMLModulesPlugin");
14+
15+
// Module Type Dependencies
16+
const HTMLDependencyPlugin = require("./html/HTMLDependency");
1217

1318
const LoaderTargetPlugin = require("./LoaderTargetPlugin");
1419
const FunctionModulePlugin = require("./FunctionModulePlugin");
@@ -270,6 +275,9 @@ class WebpackOptionsApply extends OptionsApply {
270275
new JavascriptModulesPlugin().apply(compiler);
271276
new JsonModulesPlugin().apply(compiler);
272277
new WebAssemblyModulesPlugin().apply(compiler);
278+
// HTML
279+
new HTMLModulesPlugin().apply(compiler);
280+
new HTMLDependencyPlugin().apply(compiler);
273281

274282
new EntryOptionPlugin().apply(compiler);
275283
compiler.hooks.entryOption.call(options.context, options.entry);

lib/WebpackOptionsDefaulter.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ class WebpackOptionsDefaulter extends OptionsDefaulter {
6767
{
6868
test: /\.wasm$/i,
6969
type: "webassembly/experimental"
70+
},
71+
{
72+
test: /\.html$/,
73+
type: "html/experimental"
7074
}
7175
]);
7276

@@ -94,6 +98,8 @@ class WebpackOptionsDefaulter extends OptionsDefaulter {
9498
// Elsewise prefix "[id]." in front of the basename to make it changing
9599
return filename.replace(/(^|\/)([^/]*(?:\?|$))/, "$1[id].$2");
96100
});
101+
// HTML
102+
this.set("output.HTMLModuleFilename", "[modulehash].module.html");
97103
this.set("output.webassemblyModuleFilename", "[modulehash].module.wasm");
98104
this.set("output.library", "");
99105
this.set("output.hotUpdateFunction", "make", options => {
@@ -281,7 +287,7 @@ class WebpackOptionsDefaulter extends OptionsDefaulter {
281287
this.set("resolve", "call", value => Object.assign({}, value));
282288
this.set("resolve.unsafeCache", true);
283289
this.set("resolve.modules", ["node_modules"]);
284-
this.set("resolve.extensions", [".wasm", ".mjs", ".js", ".json"]);
290+
this.set("resolve.extensions", [".html", ".wasm", ".mjs", ".js", ".json"]);
285291
this.set("resolve.mainFiles", ["index"]);
286292
this.set("resolve.aliasFields", "make", options => {
287293
if (options.target === "web" || options.target === "webworker")

lib/html/HTMLDependency.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
const { HTMLURLDependency, HTMLImportDependency } = require("./dependencies");
2+
3+
class HTMLDependencyPlugin {
4+
constructor(options) {
5+
this.plugin = "HTMLDependencyPlugin";
6+
this.options = options;
7+
}
8+
9+
apply(compiler) {
10+
const { plugin } = this;
11+
const { compilation } = compiler.hooks;
12+
13+
compilation.tap(plugin, (compilation, { normalModuleFactory }) => {
14+
const { dependencyFactories, dependencyTemplates } = compilation;
15+
16+
dependencyFactories.set(HTMLURLDependency, normalModuleFactory);
17+
dependencyFactories.set(HTMLImportDependency, normalModuleFactory);
18+
19+
dependencyTemplates.set(
20+
HTMLImportDependency,
21+
new HTMLImportDependency.Template()
22+
);
23+
});
24+
}
25+
}
26+
27+
module.exports = HTMLDependencyPlugin;

lib/html/HTMLGenerator.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class HTMLGenerator {
2+
generate(module) {
3+
return module.originalSource();
4+
}
5+
}
6+
7+
module.exports = HTMLGenerator;

lib/html/HTMLModulesPlugin.js

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
const HTMLParser = require("./HTMLParser");
2+
const HTMLGenerator = require("./HTMLGenerator");
3+
4+
const Template = require("../Template");
5+
const { ConcatSource } = require("webpack-sources");
6+
7+
class HTMLModulesPlugin {
8+
constructor() {
9+
this.plugin = {
10+
name: "HTMLModulesPlugin"
11+
};
12+
}
13+
14+
apply(compiler) {
15+
const { plugin } = this;
16+
const { compilation } = compiler.hooks;
17+
18+
compilation.tap(plugin, (compilation, { normalModuleFactory }) => {
19+
const { createParser, createGenerator } = normalModuleFactory.hooks;
20+
21+
createParser.for("html/experimental").tap(plugin, () => {
22+
return new HTMLParser();
23+
});
24+
25+
createGenerator.for("html/experimental").tap(plugin, () => {
26+
return new HTMLGenerator();
27+
});
28+
29+
const { chunkTemplate } = compilation;
30+
31+
chunkTemplate.hooks.renderManifest.tap(plugin, (result, options) => {
32+
const chunk = options.chunk;
33+
const output = options.outputOptions;
34+
35+
const { moduleTemplates, dependencyTemplates } = options;
36+
37+
for (const module of chunk.modulesIterable) {
38+
if (module.type && module.type.startsWith("html")) {
39+
const filenameTemplate = output.HTMLModuleFilename;
40+
41+
result.push({
42+
// render: () => this.renderHTMLModules(
43+
// module,
44+
// moduleTemplates.html,
45+
// dependencyTemplates
46+
// ),
47+
render: () =>
48+
this.renderHTML(
49+
chunkTemplate,
50+
chunk,
51+
moduleTemplates.html,
52+
dependencyTemplates
53+
),
54+
filenameTemplate,
55+
pathOptions: {
56+
module
57+
},
58+
identifier: `HTMLModule ${module.id}`,
59+
hash: module.hash
60+
});
61+
}
62+
}
63+
64+
return result;
65+
});
66+
});
67+
}
68+
69+
renderHTMLModules(module, moduleTemplate, dependencyTemplates) {
70+
return moduleTemplate.render(module, dependencyTemplates, {});
71+
}
72+
73+
renderHTML(chunkTemplate, chunk, moduleTemplate, dependencyTemplates) {
74+
const { modules /* render */ } = chunkTemplate.hooks;
75+
76+
const sources = Template.renderHTMLChunk(
77+
chunk,
78+
module => module.type.startsWith("html"),
79+
moduleTemplate,
80+
dependencyTemplates
81+
);
82+
83+
const core = modules.call(
84+
sources,
85+
chunk,
86+
moduleTemplate,
87+
dependencyTemplates
88+
);
89+
90+
// let source = render.call(
91+
// core,
92+
// chunk,
93+
// moduleTemplate,
94+
// dependencyTemplates
95+
// );
96+
97+
chunk.rendered = true;
98+
99+
return new ConcatSource(core);
100+
}
101+
}
102+
103+
module.exports = HTMLModulesPlugin;

0 commit comments

Comments
 (0)