html-webpack-plugin 5.5.4 → 5.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +147 -146
- package/index.js +785 -399
- package/lib/cached-child-compiler.js +172 -126
- package/lib/child-compiler.js +90 -56
- package/lib/chunksorter.js +7 -3
- package/lib/errors.js +10 -8
- package/lib/html-tags.js +41 -13
- package/lib/loader.js +26 -11
- package/package.json +37 -19
- package/typings.d.ts +9 -8
- package/lib/hooks.js +0 -108
package/index.js
CHANGED
|
@@ -1,34 +1,124 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
|
|
2
|
+
"use strict";
|
|
3
3
|
|
|
4
|
-
const promisify = require(
|
|
4
|
+
const promisify = require("util").promisify;
|
|
5
5
|
|
|
6
|
-
const vm = require(
|
|
7
|
-
const fs = require(
|
|
8
|
-
const
|
|
9
|
-
const path = require(
|
|
10
|
-
const { CachedChildCompilation } = require(
|
|
6
|
+
const vm = require("vm");
|
|
7
|
+
const fs = require("fs");
|
|
8
|
+
const _uniq = require("lodash/uniq");
|
|
9
|
+
const path = require("path");
|
|
10
|
+
const { CachedChildCompilation } = require("./lib/cached-child-compiler");
|
|
11
11
|
|
|
12
|
-
const {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
const {
|
|
13
|
+
createHtmlTagObject,
|
|
14
|
+
htmlTagObjectToString,
|
|
15
|
+
HtmlTagArray,
|
|
16
|
+
} = require("./lib/html-tags");
|
|
17
|
+
const prettyError = require("./lib/errors.js");
|
|
18
|
+
const chunkSorter = require("./lib/chunksorter.js");
|
|
19
|
+
const { AsyncSeriesWaterfallHook } = require("tapable");
|
|
16
20
|
|
|
17
21
|
/** @typedef {import("./typings").HtmlTagObject} HtmlTagObject */
|
|
18
22
|
/** @typedef {import("./typings").Options} HtmlWebpackOptions */
|
|
19
23
|
/** @typedef {import("./typings").ProcessedOptions} ProcessedHtmlWebpackOptions */
|
|
20
24
|
/** @typedef {import("./typings").TemplateParameter} TemplateParameter */
|
|
21
25
|
/** @typedef {import("webpack").Compiler} Compiler */
|
|
26
|
+
/** @typedef {import("webpack").Compilation} Compilation */
|
|
27
|
+
/** @typedef {Required<Compilation["outputOptions"]["publicPath"]>} PublicPath */
|
|
22
28
|
/** @typedef {ReturnType<Compiler["getInfrastructureLogger"]>} Logger */
|
|
23
|
-
/** @typedef {
|
|
29
|
+
/** @typedef {Compilation["entrypoints"] extends Map<string, infer I> ? I : never} Entrypoint */
|
|
24
30
|
/** @typedef {Array<{ name: string, source: import('webpack').sources.Source, info?: import('webpack').AssetInfo }>} PreviousEmittedAssets */
|
|
25
31
|
/** @typedef {{ publicPath: string, js: Array<string>, css: Array<string>, manifest?: string, favicon?: string }} AssetsInformationByGroups */
|
|
32
|
+
/** @typedef {import("./typings").Hooks} HtmlWebpackPluginHooks */
|
|
33
|
+
/**
|
|
34
|
+
* @type {WeakMap<Compilation, HtmlWebpackPluginHooks>}}
|
|
35
|
+
*/
|
|
36
|
+
const compilationHooksMap = new WeakMap();
|
|
26
37
|
|
|
27
38
|
class HtmlWebpackPlugin {
|
|
39
|
+
// The following is the API definition for all available hooks
|
|
40
|
+
// For the TypeScript definition, see the Hooks type in typings.d.ts
|
|
41
|
+
/**
|
|
42
|
+
beforeAssetTagGeneration:
|
|
43
|
+
AsyncSeriesWaterfallHook<{
|
|
44
|
+
assets: {
|
|
45
|
+
publicPath: string,
|
|
46
|
+
js: Array<string>,
|
|
47
|
+
css: Array<string>,
|
|
48
|
+
favicon?: string | undefined,
|
|
49
|
+
manifest?: string | undefined
|
|
50
|
+
},
|
|
51
|
+
outputName: string,
|
|
52
|
+
plugin: HtmlWebpackPlugin
|
|
53
|
+
}>,
|
|
54
|
+
alterAssetTags:
|
|
55
|
+
AsyncSeriesWaterfallHook<{
|
|
56
|
+
assetTags: {
|
|
57
|
+
scripts: Array<HtmlTagObject>,
|
|
58
|
+
styles: Array<HtmlTagObject>,
|
|
59
|
+
meta: Array<HtmlTagObject>,
|
|
60
|
+
},
|
|
61
|
+
publicPath: string,
|
|
62
|
+
outputName: string,
|
|
63
|
+
plugin: HtmlWebpackPlugin
|
|
64
|
+
}>,
|
|
65
|
+
alterAssetTagGroups:
|
|
66
|
+
AsyncSeriesWaterfallHook<{
|
|
67
|
+
headTags: Array<HtmlTagObject | HtmlTagObject>,
|
|
68
|
+
bodyTags: Array<HtmlTagObject | HtmlTagObject>,
|
|
69
|
+
publicPath: string,
|
|
70
|
+
outputName: string,
|
|
71
|
+
plugin: HtmlWebpackPlugin
|
|
72
|
+
}>,
|
|
73
|
+
afterTemplateExecution:
|
|
74
|
+
AsyncSeriesWaterfallHook<{
|
|
75
|
+
html: string,
|
|
76
|
+
headTags: Array<HtmlTagObject | HtmlTagObject>,
|
|
77
|
+
bodyTags: Array<HtmlTagObject | HtmlTagObject>,
|
|
78
|
+
outputName: string,
|
|
79
|
+
plugin: HtmlWebpackPlugin,
|
|
80
|
+
}>,
|
|
81
|
+
beforeEmit:
|
|
82
|
+
AsyncSeriesWaterfallHook<{
|
|
83
|
+
html: string,
|
|
84
|
+
outputName: string,
|
|
85
|
+
plugin: HtmlWebpackPlugin,
|
|
86
|
+
}>,
|
|
87
|
+
afterEmit:
|
|
88
|
+
AsyncSeriesWaterfallHook<{
|
|
89
|
+
outputName: string,
|
|
90
|
+
plugin: HtmlWebpackPlugin
|
|
91
|
+
}>
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Returns all public hooks of the html webpack plugin for the given compilation
|
|
96
|
+
*
|
|
97
|
+
* @param {Compilation} compilation
|
|
98
|
+
* @returns {HtmlWebpackPluginHooks}
|
|
99
|
+
*/
|
|
100
|
+
static getCompilationHooks(compilation) {
|
|
101
|
+
let hooks = compilationHooksMap.get(compilation);
|
|
102
|
+
|
|
103
|
+
if (!hooks) {
|
|
104
|
+
hooks = {
|
|
105
|
+
beforeAssetTagGeneration: new AsyncSeriesWaterfallHook(["pluginArgs"]),
|
|
106
|
+
alterAssetTags: new AsyncSeriesWaterfallHook(["pluginArgs"]),
|
|
107
|
+
alterAssetTagGroups: new AsyncSeriesWaterfallHook(["pluginArgs"]),
|
|
108
|
+
afterTemplateExecution: new AsyncSeriesWaterfallHook(["pluginArgs"]),
|
|
109
|
+
beforeEmit: new AsyncSeriesWaterfallHook(["pluginArgs"]),
|
|
110
|
+
afterEmit: new AsyncSeriesWaterfallHook(["pluginArgs"]),
|
|
111
|
+
};
|
|
112
|
+
compilationHooksMap.set(compilation, hooks);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return hooks;
|
|
116
|
+
}
|
|
117
|
+
|
|
28
118
|
/**
|
|
29
119
|
* @param {HtmlWebpackOptions} [options]
|
|
30
120
|
*/
|
|
31
|
-
constructor
|
|
121
|
+
constructor(options) {
|
|
32
122
|
/** @type {HtmlWebpackOptions} */
|
|
33
123
|
// TODO remove me in the next major release
|
|
34
124
|
this.userOptions = options || {};
|
|
@@ -37,26 +127,29 @@ class HtmlWebpackPlugin {
|
|
|
37
127
|
// Default options
|
|
38
128
|
/** @type {ProcessedHtmlWebpackOptions} */
|
|
39
129
|
const defaultOptions = {
|
|
40
|
-
template:
|
|
130
|
+
template: "auto",
|
|
41
131
|
templateContent: false,
|
|
42
132
|
templateParameters: templateParametersGenerator,
|
|
43
|
-
filename:
|
|
44
|
-
publicPath:
|
|
133
|
+
filename: "index.html",
|
|
134
|
+
publicPath:
|
|
135
|
+
this.userOptions.publicPath === undefined
|
|
136
|
+
? "auto"
|
|
137
|
+
: this.userOptions.publicPath,
|
|
45
138
|
hash: false,
|
|
46
|
-
inject: this.userOptions.scriptLoading ===
|
|
47
|
-
scriptLoading:
|
|
139
|
+
inject: this.userOptions.scriptLoading === "blocking" ? "body" : "head",
|
|
140
|
+
scriptLoading: "defer",
|
|
48
141
|
compile: true,
|
|
49
142
|
favicon: false,
|
|
50
|
-
minify:
|
|
143
|
+
minify: "auto",
|
|
51
144
|
cache: true,
|
|
52
145
|
showErrors: true,
|
|
53
|
-
chunks:
|
|
146
|
+
chunks: "all",
|
|
54
147
|
excludeChunks: [],
|
|
55
|
-
chunksSortMode:
|
|
148
|
+
chunksSortMode: "auto",
|
|
56
149
|
meta: {},
|
|
57
150
|
base: false,
|
|
58
|
-
title:
|
|
59
|
-
xhtml: false
|
|
151
|
+
title: "Webpack App",
|
|
152
|
+
xhtml: false,
|
|
60
153
|
};
|
|
61
154
|
|
|
62
155
|
/** @type {ProcessedHtmlWebpackOptions} */
|
|
@@ -68,113 +161,152 @@ class HtmlWebpackPlugin {
|
|
|
68
161
|
* @param {Compiler} compiler
|
|
69
162
|
* @returns {void}
|
|
70
163
|
*/
|
|
71
|
-
apply
|
|
72
|
-
this.logger = compiler.getInfrastructureLogger(
|
|
164
|
+
apply(compiler) {
|
|
165
|
+
this.logger = compiler.getInfrastructureLogger("HtmlWebpackPlugin");
|
|
73
166
|
|
|
74
|
-
|
|
75
|
-
compiler.hooks.initialize.tap('HtmlWebpackPlugin', () => {
|
|
76
|
-
const options = this.options;
|
|
167
|
+
const options = this.options;
|
|
77
168
|
|
|
78
|
-
|
|
169
|
+
options.template = this.getTemplatePath(
|
|
170
|
+
this.options.template,
|
|
171
|
+
compiler.context,
|
|
172
|
+
);
|
|
79
173
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
174
|
+
// Assert correct option spelling
|
|
175
|
+
if (
|
|
176
|
+
options.scriptLoading !== "defer" &&
|
|
177
|
+
options.scriptLoading !== "blocking" &&
|
|
178
|
+
options.scriptLoading !== "module" &&
|
|
179
|
+
options.scriptLoading !== "systemjs-module"
|
|
180
|
+
) {
|
|
181
|
+
/** @type {Logger} */
|
|
182
|
+
(this.logger).error(
|
|
183
|
+
'The "scriptLoading" option need to be set to "defer", "blocking" or "module" or "systemjs-module"',
|
|
184
|
+
);
|
|
185
|
+
}
|
|
85
186
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
187
|
+
if (
|
|
188
|
+
options.inject !== true &&
|
|
189
|
+
options.inject !== false &&
|
|
190
|
+
options.inject !== "head" &&
|
|
191
|
+
options.inject !== "body"
|
|
192
|
+
) {
|
|
193
|
+
/** @type {Logger} */
|
|
194
|
+
(this.logger).error(
|
|
195
|
+
'The `inject` option needs to be set to true, false, "head" or "body',
|
|
196
|
+
);
|
|
197
|
+
}
|
|
90
198
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
199
|
+
if (
|
|
200
|
+
this.options.templateParameters !== false &&
|
|
201
|
+
typeof this.options.templateParameters !== "function" &&
|
|
202
|
+
typeof this.options.templateParameters !== "object"
|
|
203
|
+
) {
|
|
204
|
+
/** @type {Logger} */
|
|
205
|
+
(this.logger).error(
|
|
206
|
+
"The `templateParameters` has to be either a function or an object or false",
|
|
207
|
+
);
|
|
208
|
+
}
|
|
99
209
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
210
|
+
// Default metaOptions if no template is provided
|
|
211
|
+
if (
|
|
212
|
+
!this.userOptions.template &&
|
|
213
|
+
options.templateContent === false &&
|
|
214
|
+
options.meta
|
|
215
|
+
) {
|
|
216
|
+
options.meta = Object.assign(
|
|
217
|
+
{},
|
|
218
|
+
options.meta,
|
|
219
|
+
{
|
|
220
|
+
// TODO remove in the next major release
|
|
221
|
+
// From https://developer.mozilla.org/en-US/docs/Mozilla/Mobile/Viewport_meta_tag
|
|
222
|
+
viewport: "width=device-width, initial-scale=1",
|
|
223
|
+
},
|
|
224
|
+
this.userOptions.meta,
|
|
225
|
+
);
|
|
226
|
+
}
|
|
113
227
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
228
|
+
// entryName to fileName conversion function
|
|
229
|
+
const userOptionFilename =
|
|
230
|
+
this.userOptions.filename || this.options.filename;
|
|
231
|
+
const filenameFunction =
|
|
232
|
+
typeof userOptionFilename === "function"
|
|
117
233
|
? userOptionFilename
|
|
118
|
-
// Replace '[name]' with entry name
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
/** output filenames for the given entry names */
|
|
122
|
-
const entryNames = Object.keys(compiler.options.entry);
|
|
123
|
-
const outputFileNames = new Set((entryNames.length ? entryNames : ['main']).map(filenameFunction));
|
|
124
|
-
|
|
125
|
-
// Hook all options into the webpack compiler
|
|
126
|
-
outputFileNames.forEach((outputFileName) => {
|
|
127
|
-
// Instance variables to keep caching information for multiple builds
|
|
128
|
-
const assetJson = { value: undefined };
|
|
129
|
-
/**
|
|
130
|
-
* store the previous generated asset to emit them even if the content did not change
|
|
131
|
-
* to support watch mode for third party plugins like the clean-webpack-plugin or the compression plugin
|
|
132
|
-
* @type {PreviousEmittedAssets}
|
|
133
|
-
*/
|
|
134
|
-
const previousEmittedAssets = [];
|
|
234
|
+
: // Replace '[name]' with entry name
|
|
235
|
+
(entryName) => userOptionFilename.replace(/\[name\]/g, entryName);
|
|
135
236
|
|
|
136
|
-
|
|
137
|
-
|
|
237
|
+
/** output filenames for the given entry names */
|
|
238
|
+
const entryNames = Object.keys(compiler.options.entry);
|
|
239
|
+
const outputFileNames = new Set(
|
|
240
|
+
(entryNames.length ? entryNames : ["main"]).map(filenameFunction),
|
|
241
|
+
);
|
|
138
242
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
243
|
+
// Hook all options into the webpack compiler
|
|
244
|
+
outputFileNames.forEach((outputFileName) => {
|
|
245
|
+
// Instance variables to keep caching information for multiple builds
|
|
246
|
+
const assetJson = { value: undefined };
|
|
247
|
+
/**
|
|
248
|
+
* store the previous generated asset to emit them even if the content did not change
|
|
249
|
+
* to support watch mode for third party plugins like the clean-webpack-plugin or the compression plugin
|
|
250
|
+
* @type {PreviousEmittedAssets}
|
|
251
|
+
*/
|
|
252
|
+
const previousEmittedAssets = [];
|
|
253
|
+
|
|
254
|
+
// Inject child compiler plugin
|
|
255
|
+
const childCompilerPlugin = new CachedChildCompilation(compiler);
|
|
256
|
+
|
|
257
|
+
if (!this.options.templateContent) {
|
|
258
|
+
childCompilerPlugin.addEntry(this.options.template);
|
|
259
|
+
}
|
|
142
260
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
261
|
+
// convert absolute filename into relative so that webpack can
|
|
262
|
+
// generate it at correct location
|
|
263
|
+
let filename = outputFileName;
|
|
146
264
|
|
|
147
|
-
|
|
148
|
-
|
|
265
|
+
if (path.resolve(filename) === path.normalize(filename)) {
|
|
266
|
+
const outputPath =
|
|
267
|
+
/** @type {string} - Once initialized the path is always a string */ (
|
|
268
|
+
compiler.options.output.path
|
|
269
|
+
);
|
|
149
270
|
|
|
150
|
-
|
|
151
|
-
|
|
271
|
+
filename = path.relative(outputPath, filename);
|
|
272
|
+
}
|
|
152
273
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
274
|
+
compiler.hooks.thisCompilation.tap(
|
|
275
|
+
"HtmlWebpackPlugin",
|
|
276
|
+
/**
|
|
277
|
+
* Hook into the webpack compilation
|
|
278
|
+
* @param {Compilation} compilation
|
|
279
|
+
*/
|
|
280
|
+
(compilation) => {
|
|
281
|
+
compilation.hooks.processAssets.tapAsync(
|
|
282
|
+
{
|
|
283
|
+
name: "HtmlWebpackPlugin",
|
|
284
|
+
stage:
|
|
163
285
|
/**
|
|
164
286
|
* Generate the html after minification and dev tooling is done
|
|
165
287
|
*/
|
|
166
|
-
compiler.webpack.Compilation
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
288
|
+
compiler.webpack.Compilation
|
|
289
|
+
.PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE,
|
|
290
|
+
},
|
|
291
|
+
/**
|
|
292
|
+
* Hook into the process assets hook
|
|
293
|
+
* @param {any} _
|
|
294
|
+
* @param {(err?: Error) => void} callback
|
|
295
|
+
*/
|
|
296
|
+
(_, callback) => {
|
|
297
|
+
this.generateHTML(
|
|
298
|
+
compiler,
|
|
299
|
+
compilation,
|
|
300
|
+
filename,
|
|
301
|
+
childCompilerPlugin,
|
|
302
|
+
previousEmittedAssets,
|
|
303
|
+
assetJson,
|
|
304
|
+
callback,
|
|
305
|
+
);
|
|
306
|
+
},
|
|
307
|
+
);
|
|
308
|
+
},
|
|
309
|
+
);
|
|
178
310
|
});
|
|
179
311
|
}
|
|
180
312
|
|
|
@@ -185,23 +317,28 @@ class HtmlWebpackPlugin {
|
|
|
185
317
|
* @param {string} template The path to the template e.g. './index.html'
|
|
186
318
|
* @param {string} context The webpack base resolution path for relative paths e.g. process.cwd()
|
|
187
319
|
*/
|
|
188
|
-
getTemplatePath
|
|
189
|
-
if (template ===
|
|
190
|
-
template = path.resolve(context,
|
|
320
|
+
getTemplatePath(template, context) {
|
|
321
|
+
if (template === "auto") {
|
|
322
|
+
template = path.resolve(context, "src/index.ejs");
|
|
191
323
|
if (!fs.existsSync(template)) {
|
|
192
|
-
template = path.join(__dirname,
|
|
324
|
+
template = path.join(__dirname, "default_index.ejs");
|
|
193
325
|
}
|
|
194
326
|
}
|
|
195
327
|
|
|
196
328
|
// If the template doesn't use a loader use the lodash template loader
|
|
197
|
-
if (template.indexOf(
|
|
198
|
-
template =
|
|
329
|
+
if (template.indexOf("!") === -1) {
|
|
330
|
+
template =
|
|
331
|
+
require.resolve("./lib/loader.js") +
|
|
332
|
+
"!" +
|
|
333
|
+
path.resolve(context, template);
|
|
199
334
|
}
|
|
200
335
|
|
|
201
336
|
// Resolve template path
|
|
202
337
|
return template.replace(
|
|
203
338
|
/([!])([^/\\][^!?]+|[^/\\!?])($|\?[^!?\n]+$)/,
|
|
204
|
-
(match, prefix, filepath, postfix) =>
|
|
339
|
+
(match, prefix, filepath, postfix) =>
|
|
340
|
+
prefix + path.resolve(filepath) + postfix,
|
|
341
|
+
);
|
|
205
342
|
}
|
|
206
343
|
|
|
207
344
|
/**
|
|
@@ -212,15 +349,21 @@ class HtmlWebpackPlugin {
|
|
|
212
349
|
* @param {string[]|'all'} includedChunks
|
|
213
350
|
* @param {string[]} excludedChunks
|
|
214
351
|
*/
|
|
215
|
-
filterEntryChunks
|
|
216
|
-
return chunks.filter(chunkName => {
|
|
352
|
+
filterEntryChunks(chunks, includedChunks, excludedChunks) {
|
|
353
|
+
return chunks.filter((chunkName) => {
|
|
217
354
|
// Skip if the chunks should be filtered and the given chunk was not added explicity
|
|
218
|
-
if (
|
|
355
|
+
if (
|
|
356
|
+
Array.isArray(includedChunks) &&
|
|
357
|
+
includedChunks.indexOf(chunkName) === -1
|
|
358
|
+
) {
|
|
219
359
|
return false;
|
|
220
360
|
}
|
|
221
361
|
|
|
222
362
|
// Skip if the chunks should be filtered and the given chunk was excluded explicity
|
|
223
|
-
if (
|
|
363
|
+
if (
|
|
364
|
+
Array.isArray(excludedChunks) &&
|
|
365
|
+
excludedChunks.indexOf(chunkName) !== -1
|
|
366
|
+
) {
|
|
224
367
|
return false;
|
|
225
368
|
}
|
|
226
369
|
|
|
@@ -237,13 +380,13 @@ class HtmlWebpackPlugin {
|
|
|
237
380
|
* @param {string|((entryNameA: string, entryNameB: string) => number)} sortMode
|
|
238
381
|
* @param {Compilation} compilation
|
|
239
382
|
*/
|
|
240
|
-
sortEntryChunks
|
|
383
|
+
sortEntryChunks(entryNames, sortMode, compilation) {
|
|
241
384
|
// Custom function
|
|
242
|
-
if (typeof sortMode ===
|
|
385
|
+
if (typeof sortMode === "function") {
|
|
243
386
|
return entryNames.sort(sortMode);
|
|
244
387
|
}
|
|
245
388
|
// Check if the given sort mode is a valid chunkSorter sort mode
|
|
246
|
-
if (typeof chunkSorter[sortMode] !==
|
|
389
|
+
if (typeof chunkSorter[sortMode] !== "undefined") {
|
|
247
390
|
return chunkSorter[sortMode](entryNames, compilation, this.options);
|
|
248
391
|
}
|
|
249
392
|
throw new Error('"' + sortMode + '" is not a valid chunk sort mode');
|
|
@@ -271,17 +414,18 @@ class HtmlWebpackPlugin {
|
|
|
271
414
|
* @private
|
|
272
415
|
* @param {string} filePath
|
|
273
416
|
*/
|
|
274
|
-
urlencodePath
|
|
417
|
+
urlencodePath(filePath) {
|
|
275
418
|
// People use the filepath in quite unexpected ways.
|
|
276
419
|
// Try to extract the first querystring of the url:
|
|
277
420
|
//
|
|
278
421
|
// some+path/demo.html?value=abc?def
|
|
279
422
|
//
|
|
280
|
-
const queryStringStart = filePath.indexOf(
|
|
281
|
-
const urlPath =
|
|
423
|
+
const queryStringStart = filePath.indexOf("?");
|
|
424
|
+
const urlPath =
|
|
425
|
+
queryStringStart === -1 ? filePath : filePath.substr(0, queryStringStart);
|
|
282
426
|
const queryString = filePath.substr(urlPath.length);
|
|
283
427
|
// Encode all parts except '/' which are not part of the querystring:
|
|
284
|
-
const encodedUrlPath = urlPath.split(
|
|
428
|
+
const encodedUrlPath = urlPath.split("/").map(encodeURIComponent).join("/");
|
|
285
429
|
return encodedUrlPath + queryString;
|
|
286
430
|
}
|
|
287
431
|
|
|
@@ -290,14 +434,15 @@ class HtmlWebpackPlugin {
|
|
|
290
434
|
* E.g. http://localhost:8080/ -> http://localhost:8080/?50c9096ba6183fd728eeb065a26ec175
|
|
291
435
|
*
|
|
292
436
|
* @private
|
|
293
|
-
* @param {string} url
|
|
437
|
+
* @param {string | undefined} url
|
|
294
438
|
* @param {string} hash
|
|
295
439
|
*/
|
|
296
|
-
appendHash
|
|
440
|
+
appendHash(url, hash) {
|
|
297
441
|
if (!url) {
|
|
298
442
|
return url;
|
|
299
443
|
}
|
|
300
|
-
|
|
444
|
+
|
|
445
|
+
return url + (url.indexOf("?") === -1 ? "?" : "&") + hash;
|
|
301
446
|
}
|
|
302
447
|
|
|
303
448
|
/**
|
|
@@ -310,30 +455,42 @@ class HtmlWebpackPlugin {
|
|
|
310
455
|
* @param {string | 'auto'} customPublicPath
|
|
311
456
|
* @returns {string}
|
|
312
457
|
*/
|
|
313
|
-
getPublicPath
|
|
458
|
+
getPublicPath(compilation, filename, customPublicPath) {
|
|
314
459
|
/**
|
|
315
460
|
* @type {string} the configured public path to the asset root
|
|
316
461
|
* if a path publicPath is set in the current webpack config use it otherwise
|
|
317
462
|
* fallback to a relative path
|
|
318
463
|
*/
|
|
319
|
-
const webpackPublicPath = compilation.getAssetPath(
|
|
464
|
+
const webpackPublicPath = compilation.getAssetPath(
|
|
465
|
+
/** @type {NonNullable<Compilation["outputOptions"]["publicPath"]>} */ (
|
|
466
|
+
compilation.outputOptions.publicPath
|
|
467
|
+
),
|
|
468
|
+
{ hash: compilation.hash },
|
|
469
|
+
);
|
|
320
470
|
// Webpack 5 introduced "auto" as default value
|
|
321
|
-
const isPublicPathDefined = webpackPublicPath !==
|
|
471
|
+
const isPublicPathDefined = webpackPublicPath !== "auto";
|
|
322
472
|
|
|
323
473
|
let publicPath =
|
|
324
|
-
// If the html-webpack-plugin options contain a custom public path
|
|
325
|
-
customPublicPath !==
|
|
474
|
+
// If the html-webpack-plugin options contain a custom public path unset it
|
|
475
|
+
customPublicPath !== "auto"
|
|
326
476
|
? customPublicPath
|
|
327
|
-
:
|
|
328
|
-
// If a hard coded public path exists use it
|
|
329
|
-
|
|
330
|
-
// If no public path was set get a relative url path
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
477
|
+
: isPublicPathDefined
|
|
478
|
+
? // If a hard coded public path exists use it
|
|
479
|
+
webpackPublicPath
|
|
480
|
+
: // If no public path was set get a relative url path
|
|
481
|
+
path
|
|
482
|
+
.relative(
|
|
483
|
+
path.resolve(
|
|
484
|
+
/** @type {string} */ (compilation.options.output.path),
|
|
485
|
+
path.dirname(filename),
|
|
486
|
+
),
|
|
487
|
+
/** @type {string} */ (compilation.options.output.path),
|
|
488
|
+
)
|
|
489
|
+
.split(path.sep)
|
|
490
|
+
.join("/");
|
|
491
|
+
|
|
492
|
+
if (publicPath.length && publicPath.substr(-1, 1) !== "/") {
|
|
493
|
+
publicPath += "/";
|
|
337
494
|
}
|
|
338
495
|
|
|
339
496
|
return publicPath;
|
|
@@ -348,9 +505,13 @@ class HtmlWebpackPlugin {
|
|
|
348
505
|
* @param {string[]} entryNames
|
|
349
506
|
* @returns {AssetsInformationByGroups}
|
|
350
507
|
*/
|
|
351
|
-
getAssetsInformationByGroups
|
|
508
|
+
getAssetsInformationByGroups(compilation, outputName, entryNames) {
|
|
352
509
|
/** The public path used inside the html file */
|
|
353
|
-
const publicPath = this.getPublicPath(
|
|
510
|
+
const publicPath = this.getPublicPath(
|
|
511
|
+
compilation,
|
|
512
|
+
outputName,
|
|
513
|
+
this.options.publicPath,
|
|
514
|
+
);
|
|
354
515
|
/**
|
|
355
516
|
* @type {AssetsInformationByGroups}
|
|
356
517
|
*/
|
|
@@ -362,14 +523,19 @@ class HtmlWebpackPlugin {
|
|
|
362
523
|
// Will contain all css files
|
|
363
524
|
css: [],
|
|
364
525
|
// Will contain the html5 appcache manifest files if it exists
|
|
365
|
-
manifest: Object.keys(compilation.assets).find(
|
|
526
|
+
manifest: Object.keys(compilation.assets).find(
|
|
527
|
+
(assetFile) => path.extname(assetFile) === ".appcache",
|
|
528
|
+
),
|
|
366
529
|
// Favicon
|
|
367
|
-
favicon: undefined
|
|
530
|
+
favicon: undefined,
|
|
368
531
|
};
|
|
369
532
|
|
|
370
533
|
// Append a hash for cache busting
|
|
371
534
|
if (this.options.hash && assets.manifest) {
|
|
372
|
-
assets.manifest = this.appendHash(
|
|
535
|
+
assets.manifest = this.appendHash(
|
|
536
|
+
assets.manifest,
|
|
537
|
+
/** @type {string} */ (compilation.hash),
|
|
538
|
+
);
|
|
373
539
|
}
|
|
374
540
|
|
|
375
541
|
// Extract paths to .js, .mjs and .css files from the current compilation
|
|
@@ -379,7 +545,9 @@ class HtmlWebpackPlugin {
|
|
|
379
545
|
for (let i = 0; i < entryNames.length; i++) {
|
|
380
546
|
const entryName = entryNames[i];
|
|
381
547
|
/** entryPointUnfilteredFiles - also includes hot module update files */
|
|
382
|
-
const entryPointUnfilteredFiles =
|
|
548
|
+
const entryPointUnfilteredFiles = /** @type {Entrypoint} */ (
|
|
549
|
+
compilation.entrypoints.get(entryName)
|
|
550
|
+
).getFiles();
|
|
383
551
|
const entryPointFiles = entryPointUnfilteredFiles.filter((chunkFile) => {
|
|
384
552
|
const asset = compilation.getAsset(chunkFile);
|
|
385
553
|
|
|
@@ -390,21 +558,28 @@ class HtmlWebpackPlugin {
|
|
|
390
558
|
// Prevent hot-module files from being included:
|
|
391
559
|
const assetMetaInformation = asset.info || {};
|
|
392
560
|
|
|
393
|
-
return !(
|
|
561
|
+
return !(
|
|
562
|
+
assetMetaInformation.hotModuleReplacement ||
|
|
563
|
+
assetMetaInformation.development
|
|
564
|
+
);
|
|
394
565
|
});
|
|
395
566
|
// Prepend the publicPath and append the hash depending on the
|
|
396
567
|
// webpack.output.publicPath and hashOptions
|
|
397
568
|
// E.g. bundle.js -> /bundle.js?hash
|
|
398
|
-
const entryPointPublicPaths = entryPointFiles
|
|
399
|
-
.
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
569
|
+
const entryPointPublicPaths = entryPointFiles.map((chunkFile) => {
|
|
570
|
+
const entryPointPublicPath = publicPath + this.urlencodePath(chunkFile);
|
|
571
|
+
return this.options.hash
|
|
572
|
+
? this.appendHash(
|
|
573
|
+
entryPointPublicPath,
|
|
574
|
+
/** @type {string} */ (compilation.hash),
|
|
575
|
+
)
|
|
576
|
+
: entryPointPublicPath;
|
|
577
|
+
});
|
|
405
578
|
|
|
406
579
|
entryPointPublicPaths.forEach((entryPointPublicPath) => {
|
|
407
|
-
const extMatch = extensionRegexp.exec(
|
|
580
|
+
const extMatch = extensionRegexp.exec(
|
|
581
|
+
/** @type {string} */ (entryPointPublicPath),
|
|
582
|
+
);
|
|
408
583
|
|
|
409
584
|
// Skip if the public path is not a .css, .mjs or .js file
|
|
410
585
|
if (!extMatch) {
|
|
@@ -420,7 +595,7 @@ class HtmlWebpackPlugin {
|
|
|
420
595
|
entryPointPublicPathMap[entryPointPublicPath] = true;
|
|
421
596
|
|
|
422
597
|
// ext will contain .js or .css, because .mjs recognizes as .js
|
|
423
|
-
const ext = extMatch[1] ===
|
|
598
|
+
const ext = extMatch[1] === "mjs" ? "js" : extMatch[1];
|
|
424
599
|
|
|
425
600
|
assets[ext].push(entryPointPublicPath);
|
|
426
601
|
});
|
|
@@ -441,18 +616,22 @@ class HtmlWebpackPlugin {
|
|
|
441
616
|
* @param {string} templateFilename
|
|
442
617
|
* @returns {Promise<string | (() => string | Promise<string>)>}
|
|
443
618
|
*/
|
|
444
|
-
evaluateCompilationResult
|
|
619
|
+
evaluateCompilationResult(source, publicPath, templateFilename) {
|
|
445
620
|
if (!source) {
|
|
446
|
-
return Promise.reject(
|
|
621
|
+
return Promise.reject(
|
|
622
|
+
new Error("The child compilation didn't provide a result"),
|
|
623
|
+
);
|
|
447
624
|
}
|
|
448
625
|
|
|
449
626
|
// The LibraryTemplatePlugin stores the template result in a local variable.
|
|
450
627
|
// By adding it to the end the value gets extracted during evaluation
|
|
451
|
-
if (source.indexOf(
|
|
452
|
-
source +=
|
|
628
|
+
if (source.indexOf("HTML_WEBPACK_PLUGIN_RESULT") >= 0) {
|
|
629
|
+
source += ";\nHTML_WEBPACK_PLUGIN_RESULT";
|
|
453
630
|
}
|
|
454
631
|
|
|
455
|
-
const templateWithoutLoaders = templateFilename
|
|
632
|
+
const templateWithoutLoaders = templateFilename
|
|
633
|
+
.replace(/^.+!/, "")
|
|
634
|
+
.replace(/\?.+$/, "");
|
|
456
635
|
const vmContext = vm.createContext({
|
|
457
636
|
...global,
|
|
458
637
|
HTML_WEBPACK_PLUGIN: true,
|
|
@@ -507,10 +686,12 @@ class HtmlWebpackPlugin {
|
|
|
507
686
|
WebAssembly: global.WebAssembly,
|
|
508
687
|
WritableStream: global.WritableStream,
|
|
509
688
|
WritableStreamDefaultController: global.WritableStreamDefaultController,
|
|
510
|
-
WritableStreamDefaultWriter: global.WritableStreamDefaultWriter
|
|
689
|
+
WritableStreamDefaultWriter: global.WritableStreamDefaultWriter,
|
|
511
690
|
});
|
|
512
691
|
|
|
513
|
-
const vmScript = new vm.Script(source, {
|
|
692
|
+
const vmScript = new vm.Script(source, {
|
|
693
|
+
filename: templateWithoutLoaders,
|
|
694
|
+
});
|
|
514
695
|
|
|
515
696
|
// Evaluate code and cast to string
|
|
516
697
|
let newSource;
|
|
@@ -521,13 +702,21 @@ class HtmlWebpackPlugin {
|
|
|
521
702
|
return Promise.reject(e);
|
|
522
703
|
}
|
|
523
704
|
|
|
524
|
-
if (
|
|
705
|
+
if (
|
|
706
|
+
typeof newSource === "object" &&
|
|
707
|
+
newSource.__esModule &&
|
|
708
|
+
newSource.default
|
|
709
|
+
) {
|
|
525
710
|
newSource = newSource.default;
|
|
526
711
|
}
|
|
527
712
|
|
|
528
|
-
return typeof newSource ===
|
|
713
|
+
return typeof newSource === "string" || typeof newSource === "function"
|
|
529
714
|
? Promise.resolve(newSource)
|
|
530
|
-
: Promise.reject(
|
|
715
|
+
: Promise.reject(
|
|
716
|
+
new Error(
|
|
717
|
+
'The loader "' + templateWithoutLoaders + "\" didn't return html.",
|
|
718
|
+
),
|
|
719
|
+
);
|
|
531
720
|
}
|
|
532
721
|
|
|
533
722
|
/**
|
|
@@ -537,15 +726,17 @@ class HtmlWebpackPlugin {
|
|
|
537
726
|
* @param {Array<HtmlTagObject>} assetTagGroup
|
|
538
727
|
* @returns {Array<HtmlTagObject>}
|
|
539
728
|
*/
|
|
540
|
-
prepareAssetTagGroupForRendering
|
|
729
|
+
prepareAssetTagGroupForRendering(assetTagGroup) {
|
|
541
730
|
const xhtml = this.options.xhtml;
|
|
542
|
-
return HtmlTagArray.from(
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
731
|
+
return HtmlTagArray.from(
|
|
732
|
+
assetTagGroup.map((assetTag) => {
|
|
733
|
+
const copiedAssetTag = Object.assign({}, assetTag);
|
|
734
|
+
copiedAssetTag.toString = function () {
|
|
735
|
+
return htmlTagObjectToString(this, xhtml);
|
|
736
|
+
};
|
|
737
|
+
return copiedAssetTag;
|
|
738
|
+
}),
|
|
739
|
+
);
|
|
549
740
|
}
|
|
550
741
|
|
|
551
742
|
/**
|
|
@@ -560,32 +751,50 @@ class HtmlWebpackPlugin {
|
|
|
560
751
|
}} assetTags
|
|
561
752
|
* @returns {Promise<{[key: any]: any}>}
|
|
562
753
|
*/
|
|
563
|
-
getTemplateParameters
|
|
754
|
+
getTemplateParameters(compilation, assetsInformationByGroups, assetTags) {
|
|
564
755
|
const templateParameters = this.options.templateParameters;
|
|
565
756
|
|
|
566
757
|
if (templateParameters === false) {
|
|
567
758
|
return Promise.resolve({});
|
|
568
759
|
}
|
|
569
760
|
|
|
570
|
-
if (
|
|
571
|
-
|
|
761
|
+
if (
|
|
762
|
+
typeof templateParameters !== "function" &&
|
|
763
|
+
typeof templateParameters !== "object"
|
|
764
|
+
) {
|
|
765
|
+
throw new Error(
|
|
766
|
+
"templateParameters has to be either a function or an object",
|
|
767
|
+
);
|
|
572
768
|
}
|
|
573
769
|
|
|
574
|
-
const templateParameterFunction =
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
770
|
+
const templateParameterFunction =
|
|
771
|
+
typeof templateParameters === "function"
|
|
772
|
+
? // A custom function can overwrite the entire template parameter preparation
|
|
773
|
+
templateParameters
|
|
774
|
+
: // If the template parameters is an object merge it with the default values
|
|
775
|
+
(compilation, assetsInformationByGroups, assetTags, options) =>
|
|
776
|
+
Object.assign(
|
|
777
|
+
{},
|
|
778
|
+
templateParametersGenerator(
|
|
779
|
+
compilation,
|
|
780
|
+
assetsInformationByGroups,
|
|
781
|
+
assetTags,
|
|
782
|
+
options,
|
|
783
|
+
),
|
|
784
|
+
templateParameters,
|
|
785
|
+
);
|
|
582
786
|
const preparedAssetTags = {
|
|
583
787
|
headTags: this.prepareAssetTagGroupForRendering(assetTags.headTags),
|
|
584
|
-
bodyTags: this.prepareAssetTagGroupForRendering(assetTags.bodyTags)
|
|
788
|
+
bodyTags: this.prepareAssetTagGroupForRendering(assetTags.bodyTags),
|
|
585
789
|
};
|
|
586
|
-
return Promise
|
|
587
|
-
|
|
588
|
-
|
|
790
|
+
return Promise.resolve().then(() =>
|
|
791
|
+
templateParameterFunction(
|
|
792
|
+
compilation,
|
|
793
|
+
assetsInformationByGroups,
|
|
794
|
+
preparedAssetTags,
|
|
795
|
+
this.options,
|
|
796
|
+
),
|
|
797
|
+
);
|
|
589
798
|
}
|
|
590
799
|
|
|
591
800
|
/**
|
|
@@ -601,9 +810,18 @@ class HtmlWebpackPlugin {
|
|
|
601
810
|
* @param {Compilation} compilation
|
|
602
811
|
* @returns Promise<string>
|
|
603
812
|
*/
|
|
604
|
-
executeTemplate
|
|
813
|
+
executeTemplate(
|
|
814
|
+
templateFunction,
|
|
815
|
+
assetsInformationByGroups,
|
|
816
|
+
assetTags,
|
|
817
|
+
compilation,
|
|
818
|
+
) {
|
|
605
819
|
// Template processing
|
|
606
|
-
const templateParamsPromise = this.getTemplateParameters(
|
|
820
|
+
const templateParamsPromise = this.getTemplateParameters(
|
|
821
|
+
compilation,
|
|
822
|
+
assetsInformationByGroups,
|
|
823
|
+
assetTags,
|
|
824
|
+
);
|
|
607
825
|
|
|
608
826
|
return templateParamsPromise.then((templateParams) => {
|
|
609
827
|
try {
|
|
@@ -611,7 +829,8 @@ class HtmlWebpackPlugin {
|
|
|
611
829
|
// If html is a string turn it into a promise
|
|
612
830
|
return templateFunction(templateParams);
|
|
613
831
|
} catch (e) {
|
|
614
|
-
|
|
832
|
+
// @ts-ignore
|
|
833
|
+
compilation.errors.push(new Error("Template execution failed: " + e));
|
|
615
834
|
return Promise.reject(e);
|
|
616
835
|
}
|
|
617
836
|
});
|
|
@@ -627,11 +846,20 @@ class HtmlWebpackPlugin {
|
|
|
627
846
|
* @param {{headTags: HtmlTagObject[], bodyTags: HtmlTagObject[]}} assetTags The asset tags to inject
|
|
628
847
|
* @returns {Promise<string>}
|
|
629
848
|
*/
|
|
630
|
-
postProcessHtml
|
|
849
|
+
postProcessHtml(
|
|
850
|
+
compiler,
|
|
851
|
+
originalHtml,
|
|
852
|
+
assetsInformationByGroups,
|
|
853
|
+
assetTags,
|
|
854
|
+
) {
|
|
631
855
|
let html = originalHtml;
|
|
632
856
|
|
|
633
|
-
if (typeof html !==
|
|
634
|
-
return Promise.reject(
|
|
857
|
+
if (typeof html !== "string") {
|
|
858
|
+
return Promise.reject(
|
|
859
|
+
new Error(
|
|
860
|
+
"Expected html to be a string but got " + JSON.stringify(html),
|
|
861
|
+
),
|
|
862
|
+
);
|
|
635
863
|
}
|
|
636
864
|
|
|
637
865
|
if (this.options.inject) {
|
|
@@ -639,22 +867,33 @@ class HtmlWebpackPlugin {
|
|
|
639
867
|
const headRegExp = /(<\/head\s*>)/i;
|
|
640
868
|
const bodyRegExp = /(<\/body\s*>)/i;
|
|
641
869
|
const metaViewportRegExp = /<meta[^>]+name=["']viewport["'][^>]*>/i;
|
|
642
|
-
const body = assetTags.bodyTags.map((assetTagObject) =>
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
870
|
+
const body = assetTags.bodyTags.map((assetTagObject) =>
|
|
871
|
+
htmlTagObjectToString(assetTagObject, this.options.xhtml),
|
|
872
|
+
);
|
|
873
|
+
const head = assetTags.headTags
|
|
874
|
+
.filter((item) => {
|
|
875
|
+
if (
|
|
876
|
+
item.tagName === "meta" &&
|
|
877
|
+
item.attributes &&
|
|
878
|
+
item.attributes.name === "viewport" &&
|
|
879
|
+
metaViewportRegExp.test(html)
|
|
880
|
+
) {
|
|
881
|
+
return false;
|
|
882
|
+
}
|
|
647
883
|
|
|
648
|
-
|
|
649
|
-
|
|
884
|
+
return true;
|
|
885
|
+
})
|
|
886
|
+
.map((assetTagObject) =>
|
|
887
|
+
htmlTagObjectToString(assetTagObject, this.options.xhtml),
|
|
888
|
+
);
|
|
650
889
|
|
|
651
890
|
if (body.length) {
|
|
652
891
|
if (bodyRegExp.test(html)) {
|
|
653
892
|
// Append assets to body element
|
|
654
|
-
html = html.replace(bodyRegExp, match => body.join(
|
|
893
|
+
html = html.replace(bodyRegExp, (match) => body.join("") + match);
|
|
655
894
|
} else {
|
|
656
895
|
// Append scripts to the end of the file if no <body> element exists:
|
|
657
|
-
html += body.join(
|
|
896
|
+
html += body.join("");
|
|
658
897
|
}
|
|
659
898
|
}
|
|
660
899
|
|
|
@@ -662,14 +901,14 @@ class HtmlWebpackPlugin {
|
|
|
662
901
|
// Create a head tag if none exists
|
|
663
902
|
if (!headRegExp.test(html)) {
|
|
664
903
|
if (!htmlRegExp.test(html)) {
|
|
665
|
-
html =
|
|
904
|
+
html = "<head></head>" + html;
|
|
666
905
|
} else {
|
|
667
|
-
html = html.replace(htmlRegExp, match => match +
|
|
906
|
+
html = html.replace(htmlRegExp, (match) => match + "<head></head>");
|
|
668
907
|
}
|
|
669
908
|
}
|
|
670
909
|
|
|
671
910
|
// Append assets to head element
|
|
672
|
-
html = html.replace(headRegExp, match => head.join(
|
|
911
|
+
html = html.replace(headRegExp, (match) => head.join("") + match);
|
|
673
912
|
}
|
|
674
913
|
|
|
675
914
|
// Inject manifest into the opening html tag
|
|
@@ -679,7 +918,13 @@ class HtmlWebpackPlugin {
|
|
|
679
918
|
if (/\smanifest\s*=/.test(match)) {
|
|
680
919
|
return match;
|
|
681
920
|
}
|
|
682
|
-
return
|
|
921
|
+
return (
|
|
922
|
+
start +
|
|
923
|
+
' manifest="' +
|
|
924
|
+
assetsInformationByGroups.manifest +
|
|
925
|
+
'"' +
|
|
926
|
+
end
|
|
927
|
+
);
|
|
683
928
|
});
|
|
684
929
|
}
|
|
685
930
|
}
|
|
@@ -687,41 +932,48 @@ class HtmlWebpackPlugin {
|
|
|
687
932
|
// TODO avoid this logic and use https://github.com/webpack-contrib/html-minimizer-webpack-plugin under the hood in the next major version
|
|
688
933
|
// Check if webpack is running in production mode
|
|
689
934
|
// @see https://github.com/webpack/webpack/blob/3366421f1784c449f415cda5930a8e445086f688/lib/WebpackOptionsDefaulter.js#L12-L14
|
|
690
|
-
const isProductionLikeMode =
|
|
691
|
-
|
|
935
|
+
const isProductionLikeMode =
|
|
936
|
+
compiler.options.mode === "production" || !compiler.options.mode;
|
|
937
|
+
const needMinify =
|
|
938
|
+
this.options.minify === true ||
|
|
939
|
+
typeof this.options.minify === "object" ||
|
|
940
|
+
(this.options.minify === "auto" && isProductionLikeMode);
|
|
692
941
|
|
|
693
942
|
if (!needMinify) {
|
|
694
943
|
return Promise.resolve(html);
|
|
695
944
|
}
|
|
696
945
|
|
|
697
|
-
const minifyOptions =
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
946
|
+
const minifyOptions =
|
|
947
|
+
typeof this.options.minify === "object"
|
|
948
|
+
? this.options.minify
|
|
949
|
+
: {
|
|
950
|
+
// https://www.npmjs.com/package/html-minifier-terser#options-quick-reference
|
|
951
|
+
collapseWhitespace: true,
|
|
952
|
+
keepClosingSlash: true,
|
|
953
|
+
removeComments: true,
|
|
954
|
+
removeRedundantAttributes: true,
|
|
955
|
+
removeScriptTypeAttributes: true,
|
|
956
|
+
removeStyleLinkTypeAttributes: true,
|
|
957
|
+
useShortDoctype: true,
|
|
958
|
+
};
|
|
709
959
|
|
|
710
960
|
try {
|
|
711
|
-
html = require(
|
|
961
|
+
html = require("html-minifier-terser").minify(html, minifyOptions);
|
|
712
962
|
} catch (e) {
|
|
713
|
-
const isParseError = String(e.message).indexOf(
|
|
963
|
+
const isParseError = String(e.message).indexOf("Parse Error") === 0;
|
|
714
964
|
|
|
715
965
|
if (isParseError) {
|
|
716
|
-
e.message =
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
966
|
+
e.message =
|
|
967
|
+
"html-webpack-plugin could not minify the generated output.\n" +
|
|
968
|
+
"In production mode the html minification is enabled by default.\n" +
|
|
969
|
+
"If you are not generating a valid html output please disable it manually.\n" +
|
|
970
|
+
"You can do so by adding the following setting to your HtmlWebpackPlugin config:\n|\n|" +
|
|
971
|
+
" minify: false\n|\n" +
|
|
972
|
+
"See https://github.com/jantimon/html-webpack-plugin#options for details.\n\n" +
|
|
973
|
+
"For parser dedicated bugs please create an issue here:\n" +
|
|
974
|
+
"https://danielruf.github.io/html-minifier-terser/" +
|
|
975
|
+
"\n" +
|
|
976
|
+
e.message;
|
|
725
977
|
}
|
|
726
978
|
|
|
727
979
|
return Promise.reject(e);
|
|
@@ -734,8 +986,12 @@ class HtmlWebpackPlugin {
|
|
|
734
986
|
* Helper to return a sorted unique array of all asset files out of the asset object
|
|
735
987
|
* @private
|
|
736
988
|
*/
|
|
737
|
-
getAssetFiles
|
|
738
|
-
const files =
|
|
989
|
+
getAssetFiles(assets) {
|
|
990
|
+
const files = _uniq(
|
|
991
|
+
Object.keys(assets)
|
|
992
|
+
.filter((assetType) => assetType !== "chunks" && assets[assetType])
|
|
993
|
+
.reduce((files, assetType) => files.concat(assets[assetType]), []),
|
|
994
|
+
);
|
|
739
995
|
files.sort();
|
|
740
996
|
return files;
|
|
741
997
|
}
|
|
@@ -751,7 +1007,13 @@ class HtmlWebpackPlugin {
|
|
|
751
1007
|
* @param {PreviousEmittedAssets} previousEmittedAssets
|
|
752
1008
|
* @returns {Promise<string|undefined>}
|
|
753
1009
|
*/
|
|
754
|
-
generateFavicon
|
|
1010
|
+
generateFavicon(
|
|
1011
|
+
compiler,
|
|
1012
|
+
favicon,
|
|
1013
|
+
compilation,
|
|
1014
|
+
publicPath,
|
|
1015
|
+
previousEmittedAssets,
|
|
1016
|
+
) {
|
|
755
1017
|
if (!favicon) {
|
|
756
1018
|
return Promise.resolve(undefined);
|
|
757
1019
|
}
|
|
@@ -760,7 +1022,10 @@ class HtmlWebpackPlugin {
|
|
|
760
1022
|
|
|
761
1023
|
return promisify(compilation.inputFileSystem.readFile)(filename)
|
|
762
1024
|
.then((buf) => {
|
|
763
|
-
const source = new compiler.webpack.sources.RawSource(
|
|
1025
|
+
const source = new compiler.webpack.sources.RawSource(
|
|
1026
|
+
/** @type {string | Buffer} */ (buf),
|
|
1027
|
+
false,
|
|
1028
|
+
);
|
|
764
1029
|
const name = path.basename(filename);
|
|
765
1030
|
|
|
766
1031
|
compilation.fileDependencies.add(filename);
|
|
@@ -770,12 +1035,19 @@ class HtmlWebpackPlugin {
|
|
|
770
1035
|
const faviconPath = publicPath + name;
|
|
771
1036
|
|
|
772
1037
|
if (this.options.hash) {
|
|
773
|
-
return this.appendHash(
|
|
1038
|
+
return this.appendHash(
|
|
1039
|
+
faviconPath,
|
|
1040
|
+
/** @type {string} */ (compilation.hash),
|
|
1041
|
+
);
|
|
774
1042
|
}
|
|
775
1043
|
|
|
776
1044
|
return faviconPath;
|
|
777
1045
|
})
|
|
778
|
-
.catch(() =>
|
|
1046
|
+
.catch(() =>
|
|
1047
|
+
Promise.reject(
|
|
1048
|
+
new Error("HtmlWebpackPlugin: could not load file " + filename),
|
|
1049
|
+
),
|
|
1050
|
+
);
|
|
779
1051
|
}
|
|
780
1052
|
|
|
781
1053
|
/**
|
|
@@ -785,24 +1057,26 @@ class HtmlWebpackPlugin {
|
|
|
785
1057
|
* @param {Array<string>} jsAssets
|
|
786
1058
|
* @returns {Array<HtmlTagObject>}
|
|
787
1059
|
*/
|
|
788
|
-
generatedScriptTags
|
|
1060
|
+
generatedScriptTags(jsAssets) {
|
|
789
1061
|
// @ts-ignore
|
|
790
|
-
return jsAssets.map(src => {
|
|
1062
|
+
return jsAssets.map((src) => {
|
|
791
1063
|
const attributes = {};
|
|
792
1064
|
|
|
793
|
-
if (this.options.scriptLoading ===
|
|
1065
|
+
if (this.options.scriptLoading === "defer") {
|
|
794
1066
|
attributes.defer = true;
|
|
795
|
-
} else if (this.options.scriptLoading ===
|
|
796
|
-
attributes.type =
|
|
1067
|
+
} else if (this.options.scriptLoading === "module") {
|
|
1068
|
+
attributes.type = "module";
|
|
1069
|
+
} else if (this.options.scriptLoading === "systemjs-module") {
|
|
1070
|
+
attributes.type = "systemjs-module";
|
|
797
1071
|
}
|
|
798
1072
|
|
|
799
1073
|
attributes.src = src;
|
|
800
1074
|
|
|
801
1075
|
return {
|
|
802
|
-
tagName:
|
|
1076
|
+
tagName: "script",
|
|
803
1077
|
voidTag: false,
|
|
804
|
-
meta: { plugin:
|
|
805
|
-
attributes
|
|
1078
|
+
meta: { plugin: "html-webpack-plugin" },
|
|
1079
|
+
attributes,
|
|
806
1080
|
};
|
|
807
1081
|
});
|
|
808
1082
|
}
|
|
@@ -814,15 +1088,15 @@ class HtmlWebpackPlugin {
|
|
|
814
1088
|
* @param {Array<string>} cssAssets
|
|
815
1089
|
* @returns {Array<HtmlTagObject>}
|
|
816
1090
|
*/
|
|
817
|
-
generateStyleTags
|
|
818
|
-
return cssAssets.map(styleAsset => ({
|
|
819
|
-
tagName:
|
|
1091
|
+
generateStyleTags(cssAssets) {
|
|
1092
|
+
return cssAssets.map((styleAsset) => ({
|
|
1093
|
+
tagName: "link",
|
|
820
1094
|
voidTag: true,
|
|
821
|
-
meta: { plugin:
|
|
1095
|
+
meta: { plugin: "html-webpack-plugin" },
|
|
822
1096
|
attributes: {
|
|
823
1097
|
href: styleAsset,
|
|
824
|
-
rel:
|
|
825
|
-
}
|
|
1098
|
+
rel: "stylesheet",
|
|
1099
|
+
},
|
|
826
1100
|
}));
|
|
827
1101
|
}
|
|
828
1102
|
|
|
@@ -832,16 +1106,21 @@ class HtmlWebpackPlugin {
|
|
|
832
1106
|
* @param {string | {[attributeName: string]: string}} base
|
|
833
1107
|
* @returns {Array<HtmlTagObject>}
|
|
834
1108
|
*/
|
|
835
|
-
generateBaseTag
|
|
836
|
-
return [
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
1109
|
+
generateBaseTag(base) {
|
|
1110
|
+
return [
|
|
1111
|
+
{
|
|
1112
|
+
tagName: "base",
|
|
1113
|
+
voidTag: true,
|
|
1114
|
+
meta: { plugin: "html-webpack-plugin" },
|
|
1115
|
+
// attributes e.g. { href:"http://example.com/page.html" target:"_blank" }
|
|
1116
|
+
attributes:
|
|
1117
|
+
typeof base === "string"
|
|
1118
|
+
? {
|
|
1119
|
+
href: base,
|
|
1120
|
+
}
|
|
1121
|
+
: base,
|
|
1122
|
+
},
|
|
1123
|
+
];
|
|
845
1124
|
}
|
|
846
1125
|
|
|
847
1126
|
/**
|
|
@@ -851,7 +1130,7 @@ class HtmlWebpackPlugin {
|
|
|
851
1130
|
* @param {false | {[name: string]: false | string | {[attributeName: string]: string|boolean}}} metaOptions
|
|
852
1131
|
* @returns {Array<HtmlTagObject>}
|
|
853
1132
|
*/
|
|
854
|
-
generatedMetaTags
|
|
1133
|
+
generatedMetaTags(metaOptions) {
|
|
855
1134
|
if (metaOptions === false) {
|
|
856
1135
|
return [];
|
|
857
1136
|
}
|
|
@@ -862,10 +1141,12 @@ class HtmlWebpackPlugin {
|
|
|
862
1141
|
const metaTagAttributeObjects = Object.keys(metaOptions)
|
|
863
1142
|
.map((metaName) => {
|
|
864
1143
|
const metaTagContent = metaOptions[metaName];
|
|
865
|
-
return
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
1144
|
+
return typeof metaTagContent === "string"
|
|
1145
|
+
? {
|
|
1146
|
+
name: metaName,
|
|
1147
|
+
content: metaTagContent,
|
|
1148
|
+
}
|
|
1149
|
+
: metaTagContent;
|
|
869
1150
|
})
|
|
870
1151
|
.filter((attribute) => attribute !== false);
|
|
871
1152
|
|
|
@@ -873,13 +1154,13 @@ class HtmlWebpackPlugin {
|
|
|
873
1154
|
// the html-webpack-plugin tag structure
|
|
874
1155
|
return metaTagAttributeObjects.map((metaTagAttributes) => {
|
|
875
1156
|
if (metaTagAttributes === false) {
|
|
876
|
-
throw new Error(
|
|
1157
|
+
throw new Error("Invalid meta tag");
|
|
877
1158
|
}
|
|
878
1159
|
return {
|
|
879
|
-
tagName:
|
|
1160
|
+
tagName: "meta",
|
|
880
1161
|
voidTag: true,
|
|
881
|
-
meta: { plugin:
|
|
882
|
-
attributes: metaTagAttributes
|
|
1162
|
+
meta: { plugin: "html-webpack-plugin" },
|
|
1163
|
+
attributes: metaTagAttributes,
|
|
883
1164
|
};
|
|
884
1165
|
});
|
|
885
1166
|
}
|
|
@@ -891,16 +1172,18 @@ class HtmlWebpackPlugin {
|
|
|
891
1172
|
* @param {string} favicon
|
|
892
1173
|
* @returns {Array<HtmlTagObject>}
|
|
893
1174
|
*/
|
|
894
|
-
generateFaviconTag
|
|
895
|
-
return [
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
1175
|
+
generateFaviconTag(favicon) {
|
|
1176
|
+
return [
|
|
1177
|
+
{
|
|
1178
|
+
tagName: "link",
|
|
1179
|
+
voidTag: true,
|
|
1180
|
+
meta: { plugin: "html-webpack-plugin" },
|
|
1181
|
+
attributes: {
|
|
1182
|
+
rel: "icon",
|
|
1183
|
+
href: favicon,
|
|
1184
|
+
},
|
|
1185
|
+
},
|
|
1186
|
+
];
|
|
904
1187
|
}
|
|
905
1188
|
|
|
906
1189
|
/**
|
|
@@ -917,24 +1200,24 @@ class HtmlWebpackPlugin {
|
|
|
917
1200
|
bodyTags: Array<HtmlTagObject>;
|
|
918
1201
|
}}
|
|
919
1202
|
*/
|
|
920
|
-
groupAssetsByElements
|
|
1203
|
+
groupAssetsByElements(assetTags, scriptTarget) {
|
|
921
1204
|
/** @type {{ headTags: Array<HtmlTagObject>; bodyTags: Array<HtmlTagObject>; }} */
|
|
922
1205
|
const result = {
|
|
923
|
-
headTags: [
|
|
924
|
-
|
|
925
|
-
...assetTags.styles
|
|
926
|
-
],
|
|
927
|
-
bodyTags: []
|
|
1206
|
+
headTags: [...assetTags.meta, ...assetTags.styles],
|
|
1207
|
+
bodyTags: [],
|
|
928
1208
|
};
|
|
929
1209
|
|
|
930
1210
|
// Add script tags to head or body depending on
|
|
931
1211
|
// the htmlPluginOptions
|
|
932
|
-
if (scriptTarget ===
|
|
1212
|
+
if (scriptTarget === "body") {
|
|
933
1213
|
result.bodyTags.push(...assetTags.scripts);
|
|
934
1214
|
} else {
|
|
935
1215
|
// If script loading is blocking add the scripts to the end of the head
|
|
936
1216
|
// If script loading is non-blocking add the scripts in front of the css files
|
|
937
|
-
const insertPosition =
|
|
1217
|
+
const insertPosition =
|
|
1218
|
+
this.options.scriptLoading === "blocking"
|
|
1219
|
+
? result.headTags.length
|
|
1220
|
+
: assetTags.meta.length;
|
|
938
1221
|
|
|
939
1222
|
result.headTags.splice(insertPosition, 0, ...assetTags.scripts);
|
|
940
1223
|
}
|
|
@@ -954,12 +1237,14 @@ class HtmlWebpackPlugin {
|
|
|
954
1237
|
* @param {Compilation} compilation
|
|
955
1238
|
* @returns {{ path: string, info: {} }}
|
|
956
1239
|
*/
|
|
957
|
-
replacePlaceholdersInFilename
|
|
1240
|
+
replacePlaceholdersInFilename(compiler, filename, fileContent, compilation) {
|
|
958
1241
|
if (/\[\\*([\w:]+)\\*\]/i.test(filename) === false) {
|
|
959
1242
|
return { path: filename, info: {} };
|
|
960
1243
|
}
|
|
961
1244
|
|
|
962
|
-
const hash = compiler.webpack.util.createHash(
|
|
1245
|
+
const hash = compiler.webpack.util.createHash(
|
|
1246
|
+
compilation.outputOptions.hashFunction,
|
|
1247
|
+
);
|
|
963
1248
|
|
|
964
1249
|
hash.update(fileContent);
|
|
965
1250
|
|
|
@@ -967,18 +1252,20 @@ class HtmlWebpackPlugin {
|
|
|
967
1252
|
hash.update(compilation.outputOptions.hashSalt);
|
|
968
1253
|
}
|
|
969
1254
|
|
|
970
|
-
const contentHash =
|
|
1255
|
+
const contentHash = /** @type {string} */ (
|
|
1256
|
+
hash
|
|
1257
|
+
.digest(compilation.outputOptions.hashDigest)
|
|
1258
|
+
.slice(0, compilation.outputOptions.hashDigestLength)
|
|
1259
|
+
);
|
|
971
1260
|
|
|
972
|
-
return compilation.getPathWithInfo(
|
|
973
|
-
|
|
974
|
-
{
|
|
1261
|
+
return compilation.getPathWithInfo(filename, {
|
|
1262
|
+
contentHash,
|
|
1263
|
+
chunk: {
|
|
1264
|
+
hash: contentHash,
|
|
1265
|
+
// @ts-ignore
|
|
975
1266
|
contentHash,
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
contentHash
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
);
|
|
1267
|
+
},
|
|
1268
|
+
});
|
|
982
1269
|
}
|
|
983
1270
|
|
|
984
1271
|
/**
|
|
@@ -993,36 +1280,57 @@ class HtmlWebpackPlugin {
|
|
|
993
1280
|
* @param {{ value: string | undefined }} assetJson
|
|
994
1281
|
* @param {(err?: Error) => void} callback
|
|
995
1282
|
*/
|
|
996
|
-
generateHTML
|
|
1283
|
+
generateHTML(
|
|
997
1284
|
compiler,
|
|
998
1285
|
compilation,
|
|
999
1286
|
outputName,
|
|
1000
1287
|
childCompilerPlugin,
|
|
1001
1288
|
previousEmittedAssets,
|
|
1002
1289
|
assetJson,
|
|
1003
|
-
callback
|
|
1290
|
+
callback,
|
|
1004
1291
|
) {
|
|
1005
1292
|
// Get all entry point names for this html file
|
|
1006
1293
|
const entryNames = Array.from(compilation.entrypoints.keys());
|
|
1007
|
-
const filteredEntryNames = this.filterEntryChunks(
|
|
1008
|
-
|
|
1294
|
+
const filteredEntryNames = this.filterEntryChunks(
|
|
1295
|
+
entryNames,
|
|
1296
|
+
this.options.chunks,
|
|
1297
|
+
this.options.excludeChunks,
|
|
1298
|
+
);
|
|
1299
|
+
const sortedEntryNames = this.sortEntryChunks(
|
|
1300
|
+
filteredEntryNames,
|
|
1301
|
+
this.options.chunksSortMode,
|
|
1302
|
+
compilation,
|
|
1303
|
+
);
|
|
1009
1304
|
const templateResult = this.options.templateContent
|
|
1010
1305
|
? { mainCompilationHash: compilation.hash }
|
|
1011
1306
|
: childCompilerPlugin.getCompilationEntryResult(this.options.template);
|
|
1012
1307
|
|
|
1013
|
-
if (
|
|
1014
|
-
compilation.errors.push(
|
|
1308
|
+
if ("error" in templateResult) {
|
|
1309
|
+
compilation.errors.push(
|
|
1310
|
+
prettyError(templateResult.error, compiler.context).toString(),
|
|
1311
|
+
);
|
|
1015
1312
|
}
|
|
1016
1313
|
|
|
1017
1314
|
// If the child compilation was not executed during a previous main compile run
|
|
1018
1315
|
// it is a cached result
|
|
1019
|
-
const isCompilationCached =
|
|
1316
|
+
const isCompilationCached =
|
|
1317
|
+
templateResult.mainCompilationHash !== compilation.hash;
|
|
1020
1318
|
/** Generated file paths from the entry point names */
|
|
1021
|
-
const assetsInformationByGroups = this.getAssetsInformationByGroups(
|
|
1319
|
+
const assetsInformationByGroups = this.getAssetsInformationByGroups(
|
|
1320
|
+
compilation,
|
|
1321
|
+
outputName,
|
|
1322
|
+
sortedEntryNames,
|
|
1323
|
+
);
|
|
1022
1324
|
// If the template and the assets did not change we don't have to emit the html
|
|
1023
|
-
const newAssetJson = JSON.stringify(
|
|
1325
|
+
const newAssetJson = JSON.stringify(
|
|
1326
|
+
this.getAssetFiles(assetsInformationByGroups),
|
|
1327
|
+
);
|
|
1024
1328
|
|
|
1025
|
-
if (
|
|
1329
|
+
if (
|
|
1330
|
+
isCompilationCached &&
|
|
1331
|
+
this.options.cache &&
|
|
1332
|
+
assetJson.value === newAssetJson
|
|
1333
|
+
) {
|
|
1026
1334
|
previousEmittedAssets.forEach(({ name, source, info }) => {
|
|
1027
1335
|
compilation.emitAsset(name, source, info);
|
|
1028
1336
|
});
|
|
@@ -1035,127 +1343,204 @@ class HtmlWebpackPlugin {
|
|
|
1035
1343
|
// The html-webpack plugin uses a object representation for the html-tags which will be injected
|
|
1036
1344
|
// to allow altering them more easily
|
|
1037
1345
|
// Just before they are converted a third-party-plugin author might change the order and content
|
|
1038
|
-
const assetsPromise = this.generateFavicon(
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1346
|
+
const assetsPromise = this.generateFavicon(
|
|
1347
|
+
compiler,
|
|
1348
|
+
this.options.favicon,
|
|
1349
|
+
compilation,
|
|
1350
|
+
assetsInformationByGroups.publicPath,
|
|
1351
|
+
previousEmittedAssets,
|
|
1352
|
+
).then((faviconPath) => {
|
|
1353
|
+
assetsInformationByGroups.favicon = faviconPath;
|
|
1354
|
+
return HtmlWebpackPlugin.getCompilationHooks(
|
|
1355
|
+
compilation,
|
|
1356
|
+
).beforeAssetTagGeneration.promise({
|
|
1357
|
+
assets: assetsInformationByGroups,
|
|
1358
|
+
outputName,
|
|
1359
|
+
plugin: this,
|
|
1046
1360
|
});
|
|
1361
|
+
});
|
|
1047
1362
|
|
|
1048
1363
|
// Turn the js and css paths into grouped HtmlTagObjects
|
|
1049
1364
|
const assetTagGroupsPromise = assetsPromise
|
|
1050
1365
|
// And allow third-party-plugin authors to reorder and change the assetTags before they are grouped
|
|
1051
|
-
.then(({ assets }) =>
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1366
|
+
.then(({ assets }) =>
|
|
1367
|
+
HtmlWebpackPlugin.getCompilationHooks(
|
|
1368
|
+
compilation,
|
|
1369
|
+
).alterAssetTags.promise({
|
|
1370
|
+
assetTags: {
|
|
1371
|
+
scripts: this.generatedScriptTags(assets.js),
|
|
1372
|
+
styles: this.generateStyleTags(assets.css),
|
|
1373
|
+
meta: [
|
|
1374
|
+
...(this.options.base !== false
|
|
1375
|
+
? this.generateBaseTag(this.options.base)
|
|
1376
|
+
: []),
|
|
1377
|
+
...this.generatedMetaTags(this.options.meta),
|
|
1378
|
+
...(assets.favicon
|
|
1379
|
+
? this.generateFaviconTag(assets.favicon)
|
|
1380
|
+
: []),
|
|
1381
|
+
],
|
|
1382
|
+
},
|
|
1383
|
+
outputName,
|
|
1384
|
+
publicPath: assetsInformationByGroups.publicPath,
|
|
1385
|
+
plugin: this,
|
|
1386
|
+
}),
|
|
1387
|
+
)
|
|
1065
1388
|
.then(({ assetTags }) => {
|
|
1066
1389
|
// Inject scripts to body unless it set explicitly to head
|
|
1067
|
-
const scriptTarget =
|
|
1068
|
-
|
|
1390
|
+
const scriptTarget =
|
|
1391
|
+
this.options.inject === "head" ||
|
|
1392
|
+
(this.options.inject !== "body" &&
|
|
1393
|
+
this.options.scriptLoading !== "blocking")
|
|
1394
|
+
? "head"
|
|
1395
|
+
: "body";
|
|
1069
1396
|
// Group assets to `head` and `body` tag arrays
|
|
1070
1397
|
const assetGroups = this.groupAssetsByElements(assetTags, scriptTarget);
|
|
1071
1398
|
// Allow third-party-plugin authors to reorder and change the assetTags once they are grouped
|
|
1072
|
-
return
|
|
1399
|
+
return HtmlWebpackPlugin.getCompilationHooks(
|
|
1400
|
+
compilation,
|
|
1401
|
+
).alterAssetTagGroups.promise({
|
|
1073
1402
|
headTags: assetGroups.headTags,
|
|
1074
1403
|
bodyTags: assetGroups.bodyTags,
|
|
1075
1404
|
outputName,
|
|
1076
1405
|
publicPath: assetsInformationByGroups.publicPath,
|
|
1077
|
-
plugin: this
|
|
1406
|
+
plugin: this,
|
|
1078
1407
|
});
|
|
1079
1408
|
});
|
|
1080
1409
|
|
|
1081
1410
|
// Turn the compiled template into a nodejs function or into a nodejs string
|
|
1082
|
-
const templateEvaluationPromise = Promise.resolve()
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
// Allow to use a custom function / string instead
|
|
1089
|
-
if (this.options.templateContent !== false) {
|
|
1090
|
-
return this.options.templateContent;
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
// Once everything is compiled evaluate the html factory and replace it with its content
|
|
1094
|
-
if ('compiledEntry' in templateResult) {
|
|
1095
|
-
const compiledEntry = templateResult.compiledEntry;
|
|
1096
|
-
const assets = compiledEntry.assets;
|
|
1411
|
+
const templateEvaluationPromise = Promise.resolve().then(() => {
|
|
1412
|
+
if ("error" in templateResult) {
|
|
1413
|
+
return this.options.showErrors
|
|
1414
|
+
? prettyError(templateResult.error, compiler.context).toHtml()
|
|
1415
|
+
: "ERROR";
|
|
1416
|
+
}
|
|
1097
1417
|
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1418
|
+
// Allow to use a custom function / string instead
|
|
1419
|
+
if (this.options.templateContent !== false) {
|
|
1420
|
+
return this.options.templateContent;
|
|
1421
|
+
}
|
|
1102
1422
|
|
|
1103
|
-
|
|
1423
|
+
// Once everything is compiled evaluate the html factory and replace it with its content
|
|
1424
|
+
if ("compiledEntry" in templateResult) {
|
|
1425
|
+
const compiledEntry = templateResult.compiledEntry;
|
|
1426
|
+
const assets = compiledEntry.assets;
|
|
1427
|
+
|
|
1428
|
+
// Store assets from child compiler to re-emit them later
|
|
1429
|
+
for (const name in assets) {
|
|
1430
|
+
previousEmittedAssets.push({
|
|
1431
|
+
name,
|
|
1432
|
+
source: assets[name].source,
|
|
1433
|
+
info: assets[name].info,
|
|
1434
|
+
});
|
|
1104
1435
|
}
|
|
1105
1436
|
|
|
1106
|
-
return
|
|
1107
|
-
|
|
1108
|
-
|
|
1437
|
+
return this.evaluateCompilationResult(
|
|
1438
|
+
compiledEntry.content,
|
|
1439
|
+
assetsInformationByGroups.publicPath,
|
|
1440
|
+
this.options.template,
|
|
1441
|
+
);
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
return Promise.reject(
|
|
1445
|
+
new Error("Child compilation contained no compiledEntry"),
|
|
1446
|
+
);
|
|
1447
|
+
});
|
|
1448
|
+
const templateExecutionPromise = Promise.all([
|
|
1449
|
+
assetsPromise,
|
|
1450
|
+
assetTagGroupsPromise,
|
|
1451
|
+
templateEvaluationPromise,
|
|
1452
|
+
])
|
|
1109
1453
|
// Execute the template
|
|
1110
|
-
.then(([assetsHookResult, assetTags, compilationResult]) =>
|
|
1111
|
-
|
|
1112
|
-
|
|
1454
|
+
.then(([assetsHookResult, assetTags, compilationResult]) =>
|
|
1455
|
+
typeof compilationResult !== "function"
|
|
1456
|
+
? compilationResult
|
|
1457
|
+
: this.executeTemplate(
|
|
1458
|
+
compilationResult,
|
|
1459
|
+
assetsHookResult.assets,
|
|
1460
|
+
{ headTags: assetTags.headTags, bodyTags: assetTags.bodyTags },
|
|
1461
|
+
compilation,
|
|
1462
|
+
),
|
|
1463
|
+
);
|
|
1113
1464
|
|
|
1114
|
-
const injectedHtmlPromise = Promise.all([
|
|
1465
|
+
const injectedHtmlPromise = Promise.all([
|
|
1466
|
+
assetTagGroupsPromise,
|
|
1467
|
+
templateExecutionPromise,
|
|
1468
|
+
])
|
|
1115
1469
|
// Allow plugins to change the html before assets are injected
|
|
1116
1470
|
.then(([assetTags, html]) => {
|
|
1117
|
-
const pluginArgs = {
|
|
1118
|
-
|
|
1471
|
+
const pluginArgs = {
|
|
1472
|
+
html,
|
|
1473
|
+
headTags: assetTags.headTags,
|
|
1474
|
+
bodyTags: assetTags.bodyTags,
|
|
1475
|
+
plugin: this,
|
|
1476
|
+
outputName,
|
|
1477
|
+
};
|
|
1478
|
+
return HtmlWebpackPlugin.getCompilationHooks(
|
|
1479
|
+
compilation,
|
|
1480
|
+
).afterTemplateExecution.promise(pluginArgs);
|
|
1119
1481
|
})
|
|
1120
1482
|
.then(({ html, headTags, bodyTags }) => {
|
|
1121
|
-
return this.postProcessHtml(compiler, html, assetsInformationByGroups, {
|
|
1483
|
+
return this.postProcessHtml(compiler, html, assetsInformationByGroups, {
|
|
1484
|
+
headTags,
|
|
1485
|
+
bodyTags,
|
|
1486
|
+
});
|
|
1122
1487
|
});
|
|
1123
1488
|
|
|
1124
1489
|
const emitHtmlPromise = injectedHtmlPromise
|
|
1125
1490
|
// Allow plugins to change the html after assets are injected
|
|
1126
1491
|
.then((html) => {
|
|
1127
1492
|
const pluginArgs = { html, plugin: this, outputName };
|
|
1128
|
-
return
|
|
1129
|
-
.
|
|
1493
|
+
return HtmlWebpackPlugin.getCompilationHooks(compilation)
|
|
1494
|
+
.beforeEmit.promise(pluginArgs)
|
|
1495
|
+
.then((result) => result.html);
|
|
1130
1496
|
})
|
|
1131
|
-
.catch(err => {
|
|
1497
|
+
.catch((err) => {
|
|
1132
1498
|
// In case anything went wrong the promise is resolved
|
|
1133
1499
|
// with the error message and an error is logged
|
|
1134
1500
|
compilation.errors.push(prettyError(err, compiler.context).toString());
|
|
1135
|
-
return this.options.showErrors
|
|
1501
|
+
return this.options.showErrors
|
|
1502
|
+
? prettyError(err, compiler.context).toHtml()
|
|
1503
|
+
: "ERROR";
|
|
1136
1504
|
})
|
|
1137
|
-
.then(html => {
|
|
1138
|
-
const filename = outputName.replace(
|
|
1139
|
-
(
|
|
1140
|
-
|
|
1505
|
+
.then((html) => {
|
|
1506
|
+
const filename = outputName.replace(
|
|
1507
|
+
/\[templatehash([^\]]*)\]/g,
|
|
1508
|
+
require("util").deprecate(
|
|
1509
|
+
(match, options) => `[contenthash${options}]`,
|
|
1510
|
+
"[templatehash] is now [contenthash]",
|
|
1511
|
+
),
|
|
1512
|
+
);
|
|
1513
|
+
const replacedFilename = this.replacePlaceholdersInFilename(
|
|
1514
|
+
compiler,
|
|
1515
|
+
filename,
|
|
1516
|
+
html,
|
|
1517
|
+
compilation,
|
|
1141
1518
|
);
|
|
1142
|
-
const replacedFilename = this.replacePlaceholdersInFilename(compiler, filename, html, compilation);
|
|
1143
1519
|
const source = new compiler.webpack.sources.RawSource(html, false);
|
|
1144
1520
|
|
|
1145
1521
|
// Add the evaluated html code to the webpack assets
|
|
1146
|
-
compilation.emitAsset(
|
|
1522
|
+
compilation.emitAsset(
|
|
1523
|
+
replacedFilename.path,
|
|
1524
|
+
source,
|
|
1525
|
+
replacedFilename.info,
|
|
1526
|
+
);
|
|
1147
1527
|
previousEmittedAssets.push({ name: replacedFilename.path, source });
|
|
1148
1528
|
|
|
1149
1529
|
return replacedFilename.path;
|
|
1150
1530
|
})
|
|
1151
|
-
.then((finalOutputName) =>
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1531
|
+
.then((finalOutputName) =>
|
|
1532
|
+
HtmlWebpackPlugin.getCompilationHooks(compilation)
|
|
1533
|
+
.afterEmit.promise({
|
|
1534
|
+
outputName: finalOutputName,
|
|
1535
|
+
plugin: this,
|
|
1536
|
+
})
|
|
1537
|
+
.catch((err) => {
|
|
1538
|
+
/** @type {Logger} */
|
|
1539
|
+
(this.logger).error(err);
|
|
1540
|
+
return null;
|
|
1541
|
+
})
|
|
1542
|
+
.then(() => null),
|
|
1543
|
+
);
|
|
1159
1544
|
|
|
1160
1545
|
// Once all files are added to the webpack compilation
|
|
1161
1546
|
// let the webpack compiler continue
|
|
@@ -1179,15 +1564,15 @@ class HtmlWebpackPlugin {
|
|
|
1179
1564
|
* @param {ProcessedHtmlWebpackOptions} options
|
|
1180
1565
|
* @returns {TemplateParameter}
|
|
1181
1566
|
*/
|
|
1182
|
-
function templateParametersGenerator
|
|
1567
|
+
function templateParametersGenerator(compilation, assets, assetTags, options) {
|
|
1183
1568
|
return {
|
|
1184
1569
|
compilation: compilation,
|
|
1185
1570
|
webpackConfig: compilation.options,
|
|
1186
1571
|
htmlWebpackPlugin: {
|
|
1187
1572
|
tags: assetTags,
|
|
1188
1573
|
files: assets,
|
|
1189
|
-
options: options
|
|
1190
|
-
}
|
|
1574
|
+
options: options,
|
|
1575
|
+
},
|
|
1191
1576
|
};
|
|
1192
1577
|
}
|
|
1193
1578
|
|
|
@@ -1202,7 +1587,8 @@ HtmlWebpackPlugin.version = 5;
|
|
|
1202
1587
|
*
|
|
1203
1588
|
* Usage: HtmlWebpackPlugin.getHooks(compilation).HOOK_NAME.tapAsync('YourPluginName', () => { ... });
|
|
1204
1589
|
*/
|
|
1205
|
-
|
|
1590
|
+
// TODO remove me in the next major release in favor getCompilationHooks
|
|
1591
|
+
HtmlWebpackPlugin.getHooks = HtmlWebpackPlugin.getCompilationHooks;
|
|
1206
1592
|
HtmlWebpackPlugin.createHtmlTagObject = createHtmlTagObject;
|
|
1207
1593
|
|
|
1208
1594
|
module.exports = HtmlWebpackPlugin;
|