webpack 5.107.0 → 5.107.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.
Files changed (64) hide show
  1. package/lib/BannerPlugin.js +3 -4
  2. package/lib/Chunk.js +21 -25
  3. package/lib/ChunkGroup.js +57 -15
  4. package/lib/Compilation.js +33 -11
  5. package/lib/Compiler.js +27 -3
  6. package/lib/ContextModuleFactory.js +45 -38
  7. package/lib/EvalSourceMapDevToolPlugin.js +0 -1
  8. package/lib/ExportsInfo.js +30 -34
  9. package/lib/ExternalModule.js +15 -11
  10. package/lib/ExternalModuleFactoryPlugin.js +2 -1
  11. package/lib/Module.js +1 -1
  12. package/lib/ModuleNotFoundError.js +10 -0
  13. package/lib/ModuleSourceTypeConstants.js +24 -22
  14. package/lib/MultiCompiler.js +14 -0
  15. package/lib/NormalModule.js +531 -53
  16. package/lib/NormalModuleFactory.js +38 -26
  17. package/lib/ProgressPlugin.js +1 -1
  18. package/lib/RuntimePlugin.js +1 -1
  19. package/lib/SourceMapDevToolPlugin.js +335 -57
  20. package/lib/Template.js +1 -1
  21. package/lib/TemplatedPathPlugin.js +22 -4
  22. package/lib/asset/AssetBytesGenerator.js +6 -6
  23. package/lib/asset/AssetGenerator.js +14 -14
  24. package/lib/asset/AssetModulesPlugin.js +3 -7
  25. package/lib/asset/AssetSourceGenerator.js +6 -6
  26. package/lib/buildChunkGraph.js +24 -2
  27. package/lib/cache/getLazyHashedEtag.js +9 -2
  28. package/lib/css/CssModulesPlugin.js +2 -2
  29. package/lib/dependencies/CommonJsImportsParserPlugin.js +108 -1
  30. package/lib/dependencies/CssUrlDependency.js +3 -2
  31. package/lib/dependencies/HarmonyDetectionParserPlugin.js +21 -1
  32. package/lib/dependencies/HarmonyExportInitFragment.js +8 -9
  33. package/lib/dependencies/HtmlInlineScriptDependency.js +3 -14
  34. package/lib/dependencies/HtmlInlineStyleDependency.js +17 -0
  35. package/lib/dependencies/HtmlScriptSrcDependency.js +265 -65
  36. package/lib/dependencies/HtmlSourceDependency.js +21 -2
  37. package/lib/dependencies/WorkerPlugin.js +18 -4
  38. package/lib/hmr/LazyCompilationPlugin.js +104 -0
  39. package/lib/html/HtmlGenerator.js +81 -33
  40. package/lib/html/HtmlModulesPlugin.js +87 -28
  41. package/lib/html/walkHtmlTokens.js +641 -125
  42. package/lib/index.js +2 -0
  43. package/lib/javascript/JavascriptModulesPlugin.js +2 -2
  44. package/lib/javascript/JavascriptParser.js +1 -1
  45. package/lib/library/ModuleLibraryPlugin.js +30 -24
  46. package/lib/node/NodeWatchFileSystem.js +37 -22
  47. package/lib/optimize/ConcatenatedModule.js +3 -2
  48. package/lib/optimize/SideEffectsFlagPlugin.js +1 -2
  49. package/lib/optimize/SplitChunksPlugin.js +4 -4
  50. package/lib/runtime/AutoPublicPathRuntimeModule.js +3 -3
  51. package/lib/runtime/GetChunkFilenameRuntimeModule.js +5 -5
  52. package/lib/sharing/ConsumeSharedPlugin.js +2 -8
  53. package/lib/sharing/ProvideSharedPlugin.js +4 -4
  54. package/lib/util/fs.js +6 -1
  55. package/lib/wasm-async/AsyncWebAssemblyModulesPlugin.js +1 -2
  56. package/package.json +5 -5
  57. package/schemas/WebpackOptions.check.js +1 -1
  58. package/schemas/WebpackOptions.json +11 -9
  59. package/schemas/plugins/container/ContainerReferencePlugin.check.js +1 -1
  60. package/schemas/plugins/container/ContainerReferencePlugin.json +1 -0
  61. package/schemas/plugins/container/ExternalsType.check.js +1 -1
  62. package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
  63. package/schemas/plugins/container/ModuleFederationPlugin.json +1 -0
  64. package/types.d.ts +472 -149
