html-webpack-plugin 5.6.0 → 5.6.2
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 -401
- package/lib/cached-child-compiler.js +172 -126
- package/lib/child-compiler.js +90 -53
- 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 +28 -19
- package/typings.d.ts +8 -7
- 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 !== undefined
|
|
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,26 +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 =
|
|
797
|
-
} else if (this.options.scriptLoading ===
|
|
798
|
-
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";
|
|
799
1071
|
}
|
|
800
1072
|
|
|
801
1073
|
attributes.src = src;
|
|
802
1074
|
|
|
803
1075
|
return {
|
|
804
|
-
tagName:
|
|
1076
|
+
tagName: "script",
|
|
805
1077
|
voidTag: false,
|
|
806
|
-
meta: { plugin:
|
|
807
|
-
attributes
|
|
1078
|
+
meta: { plugin: "html-webpack-plugin" },
|
|
1079
|
+
attributes,
|
|
808
1080
|
};
|
|
809
1081
|
});
|
|
810
1082
|
}
|
|
@@ -816,15 +1088,15 @@ class HtmlWebpackPlugin {
|
|
|
816
1088
|
* @param {Array<string>} cssAssets
|
|
817
1089
|
* @returns {Array<HtmlTagObject>}
|
|
818
1090
|
*/
|
|
819
|
-
generateStyleTags
|
|
820
|
-
return cssAssets.map(styleAsset => ({
|
|
821
|
-
tagName:
|
|
1091
|
+
generateStyleTags(cssAssets) {
|
|
1092
|
+
return cssAssets.map((styleAsset) => ({
|
|
1093
|
+
tagName: "link",
|
|
822
1094
|
voidTag: true,
|
|
823
|
-
meta: { plugin:
|
|
1095
|
+
meta: { plugin: "html-webpack-plugin" },
|
|
824
1096
|
attributes: {
|
|
825
1097
|
href: styleAsset,
|
|
826
|
-
rel:
|
|
827
|
-
}
|
|
1098
|
+
rel: "stylesheet",
|
|
1099
|
+
},
|
|
828
1100
|
}));
|
|
829
1101
|
}
|
|
830
1102
|
|
|
@@ -834,16 +1106,21 @@ class HtmlWebpackPlugin {
|
|
|
834
1106
|
* @param {string | {[attributeName: string]: string}} base
|
|
835
1107
|
* @returns {Array<HtmlTagObject>}
|
|
836
1108
|
*/
|
|
837
|
-
generateBaseTag
|
|
838
|
-
return [
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
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
|
+
];
|
|
847
1124
|
}
|
|
848
1125
|
|
|
849
1126
|
/**
|
|
@@ -853,7 +1130,7 @@ class HtmlWebpackPlugin {
|
|
|
853
1130
|
* @param {false | {[name: string]: false | string | {[attributeName: string]: string|boolean}}} metaOptions
|
|
854
1131
|
* @returns {Array<HtmlTagObject>}
|
|
855
1132
|
*/
|
|
856
|
-
generatedMetaTags
|
|
1133
|
+
generatedMetaTags(metaOptions) {
|
|
857
1134
|
if (metaOptions === false) {
|
|
858
1135
|
return [];
|
|
859
1136
|
}
|
|
@@ -864,10 +1141,12 @@ class HtmlWebpackPlugin {
|
|
|
864
1141
|
const metaTagAttributeObjects = Object.keys(metaOptions)
|
|
865
1142
|
.map((metaName) => {
|
|
866
1143
|
const metaTagContent = metaOptions[metaName];
|
|
867
|
-
return
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
1144
|
+
return typeof metaTagContent === "string"
|
|
1145
|
+
? {
|
|
1146
|
+
name: metaName,
|
|
1147
|
+
content: metaTagContent,
|
|
1148
|
+
}
|
|
1149
|
+
: metaTagContent;
|
|
871
1150
|
})
|
|
872
1151
|
.filter((attribute) => attribute !== false);
|
|
873
1152
|
|
|
@@ -875,13 +1154,13 @@ class HtmlWebpackPlugin {
|
|
|
875
1154
|
// the html-webpack-plugin tag structure
|
|
876
1155
|
return metaTagAttributeObjects.map((metaTagAttributes) => {
|
|
877
1156
|
if (metaTagAttributes === false) {
|
|
878
|
-
throw new Error(
|
|
1157
|
+
throw new Error("Invalid meta tag");
|
|
879
1158
|
}
|
|
880
1159
|
return {
|
|
881
|
-
tagName:
|
|
1160
|
+
tagName: "meta",
|
|
882
1161
|
voidTag: true,
|
|
883
|
-
meta: { plugin:
|
|
884
|
-
attributes: metaTagAttributes
|
|
1162
|
+
meta: { plugin: "html-webpack-plugin" },
|
|
1163
|
+
attributes: metaTagAttributes,
|
|
885
1164
|
};
|
|
886
1165
|
});
|
|
887
1166
|
}
|
|
@@ -893,16 +1172,18 @@ class HtmlWebpackPlugin {
|
|
|
893
1172
|
* @param {string} favicon
|
|
894
1173
|
* @returns {Array<HtmlTagObject>}
|
|
895
1174
|
*/
|
|
896
|
-
generateFaviconTag
|
|
897
|
-
return [
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
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
|
+
];
|
|
906
1187
|
}
|
|
907
1188
|
|
|
908
1189
|
/**
|
|
@@ -919,24 +1200,24 @@ class HtmlWebpackPlugin {
|
|
|
919
1200
|
bodyTags: Array<HtmlTagObject>;
|
|
920
1201
|
}}
|
|
921
1202
|
*/
|
|
922
|
-
groupAssetsByElements
|
|
1203
|
+
groupAssetsByElements(assetTags, scriptTarget) {
|
|
923
1204
|
/** @type {{ headTags: Array<HtmlTagObject>; bodyTags: Array<HtmlTagObject>; }} */
|
|
924
1205
|
const result = {
|
|
925
|
-
headTags: [
|
|
926
|
-
|
|
927
|
-
...assetTags.styles
|
|
928
|
-
],
|
|
929
|
-
bodyTags: []
|
|
1206
|
+
headTags: [...assetTags.meta, ...assetTags.styles],
|
|
1207
|
+
bodyTags: [],
|
|
930
1208
|
};
|
|
931
1209
|
|
|
932
1210
|
// Add script tags to head or body depending on
|
|
933
1211
|
// the htmlPluginOptions
|
|
934
|
-
if (scriptTarget ===
|
|
1212
|
+
if (scriptTarget === "body") {
|
|
935
1213
|
result.bodyTags.push(...assetTags.scripts);
|
|
936
1214
|
} else {
|
|
937
1215
|
// If script loading is blocking add the scripts to the end of the head
|
|
938
1216
|
// If script loading is non-blocking add the scripts in front of the css files
|
|
939
|
-
const insertPosition =
|
|
1217
|
+
const insertPosition =
|
|
1218
|
+
this.options.scriptLoading === "blocking"
|
|
1219
|
+
? result.headTags.length
|
|
1220
|
+
: assetTags.meta.length;
|
|
940
1221
|
|
|
941
1222
|
result.headTags.splice(insertPosition, 0, ...assetTags.scripts);
|
|
942
1223
|
}
|
|
@@ -956,12 +1237,14 @@ class HtmlWebpackPlugin {
|
|
|
956
1237
|
* @param {Compilation} compilation
|
|
957
1238
|
* @returns {{ path: string, info: {} }}
|
|
958
1239
|
*/
|
|
959
|
-
replacePlaceholdersInFilename
|
|
1240
|
+
replacePlaceholdersInFilename(compiler, filename, fileContent, compilation) {
|
|
960
1241
|
if (/\[\\*([\w:]+)\\*\]/i.test(filename) === false) {
|
|
961
1242
|
return { path: filename, info: {} };
|
|
962
1243
|
}
|
|
963
1244
|
|
|
964
|
-
const hash = compiler.webpack.util.createHash(
|
|
1245
|
+
const hash = compiler.webpack.util.createHash(
|
|
1246
|
+
compilation.outputOptions.hashFunction,
|
|
1247
|
+
);
|
|
965
1248
|
|
|
966
1249
|
hash.update(fileContent);
|
|
967
1250
|
|
|
@@ -969,18 +1252,20 @@ class HtmlWebpackPlugin {
|
|
|
969
1252
|
hash.update(compilation.outputOptions.hashSalt);
|
|
970
1253
|
}
|
|
971
1254
|
|
|
972
|
-
const contentHash =
|
|
1255
|
+
const contentHash = /** @type {string} */ (
|
|
1256
|
+
hash
|
|
1257
|
+
.digest(compilation.outputOptions.hashDigest)
|
|
1258
|
+
.slice(0, compilation.outputOptions.hashDigestLength)
|
|
1259
|
+
);
|
|
973
1260
|
|
|
974
|
-
return compilation.getPathWithInfo(
|
|
975
|
-
|
|
976
|
-
{
|
|
1261
|
+
return compilation.getPathWithInfo(filename, {
|
|
1262
|
+
contentHash,
|
|
1263
|
+
chunk: {
|
|
1264
|
+
hash: contentHash,
|
|
1265
|
+
// @ts-ignore
|
|
977
1266
|
contentHash,
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
contentHash
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
);
|
|
1267
|
+
},
|
|
1268
|
+
});
|
|
984
1269
|
}
|
|
985
1270
|
|
|
986
1271
|
/**
|
|
@@ -995,36 +1280,57 @@ class HtmlWebpackPlugin {
|
|
|
995
1280
|
* @param {{ value: string | undefined }} assetJson
|
|
996
1281
|
* @param {(err?: Error) => void} callback
|
|
997
1282
|
*/
|
|
998
|
-
generateHTML
|
|
1283
|
+
generateHTML(
|
|
999
1284
|
compiler,
|
|
1000
1285
|
compilation,
|
|
1001
1286
|
outputName,
|
|
1002
1287
|
childCompilerPlugin,
|
|
1003
1288
|
previousEmittedAssets,
|
|
1004
1289
|
assetJson,
|
|
1005
|
-
callback
|
|
1290
|
+
callback,
|
|
1006
1291
|
) {
|
|
1007
1292
|
// Get all entry point names for this html file
|
|
1008
1293
|
const entryNames = Array.from(compilation.entrypoints.keys());
|
|
1009
|
-
const filteredEntryNames = this.filterEntryChunks(
|
|
1010
|
-
|
|
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
|
+
);
|
|
1011
1304
|
const templateResult = this.options.templateContent
|
|
1012
1305
|
? { mainCompilationHash: compilation.hash }
|
|
1013
1306
|
: childCompilerPlugin.getCompilationEntryResult(this.options.template);
|
|
1014
1307
|
|
|
1015
|
-
if (
|
|
1016
|
-
compilation.errors.push(
|
|
1308
|
+
if ("error" in templateResult) {
|
|
1309
|
+
compilation.errors.push(
|
|
1310
|
+
prettyError(templateResult.error, compiler.context).toString(),
|
|
1311
|
+
);
|
|
1017
1312
|
}
|
|
1018
1313
|
|
|
1019
1314
|
// If the child compilation was not executed during a previous main compile run
|
|
1020
1315
|
// it is a cached result
|
|
1021
|
-
const isCompilationCached =
|
|
1316
|
+
const isCompilationCached =
|
|
1317
|
+
templateResult.mainCompilationHash !== compilation.hash;
|
|
1022
1318
|
/** Generated file paths from the entry point names */
|
|
1023
|
-
const assetsInformationByGroups = this.getAssetsInformationByGroups(
|
|
1319
|
+
const assetsInformationByGroups = this.getAssetsInformationByGroups(
|
|
1320
|
+
compilation,
|
|
1321
|
+
outputName,
|
|
1322
|
+
sortedEntryNames,
|
|
1323
|
+
);
|
|
1024
1324
|
// If the template and the assets did not change we don't have to emit the html
|
|
1025
|
-
const newAssetJson = JSON.stringify(
|
|
1325
|
+
const newAssetJson = JSON.stringify(
|
|
1326
|
+
this.getAssetFiles(assetsInformationByGroups),
|
|
1327
|
+
);
|
|
1026
1328
|
|
|
1027
|
-
if (
|
|
1329
|
+
if (
|
|
1330
|
+
isCompilationCached &&
|
|
1331
|
+
this.options.cache &&
|
|
1332
|
+
assetJson.value === newAssetJson
|
|
1333
|
+
) {
|
|
1028
1334
|
previousEmittedAssets.forEach(({ name, source, info }) => {
|
|
1029
1335
|
compilation.emitAsset(name, source, info);
|
|
1030
1336
|
});
|
|
@@ -1037,127 +1343,204 @@ class HtmlWebpackPlugin {
|
|
|
1037
1343
|
// The html-webpack plugin uses a object representation for the html-tags which will be injected
|
|
1038
1344
|
// to allow altering them more easily
|
|
1039
1345
|
// Just before they are converted a third-party-plugin author might change the order and content
|
|
1040
|
-
const assetsPromise = this.generateFavicon(
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
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,
|
|
1048
1360
|
});
|
|
1361
|
+
});
|
|
1049
1362
|
|
|
1050
1363
|
// Turn the js and css paths into grouped HtmlTagObjects
|
|
1051
1364
|
const assetTagGroupsPromise = assetsPromise
|
|
1052
1365
|
// And allow third-party-plugin authors to reorder and change the assetTags before they are grouped
|
|
1053
|
-
.then(({ assets }) =>
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
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
|
+
)
|
|
1067
1388
|
.then(({ assetTags }) => {
|
|
1068
1389
|
// Inject scripts to body unless it set explicitly to head
|
|
1069
|
-
const scriptTarget =
|
|
1070
|
-
|
|
1390
|
+
const scriptTarget =
|
|
1391
|
+
this.options.inject === "head" ||
|
|
1392
|
+
(this.options.inject !== "body" &&
|
|
1393
|
+
this.options.scriptLoading !== "blocking")
|
|
1394
|
+
? "head"
|
|
1395
|
+
: "body";
|
|
1071
1396
|
// Group assets to `head` and `body` tag arrays
|
|
1072
1397
|
const assetGroups = this.groupAssetsByElements(assetTags, scriptTarget);
|
|
1073
1398
|
// Allow third-party-plugin authors to reorder and change the assetTags once they are grouped
|
|
1074
|
-
return
|
|
1399
|
+
return HtmlWebpackPlugin.getCompilationHooks(
|
|
1400
|
+
compilation,
|
|
1401
|
+
).alterAssetTagGroups.promise({
|
|
1075
1402
|
headTags: assetGroups.headTags,
|
|
1076
1403
|
bodyTags: assetGroups.bodyTags,
|
|
1077
1404
|
outputName,
|
|
1078
1405
|
publicPath: assetsInformationByGroups.publicPath,
|
|
1079
|
-
plugin: this
|
|
1406
|
+
plugin: this,
|
|
1080
1407
|
});
|
|
1081
1408
|
});
|
|
1082
1409
|
|
|
1083
1410
|
// Turn the compiled template into a nodejs function or into a nodejs string
|
|
1084
|
-
const templateEvaluationPromise = Promise.resolve()
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
// Allow to use a custom function / string instead
|
|
1091
|
-
if (this.options.templateContent !== false) {
|
|
1092
|
-
return this.options.templateContent;
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
// Once everything is compiled evaluate the html factory and replace it with its content
|
|
1096
|
-
if ('compiledEntry' in templateResult) {
|
|
1097
|
-
const compiledEntry = templateResult.compiledEntry;
|
|
1098
|
-
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
|
+
}
|
|
1099
1417
|
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1418
|
+
// Allow to use a custom function / string instead
|
|
1419
|
+
if (this.options.templateContent !== false) {
|
|
1420
|
+
return this.options.templateContent;
|
|
1421
|
+
}
|
|
1104
1422
|
|
|
1105
|
-
|
|
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
|
+
});
|
|
1106
1435
|
}
|
|
1107
1436
|
|
|
1108
|
-
return
|
|
1109
|
-
|
|
1110
|
-
|
|
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
|
+
])
|
|
1111
1453
|
// Execute the template
|
|
1112
|
-
.then(([assetsHookResult, assetTags, compilationResult]) =>
|
|
1113
|
-
|
|
1114
|
-
|
|
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
|
+
);
|
|
1115
1464
|
|
|
1116
|
-
const injectedHtmlPromise = Promise.all([
|
|
1465
|
+
const injectedHtmlPromise = Promise.all([
|
|
1466
|
+
assetTagGroupsPromise,
|
|
1467
|
+
templateExecutionPromise,
|
|
1468
|
+
])
|
|
1117
1469
|
// Allow plugins to change the html before assets are injected
|
|
1118
1470
|
.then(([assetTags, html]) => {
|
|
1119
|
-
const pluginArgs = {
|
|
1120
|
-
|
|
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);
|
|
1121
1481
|
})
|
|
1122
1482
|
.then(({ html, headTags, bodyTags }) => {
|
|
1123
|
-
return this.postProcessHtml(compiler, html, assetsInformationByGroups, {
|
|
1483
|
+
return this.postProcessHtml(compiler, html, assetsInformationByGroups, {
|
|
1484
|
+
headTags,
|
|
1485
|
+
bodyTags,
|
|
1486
|
+
});
|
|
1124
1487
|
});
|
|
1125
1488
|
|
|
1126
1489
|
const emitHtmlPromise = injectedHtmlPromise
|
|
1127
1490
|
// Allow plugins to change the html after assets are injected
|
|
1128
1491
|
.then((html) => {
|
|
1129
1492
|
const pluginArgs = { html, plugin: this, outputName };
|
|
1130
|
-
return
|
|
1131
|
-
.
|
|
1493
|
+
return HtmlWebpackPlugin.getCompilationHooks(compilation)
|
|
1494
|
+
.beforeEmit.promise(pluginArgs)
|
|
1495
|
+
.then((result) => result.html);
|
|
1132
1496
|
})
|
|
1133
|
-
.catch(err => {
|
|
1497
|
+
.catch((err) => {
|
|
1134
1498
|
// In case anything went wrong the promise is resolved
|
|
1135
1499
|
// with the error message and an error is logged
|
|
1136
1500
|
compilation.errors.push(prettyError(err, compiler.context).toString());
|
|
1137
|
-
return this.options.showErrors
|
|
1501
|
+
return this.options.showErrors
|
|
1502
|
+
? prettyError(err, compiler.context).toHtml()
|
|
1503
|
+
: "ERROR";
|
|
1138
1504
|
})
|
|
1139
|
-
.then(html => {
|
|
1140
|
-
const filename = outputName.replace(
|
|
1141
|
-
(
|
|
1142
|
-
|
|
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,
|
|
1143
1518
|
);
|
|
1144
|
-
const replacedFilename = this.replacePlaceholdersInFilename(compiler, filename, html, compilation);
|
|
1145
1519
|
const source = new compiler.webpack.sources.RawSource(html, false);
|
|
1146
1520
|
|
|
1147
1521
|
// Add the evaluated html code to the webpack assets
|
|
1148
|
-
compilation.emitAsset(
|
|
1522
|
+
compilation.emitAsset(
|
|
1523
|
+
replacedFilename.path,
|
|
1524
|
+
source,
|
|
1525
|
+
replacedFilename.info,
|
|
1526
|
+
);
|
|
1149
1527
|
previousEmittedAssets.push({ name: replacedFilename.path, source });
|
|
1150
1528
|
|
|
1151
1529
|
return replacedFilename.path;
|
|
1152
1530
|
})
|
|
1153
|
-
.then((finalOutputName) =>
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
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
|
+
);
|
|
1161
1544
|
|
|
1162
1545
|
// Once all files are added to the webpack compilation
|
|
1163
1546
|
// let the webpack compiler continue
|
|
@@ -1181,15 +1564,15 @@ class HtmlWebpackPlugin {
|
|
|
1181
1564
|
* @param {ProcessedHtmlWebpackOptions} options
|
|
1182
1565
|
* @returns {TemplateParameter}
|
|
1183
1566
|
*/
|
|
1184
|
-
function templateParametersGenerator
|
|
1567
|
+
function templateParametersGenerator(compilation, assets, assetTags, options) {
|
|
1185
1568
|
return {
|
|
1186
1569
|
compilation: compilation,
|
|
1187
1570
|
webpackConfig: compilation.options,
|
|
1188
1571
|
htmlWebpackPlugin: {
|
|
1189
1572
|
tags: assetTags,
|
|
1190
1573
|
files: assets,
|
|
1191
|
-
options: options
|
|
1192
|
-
}
|
|
1574
|
+
options: options,
|
|
1575
|
+
},
|
|
1193
1576
|
};
|
|
1194
1577
|
}
|
|
1195
1578
|
|
|
@@ -1204,7 +1587,8 @@ HtmlWebpackPlugin.version = 5;
|
|
|
1204
1587
|
*
|
|
1205
1588
|
* Usage: HtmlWebpackPlugin.getHooks(compilation).HOOK_NAME.tapAsync('YourPluginName', () => { ... });
|
|
1206
1589
|
*/
|
|
1207
|
-
|
|
1590
|
+
// TODO remove me in the next major release in favor getCompilationHooks
|
|
1591
|
+
HtmlWebpackPlugin.getHooks = HtmlWebpackPlugin.getCompilationHooks;
|
|
1208
1592
|
HtmlWebpackPlugin.createHtmlTagObject = createHtmlTagObject;
|
|
1209
1593
|
|
|
1210
1594
|
module.exports = HtmlWebpackPlugin;
|