html-webpack-plugin 4.0.0-alpha.1 → 4.0.0-beta.11
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 +217 -66
- package/index.js +235 -135
- package/lib/compiler.js +6 -33
- package/lib/errors.js +8 -1
- package/lib/hooks.js +8 -18
- package/lib/html-tags.js +1 -3
- package/lib/loader.js +4 -3
- package/package.json +28 -28
- package/typings.d.ts +236 -0
package/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
// Import types
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
/** @typedef {import("./typings").HtmlTagObject} HtmlTagObject */
|
|
4
|
+
/** @typedef {import("./typings").Options} HtmlWebpackOptions */
|
|
5
|
+
/** @typedef {import("./typings").ProcessedOptions} ProcessedHtmlWebpackOptions */
|
|
6
|
+
/** @typedef {import("./typings").TemplateParameter} TemplateParameter */
|
|
6
7
|
/** @typedef {import("webpack/lib/Compiler.js")} WebpackCompiler */
|
|
7
8
|
/** @typedef {import("webpack/lib/Compilation.js")} WebpackCompilation */
|
|
8
9
|
'use strict';
|
|
@@ -28,16 +29,16 @@ const fsReadFileAsync = promisify(fs.readFile);
|
|
|
28
29
|
|
|
29
30
|
class HtmlWebpackPlugin {
|
|
30
31
|
/**
|
|
31
|
-
* @param {
|
|
32
|
+
* @param {HtmlWebpackOptions} [options]
|
|
32
33
|
*/
|
|
33
34
|
constructor (options) {
|
|
34
|
-
/** @type {
|
|
35
|
+
/** @type {HtmlWebpackOptions} */
|
|
35
36
|
const userOptions = options || {};
|
|
36
37
|
|
|
37
38
|
// Default options
|
|
38
|
-
/** @type {
|
|
39
|
+
/** @type {ProcessedHtmlWebpackOptions} */
|
|
39
40
|
const defaultOptions = {
|
|
40
|
-
template:
|
|
41
|
+
template: 'auto',
|
|
41
42
|
templateContent: false,
|
|
42
43
|
templateParameters: templateParametersGenerator,
|
|
43
44
|
filename: 'index.html',
|
|
@@ -45,18 +46,19 @@ class HtmlWebpackPlugin {
|
|
|
45
46
|
inject: true,
|
|
46
47
|
compile: true,
|
|
47
48
|
favicon: false,
|
|
48
|
-
minify:
|
|
49
|
+
minify: 'auto',
|
|
49
50
|
cache: true,
|
|
50
51
|
showErrors: true,
|
|
51
52
|
chunks: 'all',
|
|
52
53
|
excludeChunks: [],
|
|
53
54
|
chunksSortMode: 'auto',
|
|
54
55
|
meta: {},
|
|
56
|
+
base: false,
|
|
55
57
|
title: 'Webpack App',
|
|
56
58
|
xhtml: false
|
|
57
59
|
};
|
|
58
60
|
|
|
59
|
-
/** @type {
|
|
61
|
+
/** @type {ProcessedHtmlWebpackOptions} */
|
|
60
62
|
this.options = Object.assign(defaultOptions, userOptions);
|
|
61
63
|
|
|
62
64
|
// Default metaOptions if no template is provided
|
|
@@ -99,6 +101,31 @@ class HtmlWebpackPlugin {
|
|
|
99
101
|
this.options.filename = path.relative(compiler.options.output.path, filename);
|
|
100
102
|
}
|
|
101
103
|
|
|
104
|
+
// `contenthash` is introduced in webpack v4.3
|
|
105
|
+
// which conflicts with the plugin's existing `contenthash` method,
|
|
106
|
+
// hence it is renamed to `templatehash` to avoid conflicts
|
|
107
|
+
this.options.filename = this.options.filename.replace(/\[(?:(\w+):)?contenthash(?::([a-z]+\d*))?(?::(\d+))?\]/ig, (match) => {
|
|
108
|
+
return match.replace('contenthash', 'templatehash');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Check if webpack is running in production mode
|
|
112
|
+
// @see https://github.com/webpack/webpack/blob/3366421f1784c449f415cda5930a8e445086f688/lib/WebpackOptionsDefaulter.js#L12-L14
|
|
113
|
+
const isProductionLikeMode = compiler.options.mode === 'production' || !compiler.options.mode;
|
|
114
|
+
|
|
115
|
+
const minify = this.options.minify;
|
|
116
|
+
if (minify === true || (minify === 'auto' && isProductionLikeMode)) {
|
|
117
|
+
/** @type { import('html-minifier').Options } */
|
|
118
|
+
this.options.minify = {
|
|
119
|
+
// https://github.com/kangax/html-minifier#options-quick-reference
|
|
120
|
+
collapseWhitespace: true,
|
|
121
|
+
removeComments: true,
|
|
122
|
+
removeRedundantAttributes: true,
|
|
123
|
+
removeScriptTypeAttributes: true,
|
|
124
|
+
removeStyleLinkTypeAttributes: true,
|
|
125
|
+
useShortDoctype: true
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
102
129
|
// Clear the cache once a new HtmlWebpackPlugin is added
|
|
103
130
|
childCompiler.clearCache(compiler);
|
|
104
131
|
|
|
@@ -192,11 +219,12 @@ class HtmlWebpackPlugin {
|
|
|
192
219
|
// Turn the js and css paths into grouped HtmlTagObjects
|
|
193
220
|
const assetTagGroupsPromise = assetsPromise
|
|
194
221
|
// And allow third-party-plugin authors to reorder and change the assetTags before they are grouped
|
|
195
|
-
.then(({assets}) => getHtmlWebpackPluginHooks(compilation).alterAssetTags.promise({
|
|
222
|
+
.then(({ assets }) => getHtmlWebpackPluginHooks(compilation).alterAssetTags.promise({
|
|
196
223
|
assetTags: {
|
|
197
224
|
scripts: self.generatedScriptTags(assets.js),
|
|
198
225
|
styles: self.generateStyleTags(assets.css),
|
|
199
226
|
meta: [
|
|
227
|
+
...self.generateBaseTag(self.options.base),
|
|
200
228
|
...self.generatedMetaTags(self.options.meta),
|
|
201
229
|
...self.generateFaviconTags(assets.favicon)
|
|
202
230
|
]
|
|
@@ -204,7 +232,7 @@ class HtmlWebpackPlugin {
|
|
|
204
232
|
outputName: childCompilationOutputName,
|
|
205
233
|
plugin: self
|
|
206
234
|
}))
|
|
207
|
-
.then(({assetTags}) => {
|
|
235
|
+
.then(({ assetTags }) => {
|
|
208
236
|
// Inject scripts to body unless it set explictly to head
|
|
209
237
|
const scriptTarget = self.options.inject === 'head' ? 'head' : 'body';
|
|
210
238
|
// Group assets to `head` and `body` tag arrays
|
|
@@ -237,51 +265,51 @@ class HtmlWebpackPlugin {
|
|
|
237
265
|
: self.executeTemplate(compilationResult, assetsHookResult.assets, { headTags: assetTags.headTags, bodyTags: assetTags.bodyTags }, compilation));
|
|
238
266
|
|
|
239
267
|
const injectedHtmlPromise = Promise.all([assetTagGroupsPromise, templateExectutionPromise])
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
268
|
+
// Allow plugins to change the html before assets are injected
|
|
269
|
+
.then(([assetTags, html]) => {
|
|
270
|
+
const pluginArgs = { html, headTags: assetTags.headTags, bodyTags: assetTags.bodyTags, plugin: self, outputName: childCompilationOutputName };
|
|
271
|
+
return getHtmlWebpackPluginHooks(compilation).afterTemplateExecution.promise(pluginArgs);
|
|
272
|
+
})
|
|
273
|
+
.then(({ html, headTags, bodyTags }) => {
|
|
274
|
+
return self.postProcessHtml(html, assets, { headTags, bodyTags });
|
|
275
|
+
});
|
|
248
276
|
|
|
249
277
|
const emitHtmlPromise = injectedHtmlPromise
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
278
|
+
// Allow plugins to change the html after assets are injected
|
|
279
|
+
.then((html) => {
|
|
280
|
+
const pluginArgs = { html, plugin: self, outputName: childCompilationOutputName };
|
|
281
|
+
return getHtmlWebpackPluginHooks(compilation).beforeEmit.promise(pluginArgs)
|
|
282
|
+
.then(result => result.html);
|
|
283
|
+
})
|
|
284
|
+
.catch(err => {
|
|
285
|
+
// In case anything went wrong the promise is resolved
|
|
286
|
+
// with the error message and an error is logged
|
|
287
|
+
compilation.errors.push(prettyError(err, compiler.context).toString());
|
|
288
|
+
// Prevent caching
|
|
289
|
+
self.hash = null;
|
|
290
|
+
return self.options.showErrors ? prettyError(err, compiler.context).toHtml() : 'ERROR';
|
|
291
|
+
})
|
|
292
|
+
.then(html => {
|
|
293
|
+
// Allow to use [templatehash] as placeholder for the html-webpack-plugin name
|
|
294
|
+
// See also https://survivejs.com/webpack/optimizing/adding-hashes-to-filenames/
|
|
295
|
+
// From https://github.com/webpack-contrib/extract-text-webpack-plugin/blob/8de6558e33487e7606e7cd7cb2adc2cccafef272/src/index.js#L212-L214
|
|
296
|
+
const finalOutputName = childCompilationOutputName.replace(/\[(?:(\w+):)?templatehash(?::([a-z]+\d*))?(?::(\d+))?\]/ig, (_, hashType, digestType, maxLength) => {
|
|
297
|
+
return loaderUtils.getHashDigest(Buffer.from(html, 'utf8'), hashType, digestType, parseInt(maxLength, 10));
|
|
298
|
+
});
|
|
271
299
|
// Add the evaluated html code to the webpack assets
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
300
|
+
compilation.assets[finalOutputName] = {
|
|
301
|
+
source: () => html,
|
|
302
|
+
size: () => html.length
|
|
303
|
+
};
|
|
304
|
+
return finalOutputName;
|
|
305
|
+
})
|
|
306
|
+
.then((finalOutputName) => getHtmlWebpackPluginHooks(compilation).afterEmit.promise({
|
|
307
|
+
outputName: finalOutputName,
|
|
308
|
+
plugin: self
|
|
309
|
+
}).catch(err => {
|
|
310
|
+
console.error(err);
|
|
311
|
+
return null;
|
|
312
|
+
}).then(() => null));
|
|
285
313
|
|
|
286
314
|
// Once all files are added to the webpack compilation
|
|
287
315
|
// let the webpack compiler continue
|
|
@@ -299,14 +327,14 @@ class HtmlWebpackPlugin {
|
|
|
299
327
|
*/
|
|
300
328
|
evaluateCompilationResult (compilation, source) {
|
|
301
329
|
if (!source) {
|
|
302
|
-
return Promise.reject('The child compilation didn\'t provide a result');
|
|
330
|
+
return Promise.reject(new Error('The child compilation didn\'t provide a result'));
|
|
303
331
|
}
|
|
304
332
|
// The LibraryTemplatePlugin stores the template result in a local variable.
|
|
305
333
|
// To extract the result during the evaluation this part has to be removed.
|
|
306
334
|
source = source.replace('var HTML_WEBPACK_PLUGIN_RESULT =', '');
|
|
307
335
|
const template = this.options.template.replace(/^.+!/, '').replace(/\?.+$/, '');
|
|
308
|
-
const vmContext = vm.createContext(_.extend({HTML_WEBPACK_PLUGIN: true, require: require}, global));
|
|
309
|
-
const vmScript = new vm.Script(source, {filename: template});
|
|
336
|
+
const vmContext = vm.createContext(_.extend({ HTML_WEBPACK_PLUGIN: true, require: require }, global));
|
|
337
|
+
const vmScript = new vm.Script(source, { filename: template });
|
|
310
338
|
// Evaluate code and cast to string
|
|
311
339
|
let newSource;
|
|
312
340
|
try {
|
|
@@ -319,7 +347,7 @@ class HtmlWebpackPlugin {
|
|
|
319
347
|
}
|
|
320
348
|
return typeof newSource === 'string' || typeof newSource === 'function'
|
|
321
349
|
? Promise.resolve(newSource)
|
|
322
|
-
: Promise.reject('The loader "' + this.options.template + '" didn\'t return html.');
|
|
350
|
+
: Promise.reject(new Error('The loader "' + this.options.template + '" didn\'t return html.'));
|
|
323
351
|
}
|
|
324
352
|
|
|
325
353
|
/**
|
|
@@ -327,8 +355,8 @@ class HtmlWebpackPlugin {
|
|
|
327
355
|
* @param {WebpackCompilation} compilation
|
|
328
356
|
* @param {{
|
|
329
357
|
publicPath: string,
|
|
330
|
-
js: Array<
|
|
331
|
-
css: Array<
|
|
358
|
+
js: Array<string>,
|
|
359
|
+
css: Array<string>,
|
|
332
360
|
manifest?: string,
|
|
333
361
|
favicon?: string
|
|
334
362
|
}} assets
|
|
@@ -336,17 +364,24 @@ class HtmlWebpackPlugin {
|
|
|
336
364
|
headTags: HtmlTagObject[],
|
|
337
365
|
bodyTags: HtmlTagObject[]
|
|
338
366
|
}} assetTags
|
|
339
|
-
* @returns {{[key: any]: any}}
|
|
367
|
+
* @returns {Promise<{[key: any]: any}>}
|
|
340
368
|
*/
|
|
341
369
|
getTemplateParameters (compilation, assets, assetTags) {
|
|
342
|
-
|
|
343
|
-
|
|
370
|
+
const templateParameters = this.options.templateParameters;
|
|
371
|
+
if (templateParameters === false) {
|
|
372
|
+
return Promise.resolve({});
|
|
344
373
|
}
|
|
345
|
-
if (typeof
|
|
346
|
-
|
|
374
|
+
if (typeof templateParameters === 'function') {
|
|
375
|
+
const preparedAssetTags = {
|
|
376
|
+
headTags: this.prepareAssetTagGroupForRendering(assetTags.headTags),
|
|
377
|
+
bodyTags: this.prepareAssetTagGroupForRendering(assetTags.bodyTags)
|
|
378
|
+
};
|
|
379
|
+
return Promise
|
|
380
|
+
.resolve()
|
|
381
|
+
.then(() => templateParameters(compilation, assets, preparedAssetTags, this.options));
|
|
347
382
|
}
|
|
348
|
-
if (typeof
|
|
349
|
-
return
|
|
383
|
+
if (typeof templateParameters === 'object') {
|
|
384
|
+
return Promise.resolve(templateParameters);
|
|
350
385
|
}
|
|
351
386
|
throw new Error('templateParameters has to be either a function or an object');
|
|
352
387
|
}
|
|
@@ -354,11 +389,11 @@ class HtmlWebpackPlugin {
|
|
|
354
389
|
/**
|
|
355
390
|
* This function renders the actual html by executing the template function
|
|
356
391
|
*
|
|
357
|
-
* @param {(
|
|
392
|
+
* @param {(templateParameters) => string | Promise<string>} templateFunction
|
|
358
393
|
* @param {{
|
|
359
394
|
publicPath: string,
|
|
360
|
-
js: Array<
|
|
361
|
-
css: Array<
|
|
395
|
+
js: Array<string>,
|
|
396
|
+
css: Array<string>,
|
|
362
397
|
manifest?: string,
|
|
363
398
|
favicon?: string
|
|
364
399
|
}} assets
|
|
@@ -372,18 +407,17 @@ class HtmlWebpackPlugin {
|
|
|
372
407
|
*/
|
|
373
408
|
executeTemplate (templateFunction, assets, assetTags, compilation) {
|
|
374
409
|
// Template processing
|
|
375
|
-
const
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
return Promise.resolve().then(() => html);
|
|
410
|
+
const templateParamsPromise = this.getTemplateParameters(compilation, assets, assetTags);
|
|
411
|
+
return templateParamsPromise.then((templateParams) => {
|
|
412
|
+
try {
|
|
413
|
+
// If html is a promise return the promise
|
|
414
|
+
// If html is a string turn it into a promise
|
|
415
|
+
return templateFunction(templateParams);
|
|
416
|
+
} catch (e) {
|
|
417
|
+
compilation.errors.push(new Error('Template execution failed: ' + e));
|
|
418
|
+
return Promise.reject(e);
|
|
419
|
+
}
|
|
420
|
+
});
|
|
387
421
|
}
|
|
388
422
|
|
|
389
423
|
/**
|
|
@@ -402,13 +436,13 @@ class HtmlWebpackPlugin {
|
|
|
402
436
|
*/
|
|
403
437
|
postProcessHtml (html, assets, assetTags) {
|
|
404
438
|
if (typeof html !== 'string') {
|
|
405
|
-
return Promise.reject('Expected html to be a string but got ' + JSON.stringify(html));
|
|
439
|
+
return Promise.reject(new Error('Expected html to be a string but got ' + JSON.stringify(html)));
|
|
406
440
|
}
|
|
407
441
|
const htmlAfterInjection = this.options.inject
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
const htmlAfterMinification = this.options.minify
|
|
411
|
-
? require('html-minifier').minify(htmlAfterInjection, this.options.minify
|
|
442
|
+
? this.injectAssetsIntoHtml(html, assets, assetTags)
|
|
443
|
+
: html;
|
|
444
|
+
const htmlAfterMinification = typeof this.options.minify === 'object'
|
|
445
|
+
? require('html-minifier-terser').minify(htmlAfterInjection, this.options.minify)
|
|
412
446
|
: htmlAfterInjection;
|
|
413
447
|
return Promise.resolve(htmlAfterMinification);
|
|
414
448
|
}
|
|
@@ -488,14 +522,14 @@ class HtmlWebpackPlugin {
|
|
|
488
522
|
*
|
|
489
523
|
* @param {{
|
|
490
524
|
publicPath: string,
|
|
491
|
-
js: Array<
|
|
492
|
-
css: Array<
|
|
525
|
+
js: Array<string>,
|
|
526
|
+
css: Array<string>,
|
|
493
527
|
manifest?: string,
|
|
494
528
|
favicon?: string
|
|
495
529
|
}} assets
|
|
496
530
|
*/
|
|
497
531
|
isHotUpdateCompilation (assets) {
|
|
498
|
-
return assets.js.length && assets.js.every((
|
|
532
|
+
return assets.js.length && assets.js.every((assetPath) => /\.hot-update\.js$/.test(assetPath));
|
|
499
533
|
}
|
|
500
534
|
|
|
501
535
|
/**
|
|
@@ -505,8 +539,8 @@ class HtmlWebpackPlugin {
|
|
|
505
539
|
* @param {string[]} entryNames
|
|
506
540
|
* @returns {{
|
|
507
541
|
publicPath: string,
|
|
508
|
-
js: Array<
|
|
509
|
-
css: Array<
|
|
542
|
+
js: Array<string>,
|
|
543
|
+
css: Array<string>,
|
|
510
544
|
manifest?: string,
|
|
511
545
|
favicon?: string
|
|
512
546
|
}}
|
|
@@ -516,12 +550,14 @@ class HtmlWebpackPlugin {
|
|
|
516
550
|
|
|
517
551
|
/**
|
|
518
552
|
* @type {string} the configured public path to the asset root
|
|
519
|
-
* if a publicPath is set in the current webpack config use it otherwise
|
|
553
|
+
* if a path publicPath is set in the current webpack config use it otherwise
|
|
520
554
|
* fallback to a realtive path
|
|
521
555
|
*/
|
|
522
|
-
|
|
556
|
+
const webpackPublicPath = compilation.mainTemplate.getPublicPath({ hash: compilationHash });
|
|
557
|
+
const isPublicPathDefined = webpackPublicPath.trim() !== '';
|
|
558
|
+
let publicPath = isPublicPathDefined
|
|
523
559
|
// If a hard coded public path exists use it
|
|
524
|
-
?
|
|
560
|
+
? webpackPublicPath
|
|
525
561
|
// If no public path was set get a relative url path
|
|
526
562
|
: path.relative(path.resolve(compilation.options.output.path, path.dirname(childCompilationOutputName)), compilation.options.output.path)
|
|
527
563
|
.split(path.sep).join('/');
|
|
@@ -533,8 +569,8 @@ class HtmlWebpackPlugin {
|
|
|
533
569
|
/**
|
|
534
570
|
* @type {{
|
|
535
571
|
publicPath: string,
|
|
536
|
-
js: Array<
|
|
537
|
-
css: Array<
|
|
572
|
+
js: Array<string>,
|
|
573
|
+
css: Array<string>,
|
|
538
574
|
manifest?: string,
|
|
539
575
|
favicon?: string
|
|
540
576
|
}}
|
|
@@ -542,7 +578,7 @@ class HtmlWebpackPlugin {
|
|
|
542
578
|
const assets = {
|
|
543
579
|
// The public path
|
|
544
580
|
publicPath: publicPath,
|
|
545
|
-
// Will contain all js files
|
|
581
|
+
// Will contain all js and mjs files
|
|
546
582
|
js: [],
|
|
547
583
|
// Will contain all css files
|
|
548
584
|
css: [],
|
|
@@ -557,8 +593,9 @@ class HtmlWebpackPlugin {
|
|
|
557
593
|
assets.manifest = this.appendHash(assets.manifest, compilationHash);
|
|
558
594
|
}
|
|
559
595
|
|
|
560
|
-
// Extract paths to .js and .css files from the current compilation
|
|
561
|
-
const
|
|
596
|
+
// Extract paths to .js, .mjs and .css files from the current compilation
|
|
597
|
+
const entryPointPublicPathMap = {};
|
|
598
|
+
const extensionRegexp = /\.(css|js|mjs)(\?|$)/;
|
|
562
599
|
for (let i = 0; i < entryNames.length; i++) {
|
|
563
600
|
const entryName = entryNames[i];
|
|
564
601
|
const entryPointFiles = compilation.entrypoints.get(entryName).getFiles();
|
|
@@ -567,24 +604,27 @@ class HtmlWebpackPlugin {
|
|
|
567
604
|
// E.g. bundle.js -> /bundle.js?hash
|
|
568
605
|
const entryPointPublicPaths = entryPointFiles
|
|
569
606
|
.map(chunkFile => {
|
|
570
|
-
const entryPointPublicPath = publicPath + chunkFile;
|
|
607
|
+
const entryPointPublicPath = publicPath + this.urlencodePath(chunkFile);
|
|
571
608
|
return this.options.hash
|
|
572
609
|
? this.appendHash(entryPointPublicPath, compilationHash)
|
|
573
610
|
: entryPointPublicPath;
|
|
574
611
|
});
|
|
575
612
|
|
|
576
|
-
entryPointPublicPaths.forEach((
|
|
577
|
-
const extMatch = extensionRegexp.exec(
|
|
578
|
-
// Skip if the public path is not a .css or .js file
|
|
613
|
+
entryPointPublicPaths.forEach((entryPointPublicPath) => {
|
|
614
|
+
const extMatch = extensionRegexp.exec(entryPointPublicPath);
|
|
615
|
+
// Skip if the public path is not a .css, .mjs or .js file
|
|
579
616
|
if (!extMatch) {
|
|
580
617
|
return;
|
|
581
618
|
}
|
|
582
|
-
//
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
619
|
+
// Skip if this file is already known
|
|
620
|
+
// (e.g. because of common chunk optimizations)
|
|
621
|
+
if (entryPointPublicPathMap[entryPointPublicPath]) {
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
entryPointPublicPathMap[entryPointPublicPath] = true;
|
|
625
|
+
// ext will contain .js or .css, because .mjs recognizes as .js
|
|
626
|
+
const ext = extMatch[1] === 'mjs' ? 'js' : extMatch[1];
|
|
627
|
+
assets[ext].push(entryPointPublicPath);
|
|
588
628
|
});
|
|
589
629
|
}
|
|
590
630
|
return assets;
|
|
@@ -596,7 +636,7 @@ class HtmlWebpackPlugin {
|
|
|
596
636
|
*
|
|
597
637
|
* @param {string|false} faviconFilePath
|
|
598
638
|
* @param {WebpackCompilation} compilation
|
|
599
|
-
* @
|
|
639
|
+
* @param {string} publicPath
|
|
600
640
|
* @returns {Promise<string|undefined>}
|
|
601
641
|
*/
|
|
602
642
|
getFaviconPublicPath (faviconFilePath, compilation, publicPath) {
|
|
@@ -626,14 +666,14 @@ class HtmlWebpackPlugin {
|
|
|
626
666
|
// Turn { "viewport" : "width=500, initial-scale=1" } into
|
|
627
667
|
// [{ name:"viewport" content:"width=500, initial-scale=1" }]
|
|
628
668
|
const metaTagAttributeObjects = Object.keys(metaOptions)
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
669
|
+
.map((metaName) => {
|
|
670
|
+
const metaTagContent = metaOptions[metaName];
|
|
671
|
+
return (typeof metaTagContent === 'string') ? {
|
|
672
|
+
name: metaName,
|
|
673
|
+
content: metaTagContent
|
|
674
|
+
} : metaTagContent;
|
|
675
|
+
})
|
|
676
|
+
.filter((attribute) => attribute !== false);
|
|
637
677
|
// Turn [{ name:"viewport" content:"width=500, initial-scale=1" }] into
|
|
638
678
|
// the html-webpack-plugin tag structure
|
|
639
679
|
return metaTagAttributeObjects.map((metaTagAttributes) => {
|
|
@@ -650,42 +690,64 @@ class HtmlWebpackPlugin {
|
|
|
650
690
|
|
|
651
691
|
/**
|
|
652
692
|
* Generate all tags script for the given file paths
|
|
653
|
-
* @param {Array<
|
|
693
|
+
* @param {Array<string>} jsAssets
|
|
654
694
|
* @returns {Array<HtmlTagObject>}
|
|
655
695
|
*/
|
|
656
696
|
generatedScriptTags (jsAssets) {
|
|
657
697
|
return jsAssets.map(scriptAsset => ({
|
|
658
698
|
tagName: 'script',
|
|
659
699
|
voidTag: false,
|
|
660
|
-
entry: scriptAsset.entryName,
|
|
661
700
|
attributes: {
|
|
662
|
-
src: scriptAsset
|
|
701
|
+
src: scriptAsset
|
|
663
702
|
}
|
|
664
703
|
}));
|
|
665
704
|
}
|
|
666
705
|
|
|
667
706
|
/**
|
|
668
707
|
* Generate all style tags for the given file paths
|
|
669
|
-
* @param {Array<
|
|
708
|
+
* @param {Array<string>} cssAssets
|
|
670
709
|
* @returns {Array<HtmlTagObject>}
|
|
671
710
|
*/
|
|
672
711
|
generateStyleTags (cssAssets) {
|
|
673
712
|
return cssAssets.map(styleAsset => ({
|
|
674
713
|
tagName: 'link',
|
|
675
714
|
voidTag: true,
|
|
676
|
-
entry: styleAsset.entryName,
|
|
677
715
|
attributes: {
|
|
678
|
-
href: styleAsset
|
|
716
|
+
href: styleAsset,
|
|
679
717
|
rel: 'stylesheet'
|
|
680
718
|
}
|
|
681
719
|
}));
|
|
682
720
|
}
|
|
683
721
|
|
|
722
|
+
/**
|
|
723
|
+
* Generate an optional base tag
|
|
724
|
+
* @param { false
|
|
725
|
+
| string
|
|
726
|
+
| {[attributeName: string]: string} // attributes e.g. { href:"http://example.com/page.html" target:"_blank" }
|
|
727
|
+
} baseOption
|
|
728
|
+
* @returns {Array<HtmlTagObject>}
|
|
729
|
+
*/
|
|
730
|
+
generateBaseTag (baseOption) {
|
|
731
|
+
if (baseOption === false) {
|
|
732
|
+
return [];
|
|
733
|
+
} else {
|
|
734
|
+
return [{
|
|
735
|
+
tagName: 'base',
|
|
736
|
+
voidTag: true,
|
|
737
|
+
attributes: (typeof baseOption === 'string') ? {
|
|
738
|
+
href: baseOption
|
|
739
|
+
} : baseOption
|
|
740
|
+
}];
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
|
|
684
744
|
/**
|
|
685
745
|
* Generate all meta tags for the given meta configuration
|
|
686
746
|
* @param {false | {
|
|
687
|
-
[name: string]:
|
|
688
|
-
|
|
747
|
+
[name: string]:
|
|
748
|
+
false // disabled
|
|
749
|
+
| string // name content pair e.g. {viewport: 'width=device-width, initial-scale=1, shrink-to-fit=no'}`
|
|
750
|
+
| {[attributeName: string]: string|boolean} // custom properties e.g. { name:"viewport" content:"width=500, initial-scale=1" }
|
|
689
751
|
}} metaOptions
|
|
690
752
|
* @returns {Array<HtmlTagObject>}
|
|
691
753
|
*/
|
|
@@ -693,9 +755,9 @@ class HtmlWebpackPlugin {
|
|
|
693
755
|
if (metaOptions === false) {
|
|
694
756
|
return [];
|
|
695
757
|
}
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
758
|
+
// Make tags self-closing in case of xhtml
|
|
759
|
+
// Turn { "viewport" : "width=500, initial-scale=1" } into
|
|
760
|
+
// [{ name:"viewport" content:"width=500, initial-scale=1" }]
|
|
699
761
|
const metaTagAttributeObjects = Object.keys(metaOptions)
|
|
700
762
|
.map((metaName) => {
|
|
701
763
|
const metaTagContent = metaOptions[metaName];
|
|
@@ -771,6 +833,28 @@ class HtmlWebpackPlugin {
|
|
|
771
833
|
return result;
|
|
772
834
|
}
|
|
773
835
|
|
|
836
|
+
/**
|
|
837
|
+
* Add toString methods for easier rendering
|
|
838
|
+
* inside the template
|
|
839
|
+
*
|
|
840
|
+
* @param {Array<HtmlTagObject>} assetTagGroup
|
|
841
|
+
* @returns {Array<HtmlTagObject>}
|
|
842
|
+
*/
|
|
843
|
+
prepareAssetTagGroupForRendering (assetTagGroup) {
|
|
844
|
+
const xhtml = this.options.xhtml;
|
|
845
|
+
const preparedTags = assetTagGroup.map((assetTag) => {
|
|
846
|
+
const copiedAssetTag = Object.assign({}, assetTag);
|
|
847
|
+
copiedAssetTag.toString = function () {
|
|
848
|
+
return htmlTagObjectToString(this, xhtml);
|
|
849
|
+
};
|
|
850
|
+
return copiedAssetTag;
|
|
851
|
+
});
|
|
852
|
+
preparedTags.toString = function () {
|
|
853
|
+
return this.join('');
|
|
854
|
+
};
|
|
855
|
+
return preparedTags;
|
|
856
|
+
}
|
|
857
|
+
|
|
774
858
|
/**
|
|
775
859
|
* Injects the assets into the given html string
|
|
776
860
|
*
|
|
@@ -842,14 +926,30 @@ class HtmlWebpackPlugin {
|
|
|
842
926
|
return url + (url.indexOf('?') === -1 ? '?' : '&') + hash;
|
|
843
927
|
}
|
|
844
928
|
|
|
929
|
+
/**
|
|
930
|
+
* Encode each path component using `encodeURIComponent` as files can contain characters
|
|
931
|
+
* which needs special encoding in URLs like `+ `.
|
|
932
|
+
*
|
|
933
|
+
* @param {string} filePath
|
|
934
|
+
*/
|
|
935
|
+
urlencodePath (filePath) {
|
|
936
|
+
return filePath.split('/').map(encodeURIComponent).join('/');
|
|
937
|
+
}
|
|
938
|
+
|
|
845
939
|
/**
|
|
846
940
|
* Helper to return the absolute template path with a fallback loader
|
|
847
941
|
* @param {string} template
|
|
848
|
-
* The path to the
|
|
942
|
+
* The path to the template e.g. './index.html'
|
|
849
943
|
* @param {string} context
|
|
850
944
|
* The webpack base resolution path for relative paths e.g. process.cwd()
|
|
851
945
|
*/
|
|
852
946
|
getFullTemplatePath (template, context) {
|
|
947
|
+
if (template === 'auto') {
|
|
948
|
+
template = path.resolve(context, 'src/index.ejs');
|
|
949
|
+
if (!fs.existsSync(template)) {
|
|
950
|
+
template = path.join(__dirname, 'default_index.ejs');
|
|
951
|
+
}
|
|
952
|
+
}
|
|
853
953
|
// If the template doesn't use a loader use the lodash template loader
|
|
854
954
|
if (template.indexOf('!') === -1) {
|
|
855
955
|
template = require.resolve('./lib/loader.js') + '!' + path.resolve(context, template);
|
|
@@ -879,8 +979,8 @@ class HtmlWebpackPlugin {
|
|
|
879
979
|
* @param {WebpackCompilation} compilation
|
|
880
980
|
* @param {{
|
|
881
981
|
publicPath: string,
|
|
882
|
-
js: Array<
|
|
883
|
-
css: Array<
|
|
982
|
+
js: Array<string>,
|
|
983
|
+
css: Array<string>,
|
|
884
984
|
manifest?: string,
|
|
885
985
|
favicon?: string
|
|
886
986
|
}} assets
|
|
@@ -888,8 +988,8 @@ class HtmlWebpackPlugin {
|
|
|
888
988
|
headTags: HtmlTagObject[],
|
|
889
989
|
bodyTags: HtmlTagObject[]
|
|
890
990
|
}} assetTags
|
|
891
|
-
* @param {
|
|
892
|
-
* @returns {
|
|
991
|
+
* @param {ProcessedHtmlWebpackOptions} options
|
|
992
|
+
* @returns {TemplateParameter}
|
|
893
993
|
*/
|
|
894
994
|
function templateParametersGenerator (compilation, assets, assetTags, options) {
|
|
895
995
|
return {
|
|
@@ -912,7 +1012,7 @@ HtmlWebpackPlugin.version = 4;
|
|
|
912
1012
|
/**
|
|
913
1013
|
* A static helper to get the hooks for this plugin
|
|
914
1014
|
*
|
|
915
|
-
* Usage: HtmlWebpackPlugin.
|
|
1015
|
+
* Usage: HtmlWebpackPlugin.getHooks(compilation).HOOK_NAME.tapAsync('YourPluginName', () => { ... });
|
|
916
1016
|
*/
|
|
917
1017
|
HtmlWebpackPlugin.getHooks = getHtmlWebpackPluginHooks;
|
|
918
1018
|
HtmlWebpackPlugin.createHtmlTagObject = createHtmlTagObject;
|