html-webpack-plugin 5.1.0 → 5.3.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/CHANGELOG.md CHANGED
@@ -2,6 +2,34 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [5.3.2](https://github.com/jantimon/html-webpack-plugin/compare/v5.3.1...v5.3.2) (2021-06-22)
6
+
7
+ ### Bug Fixes
8
+
9
+ * update lodash and pretty error ([9c7fba0](https://github.com/jantimon/html-webpack-plugin/commit/9c7fba02d0aa7d9e804863a66eb896db3046f645)
10
+
11
+ ### [5.3.1](https://github.com/jantimon/html-webpack-plugin/compare/v5.3.0...v5.3.1) (2021-03-09)
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * remove loader-utils from plugin core ([82d0ee8](https://github.com/jantimon/html-webpack-plugin/commit/82d0ee8ddf146f17d71e98c1b44b2f2ec7420051))
17
+
18
+ ## [5.3.0](https://github.com/jantimon/html-webpack-plugin/compare/v5.2.0...v5.3.0) (2021-03-07)
19
+
20
+
21
+ ### Features
22
+
23
+ * allow to modify the interpolation options in webpack config ([d654f5b](https://github.com/jantimon/html-webpack-plugin/commit/d654f5b90022304335b372d424ff4c08d3a9d341))
24
+ * drop loader-utils dependency ([41d7a50](https://github.com/jantimon/html-webpack-plugin/commit/41d7a50516aefd1af2704e3837d5d41351c6199b))
25
+
26
+ ## [5.2.0](https://github.com/jantimon/html-webpack-plugin/compare/v5.1.0...v5.2.0) (2021-02-19)
27
+
28
+
29
+ ### Features
30
+
31
+ * improve ssr ([73d2a66](https://github.com/jantimon/html-webpack-plugin/commit/73d2a660b10b9ebf8a341f0ddb173bcaaf1e513c))
32
+
5
33
  ## [5.1.0](https://github.com/jantimon/html-webpack-plugin/compare/v5.0.0...v5.1.0) (2021-02-12)
6
34
 
7
35
 
package/README.md CHANGED
@@ -89,13 +89,13 @@ The `html-webpack-plugin` provides [hooks](https://github.com/jantimon/html-webp
89
89
  * [webpack-nomodule-plugin](https://github.com/swimmadude66/webpack-nomodule-plugin) allows you to add a `nomodule` attribute to specific injected scripts, which prevents the scripts from being loaded by newer browsers. Good for limiting loads of polyfills.
90
90
  * [html-webpack-skip-assets-plugin](https://github.com/swimmadude66/html-webpack-skip-assets-plugin) Skip adding certain output files to the html file. Built as a drop-in replacement for [html-webpack-exclude-assets-plugin](https://www.npmjs.com/package/html-webpack-exclude-assets-plugin) and works with newer [html-webpack-plugin](https://github.com/jantimon/html-webpack-plugin) versions
91
91
  * [html-webpack-inject-preload](https://github.com/principalstudio/html-webpack-inject-preload) allows to add preload links <link rel='preload'> anywhere you want.
92
-
92
+ * [inject-body-webpack-plugin](https://github.com/Jaid/inject-body-webpack-plugin) is a simple method of injecting a custom HTML string into the body.
93
93
 
94
94
 
95
95
  <h2 align="center">Usage</h2>
96
96
 
97
97
  The plugin will generate an HTML5 file for you that includes all your `webpack`
98
- bundles in the body using `script` tags. Just add the plugin to your `webpack`
98
+ bundles in the head using `script` tags. Just add the plugin to your `webpack`
99
99
  config as follows:
100
100
 
101
101
  **webpack.config.js**
@@ -122,9 +122,9 @@ This will generate a file `dist/index.html` containing the following
122
122
  <head>
123
123
  <meta charset="utf-8">
124
124
  <title>Webpack App</title>
125
+ <script defer src="index_bundle.js"></script>
125
126
  </head>
126
127
  <body>
127
- <script src="index_bundle.js"></script>
128
128
  </body>
129
129
  </html>
130
130
  ```
@@ -464,7 +464,7 @@ which will inject the element `<base href="http://example.com/some/page.html" ta
464
464
 
465
465
  ### Long Term Caching
466
466
 
467
- For long term caching add `contenthash/templatehash` to the filename.
467
+ For long term caching add `contenthash` to the filename.
468
468
 
469
469
  **Example:**
470
470
 
@@ -476,15 +476,9 @@ plugins: [
476
476
  ]
477
477
  ```
478
478
 
479
- `contenthash/templatehash` is the hash of the content of the output file.
480
-
481
- Optionally, You can configure like `[<hashType>:contenthash:<digestType>:<length>]`
482
-
483
- * `hashType` - one of `sha1`, `md5`, `sha256`, `sha512` or any other node.js supported hash type
484
- * `digestType` - one of `hex`, `base26`, `base32`, `base36`, `base49`, `base52`, `base58`, `base62`, `base64`
485
- * `maxlength` - maximum length of the generated hash in chars
479
+ `contenthash` is the hash of the content of the output file.
486
480
 
487
- **Defaults:** `[md5:contenthash:hex:9999]`
481
+ Refer webpack's [Template Strings](https://webpack.js.org/configuration/output/#template-strings) for more details
488
482
 
489
483
  ### Events
490
484
 
package/index.js CHANGED
@@ -14,7 +14,6 @@ const vm = require('vm');
14
14
  const fs = require('fs');
15
15
  const _ = require('lodash');
16
16
  const path = require('path');
17
- const loaderUtils = require('loader-utils');
18
17
  const { CachedChildCompilation } = require('./lib/cached-child-compiler');
19
18
 
20
19
  const { createHtmlTagObject, htmlTagObjectToString, HtmlTagArray } = require('./lib/html-tags');
@@ -123,12 +122,19 @@ class HtmlWebpackPlugin {
123
122
  return Promise.reject(new Error('The child compilation didn\'t provide a result'));
124
123
  }
125
124
  // The LibraryTemplatePlugin stores the template result in a local variable.
126
- // To extract the result during the evaluation this part has to be removed.
127
- if (source && source.indexOf('HTML_WEBPACK_PLUGIN_RESULT') >= 0) {
125
+ // By adding it to the end the value gets extracted during evaluation
126
+ if (source.indexOf('HTML_WEBPACK_PLUGIN_RESULT') >= 0) {
128
127
  source += ';\nHTML_WEBPACK_PLUGIN_RESULT';
129
128
  }
130
129
  const templateWithoutLoaders = templateFilename.replace(/^.+!/, '').replace(/\?.+$/, '');
131
- const vmContext = vm.createContext({ HTML_WEBPACK_PLUGIN: true, require: require, htmlWebpackPluginPublicPath: publicPath, ...global });
130
+ const vmContext = vm.createContext({
131
+ ...global,
132
+ HTML_WEBPACK_PLUGIN: true,
133
+ require: require,
134
+ htmlWebpackPluginPublicPath: publicPath,
135
+ URL: require('url').URL,
136
+ __filename: templateWithoutLoaders
137
+ });
132
138
  const vmScript = new vm.Script(source, { filename: templateWithoutLoaders });
133
139
  // Evaluate code and cast to string
134
140
  let newSource;
@@ -147,7 +153,8 @@ class HtmlWebpackPlugin {
147
153
  }
148
154
 
149
155
  /**
150
- * apply is called by the webpack main compiler during the start phase
156
+ * connect the html-webpack-plugin to the webpack compiler lifecycle hooks
157
+ *
151
158
  * @param {import('webpack').Compiler} compiler
152
159
  * @param {ProcessedHtmlWebpackOptions} options
153
160
  * @param {HtmlWebpackPlugin} plugin
@@ -180,13 +187,6 @@ function hookIntoCompiler (compiler, options, plugin) {
180
187
  options.filename = path.relative(outputPath, filename);
181
188
  }
182
189
 
183
- // `contenthash` is introduced in webpack v4.3
184
- // which conflicts with the plugin's existing `contenthash` method,
185
- // hence it is renamed to `templatehash` to avoid conflicts
186
- options.filename = options.filename.replace(/\[(?:(\w+):)?contenthash(?::([a-z]+\d*))?(?::(\d+))?\]/ig, (match) => {
187
- return match.replace('contenthash', 'templatehash');
188
- });
189
-
190
190
  // Check if webpack is running in production mode
191
191
  // @see https://github.com/webpack/webpack/blob/3366421f1784c449f415cda5930a8e445086f688/lib/WebpackOptionsDefaulter.js#L12-L14
192
192
  const isProductionLikeMode = compiler.options.mode === 'production' || !compiler.options.mode;
@@ -240,21 +240,12 @@ function hookIntoCompiler (compiler, options, plugin) {
240
240
  compilation.errors.push(prettyError(templateResult.error, compiler.context).toString());
241
241
  }
242
242
 
243
- const compiledEntries = 'compiledEntry' in templateResult ? {
244
- hash: templateResult.compiledEntry.hash,
245
- chunk: templateResult.compiledEntry.entry
246
- } : {
247
- hash: templateResult.mainCompilationHash
248
- };
249
-
250
- const childCompilationOutputName = compilation.getAssetPath(options.filename, compiledEntries);
251
-
252
243
  // If the child compilation was not executed during a previous main compile run
253
244
  // it is a cached result
254
245
  const isCompilationCached = templateResult.mainCompilationHash !== compilation.hash;
255
246
 
256
247
  /** The public path used inside the html file */
257
- const htmlPublicPath = getPublicPath(compilation, childCompilationOutputName, options.publicPath);
248
+ const htmlPublicPath = getPublicPath(compilation, options.filename, options.publicPath);
258
249
 
259
250
  /** Generated file paths from the entry point names */
260
251
  const assets = htmlWebpackPluginAssets(compilation, sortedEntryNames, htmlPublicPath);
@@ -279,7 +270,7 @@ function hookIntoCompiler (compiler, options, plugin) {
279
270
  assets.favicon = faviconPath;
280
271
  return getHtmlWebpackPluginHooks(compilation).beforeAssetTagGeneration.promise({
281
272
  assets: assets,
282
- outputName: childCompilationOutputName,
273
+ outputName: options.filename,
283
274
  plugin: plugin
284
275
  });
285
276
  });
@@ -297,7 +288,7 @@ function hookIntoCompiler (compiler, options, plugin) {
297
288
  ...generateFaviconTags(assets.favicon)
298
289
  ]
299
290
  },
300
- outputName: childCompilationOutputName,
291
+ outputName: options.filename,
301
292
  publicPath: htmlPublicPath,
302
293
  plugin: plugin
303
294
  }))
@@ -311,7 +302,7 @@ function hookIntoCompiler (compiler, options, plugin) {
311
302
  return getHtmlWebpackPluginHooks(compilation).alterAssetTagGroups.promise({
312
303
  headTags: assetGroups.headTags,
313
304
  bodyTags: assetGroups.bodyTags,
314
- outputName: childCompilationOutputName,
305
+ outputName: options.filename,
315
306
  publicPath: htmlPublicPath,
316
307
  plugin: plugin
317
308
  });
@@ -342,7 +333,7 @@ function hookIntoCompiler (compiler, options, plugin) {
342
333
  const injectedHtmlPromise = Promise.all([assetTagGroupsPromise, templateExectutionPromise])
343
334
  // Allow plugins to change the html before assets are injected
344
335
  .then(([assetTags, html]) => {
345
- const pluginArgs = { html, headTags: assetTags.headTags, bodyTags: assetTags.bodyTags, plugin: plugin, outputName: childCompilationOutputName };
336
+ const pluginArgs = { html, headTags: assetTags.headTags, bodyTags: assetTags.bodyTags, plugin: plugin, outputName: options.filename };
346
337
  return getHtmlWebpackPluginHooks(compilation).afterTemplateExecution.promise(pluginArgs);
347
338
  })
348
339
  .then(({ html, headTags, bodyTags }) => {
@@ -352,7 +343,7 @@ function hookIntoCompiler (compiler, options, plugin) {
352
343
  const emitHtmlPromise = injectedHtmlPromise
353
344
  // Allow plugins to change the html after assets are injected
354
345
  .then((html) => {
355
- const pluginArgs = { html, plugin: plugin, outputName: childCompilationOutputName };
346
+ const pluginArgs = { html, plugin: plugin, outputName: options.filename };
356
347
  return getHtmlWebpackPluginHooks(compilation).beforeEmit.promise(pluginArgs)
357
348
  .then(result => result.html);
358
349
  })
@@ -363,16 +354,15 @@ function hookIntoCompiler (compiler, options, plugin) {
363
354
  return options.showErrors ? prettyError(err, compiler.context).toHtml() : 'ERROR';
364
355
  })
365
356
  .then(html => {
366
- // Allow to use [templatehash] as placeholder for the html-webpack-plugin name
367
- // See also https://survivejs.com/webpack/optimizing/adding-hashes-to-filenames/
368
- // From https://github.com/webpack-contrib/extract-text-webpack-plugin/blob/8de6558e33487e7606e7cd7cb2adc2cccafef272/src/index.js#L212-L214
369
- const finalOutputName = childCompilationOutputName.replace(/\[(?:(\w+):)?templatehash(?::([a-z]+\d*))?(?::(\d+))?\]/ig, (_, hashType, digestType, maxLength) => {
370
- return loaderUtils.getHashDigest(Buffer.from(html, 'utf8'), hashType, digestType, parseInt(maxLength, 10));
371
- });
357
+ const filename = options.filename.replace(/\[templatehash([^\]]*)\]/g, require('util').deprecate(
358
+ (match, options) => `[contenthash${options}]`,
359
+ '[templatehash] is now [contenthash]')
360
+ );
361
+ const replacedFilename = replacePlaceholdersInFilename(filename, html, compilation);
372
362
  // Add the evaluated html code to the webpack assets
373
- compilation.emitAsset(finalOutputName, new webpack.sources.RawSource(html, false));
374
- previousEmittedAssets.push({ name: finalOutputName, html });
375
- return finalOutputName;
363
+ compilation.emitAsset(replacedFilename.path, new webpack.sources.RawSource(html, false), replacedFilename.info);
364
+ previousEmittedAssets.push({ name: replacedFilename.path, html });
365
+ return replacedFilename.path;
376
366
  })
377
367
  .then((finalOutputName) => getHtmlWebpackPluginHooks(compilation).afterEmit.promise({
378
368
  outputName: finalOutputName,
@@ -510,6 +500,38 @@ function hookIntoCompiler (compiler, options, plugin) {
510
500
  });
511
501
  }
512
502
 
503
+ /**
504
+ * Replace [contenthash] in filename
505
+ *
506
+ * @see https://survivejs.com/webpack/optimizing/adding-hashes-to-filenames/
507
+ *
508
+ * @param {string} filename
509
+ * @param {string|Buffer} fileContent
510
+ * @param {WebpackCompilation} compilation
511
+ * @returns {{ path: string, info: {} }}
512
+ */
513
+ function replacePlaceholdersInFilename (filename, fileContent, compilation) {
514
+ if (/\[\\*([\w:]+)\\*\]/i.test(filename) === false) {
515
+ return { path: filename, info: {} };
516
+ }
517
+ const hash = compiler.webpack.util.createHash(compilation.outputOptions.hashFunction);
518
+ hash.update(fileContent);
519
+ if (compilation.outputOptions.hashSalt) {
520
+ hash.update(compilation.outputOptions.hashSalt);
521
+ }
522
+ const contentHash = hash.digest(compilation.outputOptions.hashDigest).slice(0, compilation.outputOptions.hashDigestLength);
523
+ return compilation.getPathWithInfo(
524
+ filename,
525
+ {
526
+ contentHash,
527
+ chunk: {
528
+ hash: contentHash,
529
+ contentHash
530
+ }
531
+ }
532
+ );
533
+ }
534
+
513
535
  /**
514
536
  * Helper to sort chunks
515
537
  * @param {string[]} entryNames
@@ -75,6 +75,7 @@ class HtmlWebpackChildCompiler {
75
75
  const webpack = mainCompilation.compiler.webpack;
76
76
  const Compilation = webpack.Compilation;
77
77
 
78
+ const NodeTemplatePlugin = webpack.node.NodeTemplatePlugin;
78
79
  const NodeTargetPlugin = webpack.node.NodeTargetPlugin;
79
80
  const LoaderTargetPlugin = webpack.LoaderTargetPlugin;
80
81
  const EntryPlugin = webpack.EntryPlugin;
@@ -103,6 +104,7 @@ class HtmlWebpackChildCompiler {
103
104
  const childCompiler = mainCompilation.createChildCompiler(compilerName, outputOptions, [
104
105
  // Compile the template to nodejs javascript
105
106
  new NodeTargetPlugin(),
107
+ new NodeTemplatePlugin(),
106
108
  new LoaderTargetPlugin('node'),
107
109
  new webpack.library.EnableLibraryPlugin('var')
108
110
  ]);
@@ -114,10 +116,18 @@ class HtmlWebpackChildCompiler {
114
116
 
115
117
  // Add all templates
116
118
  this.templates.forEach((template, index) => {
117
- new EntryPlugin(childCompiler.context, 'data:text/javascript,__webpack_public_path__ = htmlWebpackPluginPublicPath;', `HtmlWebpackPlugin_${index}-${this.id}`).apply(childCompiler);
119
+ new EntryPlugin(childCompiler.context, 'data:text/javascript,__webpack_public_path__ = __webpack_base_uri__ = htmlWebpackPluginPublicPath;', `HtmlWebpackPlugin_${index}-${this.id}`).apply(childCompiler);
118
120
  new EntryPlugin(childCompiler.context, template, `HtmlWebpackPlugin_${index}-${this.id}`).apply(childCompiler);
119
121
  });
120
122
 
123
+ // The templates are compiled and executed by NodeJS - similar to server side rendering
124
+ // Unfortunately this causes issues as some loaders require an absolute URL to support ES Modules
125
+ // The following config enables relative URL support for the child compiler
126
+ childCompiler.options.module = { ...childCompiler.options.module };
127
+ childCompiler.options.module.parser = { ...childCompiler.options.module.parser };
128
+ childCompiler.options.module.parser.javascript = { ...childCompiler.options.module.parser.javascript,
129
+ url: 'relative' };
130
+
121
131
  this.compilationStartedTimestamp = new Date().getTime();
122
132
  this.compilationPromise = new Promise((resolve, reject) => {
123
133
  const extractedAssets = [];
package/lib/loader.js CHANGED
@@ -2,20 +2,26 @@
2
2
  // @ts-nocheck
3
3
  'use strict';
4
4
  const _ = require('lodash');
5
- const loaderUtils = require('loader-utils');
6
5
 
7
6
  module.exports = function (source) {
8
7
  // Get templating options
9
- const options = this.query !== '' ? loaderUtils.getOptions(this) : {};
8
+ const options = this.getOptions();
10
9
  const force = options.force || false;
11
10
 
12
- const allLoadersButThisOne = this.loaders.filter(function (loader) {
13
- return loader.normal !== module.exports;
14
- });
11
+ const allLoadersButThisOne = this.loaders.filter((loader) => loader.normal !== module.exports);
12
+
15
13
  // This loader shouldn't kick in if there is any other loader (unless it's explicitly enforced)
16
14
  if (allLoadersButThisOne.length > 0 && !force) {
17
15
  return source;
18
16
  }
17
+
18
+ // Allow only one html-webpack-plugin loader to allow loader options in the webpack config
19
+ const htmlWebpackPluginLoaders = this.loaders.filter((loader) => loader.normal === module.exports);
20
+ const lastHtmlWebpackPluginLoader = htmlWebpackPluginLoaders[htmlWebpackPluginLoaders.length - 1];
21
+ if (this.loaders[this.loaderIndex] !== lastHtmlWebpackPluginLoader) {
22
+ return source;
23
+ }
24
+
19
25
  // Skip .js files (unless it's explicitly enforced)
20
26
  if (/\.js$/.test(this.resourcePath) && !force) {
21
27
  return source;
@@ -23,7 +29,7 @@ module.exports = function (source) {
23
29
 
24
30
  // The following part renders the template with lodash as a minimalistic loader
25
31
  //
26
- const template = _.template(source, _.defaults(options, { interpolate: /<%=([\s\S]+?)%>/g, variable: 'data' }));
32
+ const template = _.template(source, { interpolate: /<%=([\s\S]+?)%>/g, variable: 'data', ...options });
27
33
  // Use __non_webpack_require__ to enforce using the native nodejs require
28
34
  // during template execution
29
35
  return 'var _ = __non_webpack_require__(' + JSON.stringify(require.resolve('lodash')) + ');' +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "html-webpack-plugin",
3
- "version": "5.1.0",
3
+ "version": "5.3.2",
4
4
  "license": "MIT",
5
5
  "description": "Simplifies creation of HTML files to serve your webpack bundles",
6
6
  "author": "Jan Nicklas <j.nicklas@me.com> (https://github.com/jantimon)",
@@ -28,34 +28,31 @@
28
28
  ]
29
29
  },
30
30
  "devDependencies": {
31
- "@types/loader-utils": "2.0.1",
32
31
  "@types/node": "11.13.9",
33
- "commitizen": "4.2.1",
32
+ "commitizen": "^4.2.4",
34
33
  "css-loader": "5.0.1",
35
34
  "cz-conventional-changelog": "2.1.0",
36
35
  "dir-compare": "1.7.2",
37
- "file-loader": "6.2.0",
38
- "html-loader": "1.3.2",
36
+ "html-loader": "2.1.1",
39
37
  "jest": "26.5.3",
40
- "mini-css-extract-plugin": "1.0.0",
41
- "pug": "2.0.3",
38
+ "mini-css-extract-plugin": "^1.6.0",
39
+ "pug": "3.0.2",
42
40
  "pug-loader": "2.4.0",
43
41
  "raw-loader": "4.0.2",
44
42
  "rimraf": "2.6.3",
45
43
  "semistandard": "^13.0.1",
46
- "standard-version": "9.1.0",
44
+ "standard-version": "^9.3.0",
47
45
  "style-loader": "2.0.0",
48
46
  "typescript": "4.1.3",
49
- "webpack": "^5.20.0",
50
- "webpack-recompilation-simulator": "3.2.0",
51
- "webpack-cli": "4.2.0"
47
+ "webpack": "5.24.3",
48
+ "webpack-cli": "4.5.0",
49
+ "webpack-recompilation-simulator": "3.2.0"
52
50
  },
53
51
  "dependencies": {
54
52
  "@types/html-minifier-terser": "^5.0.0",
55
53
  "html-minifier-terser": "^5.0.1",
56
- "loader-utils": "^2.0.0",
57
- "lodash": "^4.17.20",
58
- "pretty-error": "^2.1.1",
54
+ "lodash": "^4.17.21",
55
+ "pretty-error": "^3.0.4",
59
56
  "tapable": "^2.0.0"
60
57
  },
61
58
  "peerDependencies": {
package/typings.d.ts CHANGED
@@ -94,7 +94,7 @@ declare namespace HtmlWebpackPlugin {
94
94
  * blocking will result in <script src="..."></script>
95
95
  * defer will result in <script defer src="..."></script>
96
96
  *
97
- * @default 'blocking'
97
+ * @default 'defer'
98
98
  */
99
99
  scriptLoading?: "blocking" | "defer";
100
100
  /**