@@ -17,6 +17,8 @@ const CssUrlDependency = require("../dependencies/CssUrlDependency");
17
17
 
18
18
  /** @typedef {import("webpack-sources").Source} Source */
19
19
  /** @typedef {import("../../declarations/WebpackOptions").HtmlGeneratorOptions} HtmlGeneratorOptions */
20
+ /** @typedef {import("../Chunk")} Chunk */
21
+ /** @typedef {import("../Compilation")} Compilation */
20
22
  /** @typedef {import("../Compilation").DependencyConstructor} DependencyConstructor */
21
23
  /** @typedef {import("../CodeGenerationResults")} CodeGenerationResults */
22
24
  /** @typedef {import("../Dependency")} Dependency */
@@ -39,7 +41,76 @@ const CssUrlDependency = require("../dependencies/CssUrlDependency");
39
41
  */
40
42
  const JAVASCRIPT_AND_HTML_TYPES = new Set([JAVASCRIPT_TYPE, HTML_TYPE]);
41
43
 
44
+ /** @type {WeakMap<Compilation, Map<string, Chunk>>} */
45
+ const chunksByIdCache = new WeakMap();
46
+
42
47
  class HtmlGenerator extends Generator {
48
+ /**
49
+ * Emit a sentinel for a chunk URL that can't be resolved at code-gen time
50
+ * (chunk hashes aren't computed yet); `resolveChunkUrlSentinels` swaps it
51
+ * for `${PUBLIC_PATH_AUTO}<chunkFilename>` once they are.
52
+ * @param {Chunk} chunk chunk
53
+ * @param {"javascript" | "css"} contentHashType which chunk content hash slot the resolved URL should reference
54
+ * @returns {string} sentinel
55
+ */
56
+ static makeChunkUrlSentinel(chunk, contentHashType) {
57
+ const hexId = Buffer.from(String(chunk.id), "utf8").toString("hex");
58
+ return `__WEBPACK_HTML_CHUNK_URL__${hexId}__${contentHashType}__END__`;
59
+ }
60
+
61
+ /**
62
+ * Replace every `makeChunkUrlSentinel` sentinel in `content` with
63
+ * `${PUBLIC_PATH_AUTO}<chunkFilename>`. Must run after
64
+ * `Compilation#createHash()` so `[contenthash]` resolves.
65
+ * @param {string} content content
66
+ * @param {Compilation} compilation compilation
67
+ * @returns {string} resolved content
68
+ */
69
+ static resolveChunkUrlSentinels(content, compilation) {
70
+ if (!content.includes("__WEBPACK_HTML_CHUNK_URL__")) return content;
71
+ const outputOptions = compilation.outputOptions;
72
+ let chunksById = chunksByIdCache.get(compilation);
73
+ if (chunksById === undefined) {
74
+ chunksById = new Map();
75
+ for (const chunk of compilation.chunks) {
76
+ chunksById.set(String(chunk.id), chunk);
77
+ }
78
+ chunksByIdCache.set(compilation, chunksById);
79
+ }
80
+ return content.replace(
81
+ /__WEBPACK_HTML_CHUNK_URL__([0-9a-f]+)__([a-z]+)__END__/g,
82
+ (_, hexId, contentHashType) => {
83
+ const chunkId = Buffer.from(hexId, "hex").toString("utf8");
84
+ const chunk = chunksById.get(chunkId);
85
+ if (!chunk) return "data:,";
86
+ let filenameTemplate;
87
+ if (contentHashType === "css") {
88
+ const CssModulesPlugin = require("../css/CssModulesPlugin");
89
+
90
+ filenameTemplate = CssModulesPlugin.getChunkFilenameTemplate(
91
+ chunk,
92
+ outputOptions
93
+ );
94
+ } else {
95
+ filenameTemplate =
96
+ chunk.filenameTemplate ||
97
+ (chunk.canBeInitial()
98
+ ? outputOptions.filename
99
+ : outputOptions.chunkFilename);
100
+ }
101
+ const filename = compilation.getPath(
102
+ /** @type {import("../TemplatedPathPlugin").TemplatePath} */
103
+ (filenameTemplate),
104
+ {
105
+ chunk,
106
+ contentHashType
107
+ }
108
+ );
109
+ return `${CssUrlDependency.PUBLIC_PATH_AUTO}${filename}`;
110
+ }
111
+ );
112
+ }
113
+
43
114
  /**
44
115
  * Creates an instance of HtmlGenerator.
45
116
  * @param {HtmlGeneratorOptions=} options generator options
@@ -268,28 +339,15 @@ class HtmlGenerator extends Generator {
268
339
  this.sourceModule(module, initFragments, source, generateContext);
269
340
 
270
341
  if (undoPath === undefined) {
342
+ // HTML output — leave sentinels and `[webpack/auto]` for renderManifest.
271
343
  return /** @type {string} */ (source.source());
272
344
  }
273
345
 
274
- const moduleSourceContent = source.source();
275
- const generatedSource = new ReplaceSource(source);
276
-
277
- const autoPlaceholder = CssUrlDependency.PUBLIC_PATH_AUTO;
278
- const autoPlaceholderLen = autoPlaceholder.length;
279
- for (
280
- let idx = moduleSourceContent.indexOf(autoPlaceholder);
281
- idx !== -1;
282
- idx = moduleSourceContent.indexOf(
283
- autoPlaceholder,
284
- idx + autoPlaceholderLen
285
- )
286
- ) {
287
- generatedSource.replace(idx, idx + autoPlaceholderLen - 1, undoPath);
288
- }
289
-
290
- // TODO handle `[fullhash]`
291
-
292
- return /** @type {string} */ (generatedSource.source());
346
+ // JS-export path — resolve `[webpack/auto]` inline; chunk-URL sentinels
347
+ // stay for `HtmlModulesPlugin`'s `JavascriptModulesPlugin.render` tap.
348
+ let content = /** @type {string} */ (source.source());
349
+ content = content.split(CssUrlDependency.PUBLIC_PATH_AUTO).join(undoPath);
350
+ return content;
293
351
  }
294
352
 
295
353
  /**
@@ -306,16 +364,13 @@ class HtmlGenerator extends Generator {
306
364
  }
307
365
 
308
366
  if (generateContext.type === HTML_TYPE) {
309
- // Preserve `[webpack/auto]` placeholders here the plugin's
310
- // `renderManifest` hook knows the final `.html` filename and
311
- // resolves them to an undo path relative to that location.
367
+ // Preserve `[webpack/auto]`; renderManifest resolves it once `.html` filename is known.
312
368
  return new RawSource(
313
369
  this._renderHtml(module, generateContext, undefined)
314
370
  );
315
371
  }
316
372
 
317
- // JS export: the rewritten HTML is a string the consumer reads at
318
- // runtime, so resolve placeholders to root-relative URLs.
373
+ // JS export: resolve `[webpack/auto]` to root-relative URLs.
319
374
  const generated = this._renderHtml(module, generateContext, "");
320
375
 
321
376
  /** @type {string} */
@@ -346,10 +401,7 @@ class HtmlGenerator extends Generator {
346
401
  */
347
402
  generateError(error, module, generateContext) {
348
403
  if (generateContext.type === HTML_TYPE) {
349
- // The error message can contain arbitrary text (file paths, user
350
- // input, dep request strings). Strip `<`, `>`, and `--` runs so a
351
- // crafted message can't close the comment with `-->` (or open a
352
- // fake nested comment) and inject HTML into the extracted page.
404
+ // Strip `<`, `>`, `--` runs from `error.message` so it can't escape the comment.
353
405
  const safe = String(error.message)
354
406
  .replace(/[<>]/g, "")
355
407
  .replace(/-{2,}/g, (m) => `${"-".repeat(m.length - 1)} `);
@@ -365,11 +417,7 @@ class HtmlGenerator extends Generator {
365
417
  */
366
418
  updateHash(hash, updateHashContext) {
367
419
  hash.update("html");
368
- // Hash the *effective* extraction state, not just the raw option,
369
- // so the module hash flips when a module becomes (or stops being)
370
- // a compilation entry under the `extract: undefined` default — the
371
- // generator's source-type set changes with it, so any cached
372
- // HTML-type codegen result must be invalidated.
420
+ // Hash effective extraction state source-type set changes when this flips.
373
421
  if (this._shouldExtract(updateHashContext.module)) {
374
422
  hash.update("extract");
375
423
  }
@@ -45,12 +45,43 @@ const generatorValidationOptions = {
45
45
  };
46
46
 
47
47
  class HtmlModulesPlugin {
48
+ /**
49
+ * `output.hashFunction`/`hashSalt`/`hashDigest`/`hashDigestLength`
50
+ * digest of `content`, with `nonNumericOnlyHash` applied — webpack's
51
+ * standard `[contenthash]` recipe.
52
+ * @param {string | Buffer} content content to hash
53
+ * @param {import("../../declarations/WebpackOptions").Output} outputOptions output options
54
+ * @returns {string} content hash
55
+ */
56
+ static computeContentHash(content, outputOptions) {
57
+ const createHash = require("../util/createHash");
58
+ const nonNumericOnlyHash = require("../util/nonNumericOnlyHash");
59
+
60
+ const hash = createHash(
61
+ /** @type {import("../../declarations/WebpackOptions").HashFunction} */
62
+ (outputOptions.hashFunction)
63
+ );
64
+ if (outputOptions.hashSalt) hash.update(outputOptions.hashSalt);
65
+ hash.update(content);
66
+ return nonNumericOnlyHash(
67
+ /** @type {string} */ (
68
+ hash.digest(/** @type {string} */ (outputOptions.hashDigest))
69
+ ),
70
+ /** @type {number} */ (outputOptions.hashDigestLength)
71
+ );
72
+ }
73
+
48
74
  /**
49
75
  * Applies the plugin by registering its hooks on the compiler.
50
76
  * @param {Compiler} compiler the compiler instance
51
77
  * @returns {void}
52
78
  */
53
79
  apply(compiler) {
80
+ // Per-chunk `RawSource` reused across builds when bytes are unchanged:
81
+ // keeping identity stable avoids invalidating `RealContentHashPlugin|analyse`.
82
+ /** @type {Map<string, { content: string, source: import("webpack-sources").RawSource }>} */
83
+ const sentinelResolvedSourceCache = new Map();
84
+
54
85
  // `<script src>` and `<link rel="modulepreload">` references collected
55
86
  // by HtmlParser become real compilation entries here. The classic
56
87
  // and esm-script groups are chained via a leader-only dependOn so
@@ -255,11 +286,7 @@ class HtmlModulesPlugin {
255
286
  options
256
287
  )
257
288
  );
258
- return new HtmlGenerator(
259
- /** @type {import("../../declarations/WebpackOptions").HtmlGeneratorOptions} */
260
- (generatorOptions),
261
- compilation.moduleGraph
262
- );
289
+ return new HtmlGenerator(generatorOptions, compilation.moduleGraph);
263
290
  });
264
291
 
265
292
  NormalModule.getCompilationHooks(compilation).processResult.tap(
@@ -300,8 +327,6 @@ class HtmlModulesPlugin {
300
327
  getUndoPath,
301
328
  makePathsRelative
302
329
  } = require("../util/identifier");
303
- const createHash = require("../util/createHash");
304
- const nonNumericOnlyHash = require("../util/nonNumericOnlyHash");
305
330
  const CssUrlDependency = require("../dependencies/CssUrlDependency");
306
331
 
307
332
  const autoPlaceholder = CssUrlDependency.PUBLIC_PATH_AUTO;
@@ -346,17 +371,15 @@ class HtmlModulesPlugin {
346
371
  const placeholderContent = /** @type {string} */ (
347
372
  placeholderSource.source()
348
373
  );
349
- const hashInput = createHash(outputOptions.hashFunction);
350
- if (outputOptions.hashSalt) {
351
- hashInput.update(outputOptions.hashSalt);
352
- }
353
- hashInput.update(placeholderContent);
354
- const fullContentHash = /** @type {string} */ (
355
- hashInput.digest(outputOptions.hashDigest)
374
+ // Resolve sentinels *before* hashing so the HTML's `[contenthash]`
375
+ // invalidates with the referenced chunks' filenames.
376
+ const resolvedContent = HtmlGenerator.resolveChunkUrlSentinels(
377
+ placeholderContent,
378
+ compilation
356
379
  );
357
- const contentHash = nonNumericOnlyHash(
358
- fullContentHash,
359
- outputOptions.hashDigestLength
380
+ const contentHash = HtmlModulesPlugin.computeContentHash(
381
+ resolvedContent,
382
+ outputOptions
360
383
  );
361
384
 
362
385
  const { path: filename, info } = compilation.getPathWithInfo(
@@ -386,7 +409,7 @@ class HtmlModulesPlugin {
386
409
  /** @type {string} */ (outputOptions.path),
387
410
  false
388
411
  );
389
- const finalContent = placeholderContent
412
+ const finalContent = resolvedContent
390
413
  .split(autoPlaceholder)
391
414
  .join(undoPath);
392
415
  const finalSource = new RawSource(finalContent);
@@ -399,16 +422,9 @@ class HtmlModulesPlugin {
399
422
  // the post-undo-path content in the hash, so the
400
423
  // asset cache can't reuse one variant's bytes at
401
424
  // another variant's URL.
402
- const finalHash = createHash(outputOptions.hashFunction);
403
- if (outputOptions.hashSalt) {
404
- finalHash.update(outputOptions.hashSalt);
405
- }
406
- finalHash.update(finalContent);
407
- const finalContentHash = nonNumericOnlyHash(
408
- /** @type {string} */ (
409
- finalHash.digest(outputOptions.hashDigest)
410
- ),
411
- outputOptions.hashDigestLength
425
+ const finalContentHash = HtmlModulesPlugin.computeContentHash(
426
+ finalContent,
427
+ outputOptions
412
428
  );
413
429
 
414
430
  result.push({
@@ -425,6 +441,49 @@ class HtmlModulesPlugin {
425
441
  return result;
426
442
  }
427
443
  );
444
+
445
+ // Resolve sentinels at JS chunk render time so later passes
446
+ // (SourceMapDevToolPlugin, size optimizers, RealContentHash) see resolved bytes.
447
+ const JavascriptModulesPlugin = require("../javascript/JavascriptModulesPlugin");
448
+
449
+ const jsHooks =
450
+ JavascriptModulesPlugin.getCompilationHooks(compilation);
451
+ jsHooks.render.tap(PLUGIN_NAME, (source, renderContext) => {
452
+ const raw = source.source();
453
+ if (typeof raw !== "string") return source;
454
+ if (!raw.includes("__WEBPACK_HTML_CHUNK_URL__")) return source;
455
+ const resolved = HtmlGenerator.resolveChunkUrlSentinels(
456
+ raw,
457
+ compilation
458
+ )
459
+ .split(autoPlaceholder)
460
+ .join("");
461
+ if (resolved === raw) return source;
462
+ const chunkId = String(renderContext.chunk.id);
463
+ const prior = sentinelResolvedSourceCache.get(chunkId);
464
+ if (prior !== undefined && prior.content === resolved) {
465
+ return prior.source;
466
+ }
467
+ const newSource = new RawSource(resolved);
468
+ sentinelResolvedSourceCache.set(chunkId, {
469
+ content: resolved,
470
+ source: newSource
471
+ });
472
+ return newSource;
473
+ });
474
+
475
+ // Prune cache entries for chunks no longer in the graph so a
476
+ // long watch session can't accumulate stale entries.
477
+ compilation.hooks.afterSeal.tap(PLUGIN_NAME, () => {
478
+ if (sentinelResolvedSourceCache.size === 0) return;
479
+ const live = new Set();
480
+ for (const chunk of compilation.chunks) {
481
+ live.add(String(chunk.id));
482
+ }
483
+ for (const id of sentinelResolvedSourceCache.keys()) {
484
+ if (!live.has(id)) sentinelResolvedSourceCache.delete(id);
485
+ }
486
+ });
428
487
  }
429
488
  );
430
489
  }