@depup/webpack 5.110.1-depup.0 → 5.110.3-depup.0

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 (70) hide show
  1. package/README.md +4 -4
  2. package/changes.json +3 -3
  3. package/lib/ChunkGraph.js +20 -18
  4. package/lib/Compilation.js +1 -1
  5. package/lib/ConcatenationScope.js +25 -35
  6. package/lib/DotenvPlugin.js +41 -13
  7. package/lib/ExportsInfo.js +10 -0
  8. package/lib/FlagDependencyUsagePlugin.js +8 -2
  9. package/lib/NormalModule.js +13 -20
  10. package/lib/RuntimePlugin.js +3 -0
  11. package/lib/RuntimeTemplate.js +437 -432
  12. package/lib/async-modules/AsyncModuleHelpers.js +10 -4
  13. package/lib/config/defaults.js +26 -16
  14. package/lib/config/normalization.js +18 -8
  15. package/lib/css/CssLoadingRuntimeModule.js +32 -13
  16. package/lib/css/CssModulesPlugin.js +9 -17
  17. package/lib/css/data.js +13 -3
  18. package/lib/css/syntax.js +78 -10
  19. package/lib/dependencies/CommonJsRequireDependency.js +18 -4
  20. package/lib/dependencies/ContextDependencyTemplateAsRequireCall.js +20 -7
  21. package/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js +10 -4
  22. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +2 -0
  23. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +143 -17
  24. package/lib/dependencies/HarmonyImportDependency.js +94 -41
  25. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +93 -14
  26. package/lib/dependencies/HarmonyImportSideEffectDependency.js +114 -0
  27. package/lib/dependencies/HtmlEntryDependency.js +12 -2
  28. package/lib/dependencies/ImportContextDependency.js +43 -3
  29. package/lib/dependencies/ImportDependency.js +8 -29
  30. package/lib/dependencies/ImportParserPlugin.js +24 -14
  31. package/lib/dependencies/RequireHeaderDependency.js +20 -21
  32. package/lib/dependencies/WorkerAndWorkletPlugin.js +1 -0
  33. package/lib/dependencies/WorkerDependency.js +6 -12
  34. package/lib/dependencies/WorkletDependency.js +7 -11
  35. package/lib/dependencies/importOptionsCheck.js +47 -0
  36. package/lib/errors/DotenvFileError.js +29 -0
  37. package/lib/errors/ImportedBindingAssignmentError.js +30 -0
  38. package/lib/esm/ModuleChunkLoadingPlugin.js +6 -9
  39. package/lib/esm/ModuleChunkLoadingRuntimeModule.js +7 -1
  40. package/lib/html/HtmlModulesPlugin.js +84 -38
  41. package/lib/html/syntax.js +417 -169
  42. package/lib/javascript/JavascriptModulesPlugin.js +5 -10
  43. package/lib/loaders/LoaderRunner.js +33 -5
  44. package/lib/node/ReadFileCompileAsyncWasmPlugin.js +8 -15
  45. package/lib/optimize/ConcatenatedModule.js +46 -7
  46. package/lib/optimize/ConstExportsPlugin.js +5 -0
  47. package/lib/optimize/InlineExports.js +121 -0
  48. package/lib/optimize/RealContentHashPlugin.js +111 -35
  49. package/lib/prefetch/ResourceHintPlugin.js +12 -13
  50. package/lib/runtime/AsyncModuleRuntimeModule.js +5 -1
  51. package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +11 -1
  52. package/lib/schemes/HttpUriPlugin.js +22 -5
  53. package/lib/serialization/FileMiddleware.js +15 -2
  54. package/lib/typescript/TypeScriptPlugin.js +8 -10
  55. package/lib/util/ArrayQueue.js +3 -3
  56. package/lib/util/SourceProcessor.js +24 -8
  57. package/lib/util/URLAbsoluteSpecifier.js +3 -1
  58. package/lib/util/conventions.js +5 -1
  59. package/lib/util/createMappings.js +25 -3
  60. package/lib/util/internalSerializables.js +2 -0
  61. package/lib/util/removeBOM.js +82 -1
  62. package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +5 -3
  63. package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +5 -3
  64. package/lib/wasm-async/UniversalCompileAsyncWasmPlugin.js +7 -12
  65. package/lib/web/FetchCompileAsyncWasmPlugin.js +10 -6
  66. package/lib/web/FetchCompileWasmPlugin.js +4 -2
  67. package/package.json +7 -7
  68. package/schemas/WebpackOptions.check.js +1 -1
  69. package/schemas/WebpackOptions.json +9 -10
  70. package/types.d.ts +95 -27
@@ -55,6 +55,7 @@ const {
55
55
  const getCssModulesPlugin = memoize(() => require("./css/CssModulesPlugin"));
56
56
  // `Compilation` requires this module, so its stage constants are read lazily.
57
57
  const getCompilation = memoize(() => require("./Compilation"));
58
+ const getExternalModule = memoize(() => require("./ExternalModule"));
58
59
 
59
60
  /**
60
61
  * @typedef {object} ChunkAssetNaming
@@ -178,6 +179,20 @@ const DEFER_BAILOUT =
178
179
  * @property {boolean | null} loaded whether the chunk loader fetched the chunk
179
180
  */
180
181
 
182
+ /**
183
+ * @typedef {object} AnalyzableChunkUrls
184
+ * @property {Map<ChunkId, string>} urls the urls that could be written, by chunk id
185
+ * @property {boolean} complete false when some chunk kept the runtime form, whose
186
+ * ids the consumer then still resolves through the runtime name lookup
187
+ */
188
+
189
+ /**
190
+ * @typedef {object} WasmGroups
191
+ * @property {(key: string) => string} groupOf the group a runtime key answers with
192
+ * @property {Set<string>} fetching groups an entry of which reads binaries with `fetch`
193
+ * @property {Map<string, boolean>} onlyFetching groups every entry of which fetches
194
+ */
195
+
181
196
  // A reference in no chunk at all: nothing is known about where it is read from.
182
197
  /** @type {Placement} */
183
198
  const NO_PLACEMENT = { undo: null, loaded: null };
@@ -242,6 +257,10 @@ const SPECIFIER_PART_KINDS = new Set([
242
257
  // Only these carry a chunk id, which is the one part value that may be a number.
243
258
  const CHUNK_SPECIFIER_PART_KINDS = new Set(["chunk", "cssChunk"]);
244
259
 
260
+ // Only these are spelled out of the compilation hash, so a stand-in holding one moves
261
+ // the chunk it lands in into the round after that hash settles.
262
+ const FULL_HASH_PART_KINDS = new Set(["publicPath", "template", "unserved"]);
263
+
245
264
  // What the deferred pass scans for — the other half of what the class spells.
246
265
  const ANALYZABLE_TOKEN_REGEXP = /\.\/@@webpackAnalyzableChunk:([\w-]+)@@/g;
247
266
 
@@ -331,6 +350,25 @@ const createHashProbeChunk = (chunk, hash, present) => {
331
350
  return probeChunk;
332
351
  };
333
352
 
353
+ /**
354
+ * A filename function's answer to the stand-in hash, which says what shape the name
355
+ * has before any real hash exists.
356
+ * @param {Exclude<ChunkFilenameTemplate, string>} template the filename function
357
+ * @param {Chunk} chunk the chunk being referenced
358
+ * @param {string} contentHashType which of the chunk's hashes the name reads
359
+ * @param {string} hash the stand-in hash
360
+ * @param {boolean} present whether the chunk is claimed to carry content hashes
361
+ * @returns {string} what the function names it
362
+ */
363
+ const probeTemplateName = (template, chunk, contentHashType, hash, present) =>
364
+ template({
365
+ chunk: createHashProbeChunk(chunk, hash, present),
366
+ runtime: chunk.runtime,
367
+ contentHashType,
368
+ hash,
369
+ contentHash: hash
370
+ });
371
+
334
372
  /**
335
373
  * No module id error message.
336
374
  * @param {Module} module the module
@@ -385,6 +423,11 @@ function getGlobalObject(definition) {
385
423
  return `Object(${trimmed})`;
386
424
  }
387
425
 
426
+ // An async external is a promise, not a module record with an evaluating state.
427
+ const isAsyncExternal = (/** @type {Module} */ module) =>
428
+ Boolean(/** @type {BuildMeta} */ (module.buildMeta).async) &&
429
+ module instanceof getExternalModule();
430
+
388
431
  class RuntimeTemplate {
389
432
  /**
390
433
  * Creates an instance of RuntimeTemplate.
@@ -426,14 +469,10 @@ class RuntimeTemplate {
426
469
  this._placementByChunk = new WeakMap();
427
470
  /** @type {Map<string, string | null | undefined>} */
428
471
  this._entryBaseUriByRuntime = new Map();
429
- /** @type {Map<string, string> | undefined} */
430
- this._wasmRuntimeGroupsByKey = undefined;
431
- /** @type {Set<string> | false | undefined} */
432
- this._wasmFetchingGroupSet = undefined;
472
+ /** @type {WasmGroups | undefined} */
473
+ this._wasmGroupsValue = undefined;
433
474
  /** @type {Map<string, boolean> | undefined} */
434
475
  this._wasmAnchorKnownByGroup = undefined;
435
- /** @type {Map<string, boolean> | undefined} */
436
- this._wasmGroupOnlyFetchesMap = undefined;
437
476
  }
438
477
 
439
478
  isIIFE() {
@@ -630,28 +669,28 @@ class RuntimeTemplate {
630
669
  placedModule
631
670
  )
632
671
  ) {
633
- this._analyzableBailout(
672
+ return this._analyzableBailout(
634
673
  module,
635
- "__webpack_public_path__ is reassigned in a runtime this module belongs to"
674
+ "__webpack_public_path__ is reassigned in a runtime this module belongs to",
675
+ false
636
676
  );
637
- return false;
638
677
  }
639
678
 
640
679
  if (form === "import") {
641
680
  // Read through a native `import()`, or it is not this feature at all.
642
681
  if (outputOptions.chunkFormat !== "module") {
643
- this._analyzableBailout(
682
+ return this._analyzableBailout(
644
683
  module,
645
- `output.chunkFormat is ${JSON.stringify(outputOptions.chunkFormat)}, so chunks are not read through a native import()`
684
+ `output.chunkFormat is ${JSON.stringify(outputOptions.chunkFormat)}, so chunks are not read through a native import()`,
685
+ false
646
686
  );
647
- return false;
648
687
  }
649
688
  if (outputOptions.importFunctionName !== "import") {
650
- this._analyzableBailout(
689
+ return this._analyzableBailout(
651
690
  module,
652
- `output.importFunctionName is ${JSON.stringify(outputOptions.importFunctionName)}, so the call site is not a native import()`
691
+ `output.importFunctionName is ${JSON.stringify(outputOptions.importFunctionName)}, so the call site is not a native import()`,
692
+ false
653
693
  );
654
- return false;
655
694
  }
656
695
  // A worker loading its own chunks some other way keeps that runtime; one on
657
696
  // `import` uses the same ESM loader as the main graph, so it can be analyzable.
@@ -662,29 +701,23 @@ class RuntimeTemplate {
662
701
  if (!entryOptions || !entryOptions.worker) continue;
663
702
  // `WorkerAndWorkletPlugin` always seeds this from `output.workerChunkLoading`.
664
703
  if (entryOptions.chunkLoading !== "import") {
665
- this._analyzableBailout(
704
+ return this._analyzableBailout(
666
705
  module,
667
- `this worker loads its chunks with ${JSON.stringify(entryOptions.chunkLoading)}, not "import"`
706
+ `this worker loads its chunks with ${JSON.stringify(entryOptions.chunkLoading)}, not "import"`,
707
+ false
668
708
  );
669
- return false;
670
709
  }
671
710
  }
672
711
  return true;
673
712
  }
674
713
 
675
714
  // `url-inline` asks whether the file can be named at the call site at all, and
676
- // its `.p + <file>` fallback spells that without `import.meta` — so neither gate
677
- // below rules it out, and the asset's javascript wrapper stays dropped.
715
+ // its `.p + <file>` fallback spells that without `import.meta` — so the gate
716
+ // below does not rule it out, and the asset's javascript wrapper stays dropped.
717
+ // `environment.module` is deliberately not consulted: ESM output writes
718
+ // `import.meta` regardless (the public path, the chunk loader), so the url forms
719
+ // only match what the rest of the bundle already assumes the target reads.
678
720
  if (form === "url-inline") return true;
679
- // `import.meta` is ESM syntax the target has to read. A chunk `import()` is
680
- // emitted by the module chunk loader either way, so only the url forms check.
681
- if (!this.supportsEcmaScriptModuleSyntax()) {
682
- this._analyzableBailout(
683
- module,
684
- "output.environment.module is false, so the target does not read the import.meta this form needs"
685
- );
686
- return false;
687
- }
688
721
  // `eval` devtool wraps each module in `eval(...)`, where `import.meta` is a
689
722
  // syntax error. Runtime modules are emitted beside them, never wrapped.
690
723
  const { devtool } = this.compilation.options;
@@ -693,22 +726,22 @@ class RuntimeTemplate {
693
726
  typeof devtool === "string" &&
694
727
  devtool.includes("eval")
695
728
  ) {
696
- this._analyzableBailout(
729
+ return this._analyzableBailout(
697
730
  module,
698
- `devtool ${JSON.stringify(devtool)} wraps the module in eval(), where import.meta does not parse`
731
+ `devtool ${JSON.stringify(devtool)} wraps the module in eval(), where import.meta does not parse`,
732
+ false
699
733
  );
700
- return false;
701
734
  }
702
735
  if (form === "url" || form === "url-runtime") return true;
703
736
  // A bare relative URL is what `__webpack_require__.p + path` means only under an
704
737
  // `auto` public path; anything else has to be baked, which is the "wasm" form.
705
738
  if (form === "wasm-relative") {
706
739
  if (outputOptions.publicPath === "auto") return true;
707
- this._analyzableBailout(
740
+ return this._analyzableBailout(
708
741
  module,
709
- "output.publicPath is set, so a bare relative url no longer means what the public path would have resolved to"
742
+ "output.publicPath is set, so a bare relative url no longer means what the public path would have resolved to",
743
+ false
710
744
  );
711
- return false;
712
745
  }
713
746
 
714
747
  // The rest is the `"wasm"` form: whether the whole binary url can be spelled
@@ -727,11 +760,11 @@ class RuntimeTemplate {
727
760
  this._anyWasmChunkFetches(runtime) &&
728
761
  !this._wasmFetchAnchorIsKnown(runtime)
729
762
  ) {
730
- this._analyzableBailout(
763
+ return this._analyzableBailout(
731
764
  module,
732
- "output.publicPath needs a base, and no one path back to the document fits every chunk `fetch` reads it from"
765
+ "output.publicPath needs a base, and no one path back to the document fits every chunk `fetch` reads it from",
766
+ false
733
767
  );
734
- return false;
735
768
  }
736
769
  }
737
770
  // The compilation hash is settled after code generation, so a name carrying one
@@ -753,19 +786,22 @@ class RuntimeTemplate {
753
786
  * the channel `ModuleConcatenationPlugin` already reports through, so it reaches
754
787
  * `stats.optimizationBailout`. Silent unless the build asked for ESM output, where
755
788
  * alone the answer is actionable; deduplicated because a module may write many.
789
+ * Answers with `answer` so a caller states the refusal and its reason at once —
790
+ * `false` where it answers a question, `null` where it was building a specifier.
791
+ * @template {false | null} T
756
792
  * @param {Module | undefined} module the module the reference is emitted into
757
793
  * @param {string} reason why no literal could be baked
758
- * @returns {void}
794
+ * @param {T} answer what the caller hands back
795
+ * @returns {T} that answer
759
796
  */
760
- _analyzableBailout(module, reason) {
761
- if (module === undefined || !this.outputOptions.module) return;
762
- const text = `Analyzable ESM bailout: ${reason}`;
763
- const bailouts =
764
- this.compilation.moduleGraph.getOptimizationBailout(module);
765
- for (const existing of bailouts) {
766
- if (existing === text) return;
797
+ _analyzableBailout(module, reason, answer) {
798
+ if (module !== undefined && this.outputOptions.module) {
799
+ const text = `Analyzable ESM bailout: ${reason}`;
800
+ const bailouts =
801
+ this.compilation.moduleGraph.getOptimizationBailout(module);
802
+ if (!bailouts.includes(text)) bailouts.push(text);
767
803
  }
768
- bailouts.push(text);
804
+ return answer;
769
805
  }
770
806
 
771
807
  /**
@@ -810,8 +846,7 @@ class RuntimeTemplate {
810
846
  */
811
847
  _canDeferOrBail(chunks, module) {
812
848
  if (this._canDeferAnalyzableName(chunks)) return true;
813
- this._analyzableBailout(module, DEFER_BAILOUT);
814
- return false;
849
+ return this._analyzableBailout(module, DEFER_BAILOUT, false);
815
850
  }
816
851
 
817
852
  /**
@@ -861,9 +896,7 @@ class RuntimeTemplate {
861
896
  */
862
897
  _partsBakeFullHash(parts) {
863
898
  for (const [kind, value] of parts) {
864
- if (kind === "publicPath" || kind === "template" || kind === "unserved") {
865
- return true;
866
- }
899
+ if (FULL_HASH_PART_KINDS.has(kind)) return true;
867
900
  if (kind === "literal" && this._hasReservedFullHash(String(value))) {
868
901
  return true;
869
902
  }
@@ -962,95 +995,83 @@ class RuntimeTemplate {
962
995
  const base = this.analyzableUrlReadsBaseUri()
963
996
  ? this.entryBaseUri(runtime)
964
997
  : undefined;
965
- if (base !== undefined) {
966
- if (base === null) {
967
- this._analyzableBailout(
968
- module,
969
- "the entries this module is generated for set different baseUri values, so no one url resolves for all of them"
970
- );
971
- return null;
972
- }
973
- // A relative one is no base of its own: the runtime reads it against the
974
- // chunk, so the literal spells it there rather than resolving against it.
975
- if (isChunkRelativeBaseUri(base)) {
976
- return this._getAnalyzableFileSpecifier(
977
- module,
978
- chunkGraph,
979
- [["literal", filename]],
980
- true,
981
- base
982
- );
983
- }
984
- const chunks = this._moduleChunks(module, chunkGraph);
985
- // A protocol-relative base names a host but no scheme, and the runtime reads it
986
- // against the chunk's own url to get one — the very url this literal is
987
- // resolved against — so it may stay protocol-relative instead of being settled
988
- // here. Nothing walks back to the output root first: the base replaces it.
989
- if (base.startsWith("//")) {
990
- const relative = this._resolvePublicPathPrefix(
991
- publicPath,
992
- module,
993
- chunks
994
- );
995
- return relative === null
996
- ? null
997
- : this._specifierOf(
998
- [
999
- ["literal", base],
1000
- ...rootedParts(relative),
1001
- ["literal", filename]
1002
- ],
1003
- chunks,
1004
- module
1005
- );
1006
- }
1007
- if (!isAbsoluteBaseUri(base)) {
1008
- this._analyzableBailout(
1009
- module,
1010
- `an entry sets a baseUri of ${JSON.stringify(base)}, whose scheme names no base anything can be resolved against`
1011
- );
1012
- return null;
1013
- }
1014
- const parts = this._resolvePublicPathPrefix(publicPath, module, chunks);
1015
- if (parts === null) return null;
1016
- // Resolved here rather than against the output root, and only an absolute base
1017
- // settles it — a relative one has no base of its own to be read against.
1018
- const spelled = literalText(parts);
1019
- // A hash the deferred pass still has to fill in cannot be resolved against the
1020
- // base here: the fill would land inside an already-resolved url, where a hash
1021
- // opening with a letter reads as a scheme and drops the base entirely.
1022
- const text =
1023
- spelled !== null && !this._hasReservedFullHash(spelled)
1024
- ? spelled
1025
- : null;
1026
- /** @type {URL} */
1027
- let resolved;
1028
- try {
1029
- // Empty where only the deferred pass can spell the rest, which still settles
1030
- // whether the base itself is one anything may be resolved against.
1031
- resolved = new URL(text === null ? "" : text + filename, base);
1032
- } catch (_error) {
1033
- this._analyzableBailout(
1034
- module,
1035
- `an entry sets a baseUri of ${JSON.stringify(base)}, which is not absolute, so no url can be resolved against it here`
1036
- );
1037
- return null;
1038
- }
1039
- if (text !== null) return toJsStringLiteral(resolved.href);
1040
- if (!this._canDeferOrBail(chunks, module)) return null;
1041
- return toJsStringLiteral(
1042
- this._reserveAnalyzableSpecifier([
1043
- ["base", base],
1044
- ...parts,
1045
- ["literal", filename]
1046
- ])
998
+ // No base of its own: the name is spelled against the output root, as a chunk is.
999
+ if (base === undefined) {
1000
+ return this._getAnalyzableFileSpecifier(
1001
+ module,
1002
+ chunkGraph,
1003
+ [["literal", filename]],
1004
+ true
1047
1005
  );
1048
1006
  }
1049
- return this._getAnalyzableFileSpecifier(
1050
- module,
1051
- chunkGraph,
1052
- [["literal", filename]],
1053
- true
1007
+ if (base === null) {
1008
+ return this._analyzableBailout(
1009
+ module,
1010
+ "the entries this module is generated for set different baseUri values, so no one url resolves for all of them",
1011
+ null
1012
+ );
1013
+ }
1014
+ // A relative one is no base of its own: the runtime reads it against the
1015
+ // chunk, so the literal spells it there rather than resolving against it.
1016
+ if (isChunkRelativeBaseUri(base)) {
1017
+ return this._getAnalyzableFileSpecifier(
1018
+ module,
1019
+ chunkGraph,
1020
+ [["literal", filename]],
1021
+ true,
1022
+ base
1023
+ );
1024
+ }
1025
+ const chunks = this._moduleChunks(module, chunkGraph);
1026
+ const parts = this._resolvePublicPathPrefix(publicPath, module, chunks);
1027
+ if (parts === null) return null;
1028
+ // A protocol-relative base names a host but no scheme, and the runtime reads it
1029
+ // against the chunk's own url to get one — the very url this literal is
1030
+ // resolved against — so it may stay protocol-relative instead of being settled
1031
+ // here. Nothing walks back to the output root first: the base replaces it.
1032
+ if (base.startsWith("//")) {
1033
+ return this._specifierOf(
1034
+ [["literal", base], ...rootedParts(parts), ["literal", filename]],
1035
+ chunks,
1036
+ module
1037
+ );
1038
+ }
1039
+ if (!isAbsoluteBaseUri(base)) {
1040
+ return this._analyzableBailout(
1041
+ module,
1042
+ `an entry sets a baseUri of ${JSON.stringify(base)}, whose scheme names no base anything can be resolved against`,
1043
+ null
1044
+ );
1045
+ }
1046
+ // Resolved here rather than against the output root, and only an absolute base
1047
+ // settles it — a relative one has no base of its own to be read against.
1048
+ const spelled = literalText(parts);
1049
+ // A hash the deferred pass still has to fill in cannot be resolved against the
1050
+ // base here: the fill would land inside an already-resolved url, where a hash
1051
+ // opening with a letter reads as a scheme and drops the base entirely.
1052
+ const text =
1053
+ spelled !== null && !this._hasReservedFullHash(spelled) ? spelled : null;
1054
+ /** @type {URL} */
1055
+ let resolved;
1056
+ try {
1057
+ // Empty where only the deferred pass can spell the rest, which still settles
1058
+ // whether the base itself is one anything may be resolved against.
1059
+ resolved = new URL(text === null ? "" : text + filename, base);
1060
+ } catch (_error) {
1061
+ return this._analyzableBailout(
1062
+ module,
1063
+ `an entry sets a baseUri of ${JSON.stringify(base)}, which is not absolute, so no url can be resolved against it here`,
1064
+ null
1065
+ );
1066
+ }
1067
+ if (text !== null) return toJsStringLiteral(resolved.href);
1068
+ if (!this._canDeferOrBail(chunks, module)) return null;
1069
+ return toJsStringLiteral(
1070
+ this._reserveAnalyzableSpecifier([
1071
+ ["base", base],
1072
+ ...parts,
1073
+ ["literal", filename]
1074
+ ])
1054
1075
  );
1055
1076
  }
1056
1077
 
@@ -1121,55 +1142,119 @@ class RuntimeTemplate {
1121
1142
  return publicPath.includes("[") ? undefined : publicPath;
1122
1143
  }
1123
1144
 
1145
+ /**
1146
+ * Runtime keys grouped by the binaries they share, with what the entries of each
1147
+ * group asked for. Code generation runs once for runtimes a module hashes alike in,
1148
+ * and nothing in a binary's hash knows which loader will read it — so a binary two
1149
+ * runtimes reach carries one shape into both, and a third runtime sharing another
1150
+ * binary with either is pulled in after them. Only an entry names a loader; every
1151
+ * other chunk of the runtime is served by the one its entry asked for. Asked of the
1152
+ * same modules the loaders are created for, and only once per compilation.
1153
+ * @returns {WasmGroups} the grouping, and which groups fetch
1154
+ */
1155
+ _wasmGroups() {
1156
+ if (this._wasmGroupsValue === undefined) {
1157
+ const { chunkGraph, chunks, modules } = this.compilation;
1158
+ /** @type {Map<string, string>} */
1159
+ const parent = new Map();
1160
+ /**
1161
+ * @param {string} key runtime key
1162
+ * @returns {string} the key naming its group
1163
+ */
1164
+ const find = (key) => {
1165
+ let root = key;
1166
+ let seen = parent.get(root);
1167
+ while (seen !== undefined && seen !== root) {
1168
+ root = seen;
1169
+ seen = parent.get(root);
1170
+ }
1171
+ // Path compression keeps a long share chain from costing more than once.
1172
+ let walk = key;
1173
+ let next = parent.get(walk);
1174
+ while (next !== undefined && next !== root) {
1175
+ parent.set(walk, root);
1176
+ walk = next;
1177
+ next = parent.get(walk);
1178
+ }
1179
+ return root;
1180
+ };
1181
+ /** @type {string[]} */
1182
+ const keys = [];
1183
+ for (const module of modules) {
1184
+ if (module.type !== WEBASSEMBLY_MODULE_TYPE_ASYNC) continue;
1185
+ keys.length = 0;
1186
+ for (const runtime of chunkGraph.getModuleRuntimes(module)) {
1187
+ forEachRuntime(runtime, (key) => {
1188
+ keys.push(/** @type {string} */ (key));
1189
+ });
1190
+ }
1191
+ for (const key of keys) if (!parent.has(key)) parent.set(key, key);
1192
+ // Every runtime this binary reaches answers with the first one.
1193
+ for (let i = 1; i < keys.length; i++) {
1194
+ parent.set(find(keys[i]), find(keys[0]));
1195
+ }
1196
+ }
1197
+ // Flattened once, so a lookup is a single `get` rather than a chain walk.
1198
+ for (const key of parent.keys()) parent.set(key, find(key));
1199
+ /**
1200
+ * @param {string} key a runtime key
1201
+ * @returns {string} the group it answers with, or itself when it shares nothing
1202
+ */
1203
+ const groupOf = (key) => {
1204
+ const group = parent.get(key);
1205
+ // An entry named "" makes "" a runtime key, so test absence, not a falsy value.
1206
+ return group === undefined ? key : group;
1207
+ };
1208
+ /** @type {Set<string>} */
1209
+ const fetching = new Set();
1210
+ /** @type {Map<string, boolean>} */
1211
+ const onlyFetching = new Map();
1212
+ for (const chunk of chunks) {
1213
+ if (!chunk.getEntryOptions()) continue;
1214
+ const fetches = this._chunkWasmLoading(chunk) === "fetch";
1215
+ forEachRuntime(chunk.runtime, (key) => {
1216
+ const group = groupOf(/** @type {string} */ (key));
1217
+ if (fetches) fetching.add(group);
1218
+ onlyFetching.set(group, onlyFetching.get(group) !== false && fetches);
1219
+ });
1220
+ }
1221
+ this._wasmGroupsValue = { groupOf, fetching, onlyFetching };
1222
+ }
1223
+ return this._wasmGroupsValue;
1224
+ }
1225
+
1226
+ /**
1227
+ * @param {RuntimeSpec} runtime the runtime being asked about
1228
+ * @returns {string[]} the wasm runtime groups it reaches
1229
+ */
1230
+ _wasmGroupsOf(runtime) {
1231
+ const { groupOf } = this._wasmGroups();
1232
+ /** @type {string[]} */
1233
+ const reached = [];
1234
+ forEachRuntime(runtime, (key) => {
1235
+ reached.push(groupOf(/** @type {string} */ (key)));
1236
+ });
1237
+ return reached;
1238
+ }
1239
+
1124
1240
  /**
1125
1241
  * Whether a chunk loads WebAssembly through `fetch`, which is the only loader the
1126
1242
  * public path reaches: `readFile` resolves the binary's name against the chunk it is
1127
1243
  * read from, exactly as a baked literal does, so a public path is irrelevant to it.
1128
- * Answered per runtime rather than per chunk on purpose — a module's generated source
1244
+ * Answered per group rather than per chunk on purpose — a module's generated source
1129
1245
  * and the runtime module of every chunk holding it have to agree on the shape, and
1130
- * they ask from opposite ends; one loader serves a whole runtime, so that is the
1131
- * finest scope on which they can. Without a runtime, or where the runtimes cannot be
1132
- * told apart, the answer covers the compilation.
1246
+ * they ask from opposite ends; one loader serves a whole group, so that is the finest
1247
+ * scope on which they can. Without a runtime the answer covers the compilation.
1133
1248
  * @param {RuntimeSpec=} runtime the runtime being asked about
1134
1249
  * @returns {boolean} true when a chunk of that runtime fetches its binaries
1135
1250
  */
1136
1251
  _anyWasmChunkFetches(runtime) {
1137
- const fetching = this._wasmFetchingGroups();
1138
- if (fetching === undefined) return false;
1252
+ const { fetching } = this._wasmGroups();
1253
+ if (fetching.size === 0) return false;
1139
1254
  // Without a runtime to place it in there is nothing to narrow by, so any chunk
1140
1255
  // answers for all of them.
1141
1256
  if (runtime === undefined) return true;
1142
- const groups = this._wasmRuntimeGroups();
1143
- if (typeof runtime === "string") {
1144
- return fetching.has(this._wasmGroupOf(groups, runtime));
1145
- }
1146
- for (const key of runtime) {
1147
- if (fetching.has(this._wasmGroupOf(groups, key))) return true;
1148
- }
1149
- return false;
1150
- }
1151
-
1152
- /**
1153
- * Whether every entry of a wasm runtime group reads its binaries with `fetch`. Only
1154
- * an entry names a loader, so the scan is of entries, as `_wasmFetchingGroups` is.
1155
- * @returns {Map<string, boolean>} whether each group fetches and nothing else
1156
- */
1157
- _wasmGroupOnlyFetches() {
1158
- if (this._wasmGroupOnlyFetchesMap === undefined) {
1159
- const groups = this._wasmRuntimeGroups();
1160
- /** @type {Map<string, boolean>} */
1161
- const only = new Map();
1162
- for (const chunk of this.compilation.chunks) {
1163
- if (!chunk.getEntryOptions()) continue;
1164
- const fetches = this._chunkWasmLoading(chunk) === "fetch";
1165
- forEachRuntime(chunk.runtime, (key) => {
1166
- const group = this._wasmGroupOf(groups, /** @type {string} */ (key));
1167
- only.set(group, only.get(group) !== false && fetches);
1168
- });
1169
- }
1170
- this._wasmGroupOnlyFetchesMap = only;
1171
- }
1172
- return this._wasmGroupOnlyFetchesMap;
1257
+ return this._wasmGroupsOf(runtime).some((group) => fetching.has(group));
1173
1258
  }
1174
1259
 
1175
1260
  /**
@@ -1182,30 +1267,31 @@ class RuntimeTemplate {
1182
1267
  _wasmFetchAnchorIsKnown(runtime) {
1183
1268
  if (this._wasmAnchorKnownByGroup === undefined) {
1184
1269
  const { chunkGraph, modules } = this.compilation;
1185
- const publicPath = /** @type {PublicPath} */ (
1186
- this.outputOptions.publicPath
1187
- );
1188
- const groups = this._wasmRuntimeGroups();
1270
+ const { groupOf, onlyFetching } = this._wasmGroups();
1189
1271
  /** @type {Map<string, boolean>} */
1190
1272
  const known = new Map();
1191
1273
  for (const module of modules) {
1192
1274
  if (module.type !== WEBASSEMBLY_MODULE_TYPE_ASYNC) continue;
1275
+ // Probed through the very builder the generator runs, so the gate is
1276
+ // exact: a prefix it can spell — the per-asset deferrals included — is
1277
+ // one the group may bake. The probe name stands in for the binary's.
1193
1278
  const spellable =
1194
- this._modulePlacement(module, chunkGraph).loaded !== null &&
1195
- this._resolvePublicPathPrefix(publicPath, module) !== null;
1279
+ this._getAnalyzableFileSpecifier(
1280
+ module,
1281
+ /** @type {ChunkGraph} */ (chunkGraph),
1282
+ [["literal", "x"]],
1283
+ true
1284
+ ) !== null;
1196
1285
  for (const moduleRuntime of chunkGraph.getModuleRuntimes(module)) {
1197
1286
  forEachRuntime(moduleRuntime, (key) => {
1198
- const group = this._wasmGroupOf(
1199
- groups,
1200
- /** @type {string} */ (key)
1201
- );
1287
+ const group = groupOf(/** @type {string} */ (key));
1202
1288
  known.set(group, known.get(group) !== false && spellable);
1203
1289
  });
1204
1290
  }
1205
1291
  }
1206
1292
  // A group one entry reads with `readFile` cannot take a prefix meant for the
1207
1293
  // document, and only an entry names its loader.
1208
- for (const [group, only] of this._wasmGroupOnlyFetches()) {
1294
+ for (const [group, only] of onlyFetching) {
1209
1295
  if (!only) known.set(group, false);
1210
1296
  }
1211
1297
  this._wasmAnchorKnownByGroup = known;
@@ -1217,124 +1303,9 @@ class RuntimeTemplate {
1217
1303
  for (const value of known.values()) if (!value) return false;
1218
1304
  return known.size > 0;
1219
1305
  }
1220
- const groups = this._wasmRuntimeGroups();
1221
- if (typeof runtime === "string") {
1222
- return known.get(this._wasmGroupOf(groups, runtime)) === true;
1223
- }
1224
- for (const key of runtime) {
1225
- if (known.get(this._wasmGroupOf(groups, key)) !== true) return false;
1226
- }
1227
- return true;
1228
- }
1229
-
1230
- /**
1231
- * The runtime groups an entry of which asked for the `fetch` loader, or `undefined`
1232
- * where none did — the cheap answer for a compilation with no fetching entry at all,
1233
- * which is every non-web target. Only an entry names a loader; every other chunk of
1234
- * the runtime is served by the one its entry asked for.
1235
- * @returns {Set<string> | undefined} the groups that fetch
1236
- */
1237
- _wasmFetchingGroups() {
1238
- if (this._wasmFetchingGroupSet === undefined) {
1239
- /** @type {Set<string>} */
1240
- const fetching = new Set();
1241
- /** @type {Map<string, string> | undefined} */
1242
- let groups;
1243
- for (const chunk of this.compilation.chunks) {
1244
- // Only an entry names a loader; every other chunk of the runtime is served
1245
- // by the one its entry asked for, so it says nothing on its own.
1246
- if (!chunk.getEntryOptions()) continue;
1247
- if (this._chunkWasmLoading(chunk) !== "fetch") continue;
1248
- // Built only once something fetches, so nothing pays for the grouping.
1249
- if (groups === undefined) groups = this._wasmRuntimeGroups();
1250
- const named = /** @type {Map<string, string>} */ (groups);
1251
- forEachRuntime(chunk.runtime, (key) => {
1252
- fetching.add(this._wasmGroupOf(named, /** @type {string} */ (key)));
1253
- });
1254
- }
1255
- this._wasmFetchingGroupSet = fetching.size === 0 ? false : fetching;
1256
- }
1257
- return this._wasmFetchingGroupSet === false
1258
- ? undefined
1259
- : this._wasmFetchingGroupSet;
1260
- }
1261
-
1262
- /**
1263
- * Runtime keys mapped to the group they answer with, named by one key of it. Code
1264
- * generation runs once for runtimes a module hashes alike in, and nothing in a
1265
- * binary's hash knows which loader will read it — so a binary two runtimes reach
1266
- * carries one shape into both, and a third runtime sharing another binary with
1267
- * either is pulled in after them. A key in no group answers as itself. Asked of the
1268
- * same modules the loaders are created for, and only once per compilation.
1269
- * @returns {Map<string, string>} runtime key to the group it answers with
1270
- */
1271
- _wasmRuntimeGroups() {
1272
- if (this._wasmRuntimeGroupsByKey === undefined) {
1273
- const { chunkGraph } = this.compilation;
1274
- /** @type {Map<string, string>} */
1275
- const parent = new Map();
1276
- /**
1277
- * @param {string} key runtime key
1278
- * @returns {string} the key naming its group
1279
- */
1280
- const find = (key) => {
1281
- let root = key;
1282
- let seen = parent.get(root);
1283
- while (seen !== undefined && seen !== root) {
1284
- root = seen;
1285
- seen = parent.get(root);
1286
- }
1287
- // Path compression keeps a long share chain from costing more than once.
1288
- let walk = key;
1289
- let next = parent.get(walk);
1290
- while (next !== undefined && next !== root) {
1291
- parent.set(walk, root);
1292
- walk = next;
1293
- next = parent.get(walk);
1294
- }
1295
- return root;
1296
- };
1297
- /** @type {string[]} */
1298
- const keys = [];
1299
- /**
1300
- * @param {RuntimeSpec} runtime a runtime the module is generated for
1301
- * @returns {void}
1302
- */
1303
- const collectKeys = (runtime) => {
1304
- forEachRuntime(runtime, (key) => {
1305
- keys.push(/** @type {string} */ (key));
1306
- });
1307
- };
1308
- for (const module of this.compilation.modules) {
1309
- if (module.type !== WEBASSEMBLY_MODULE_TYPE_ASYNC) continue;
1310
- keys.length = 0;
1311
- for (const runtime of chunkGraph.getModuleRuntimes(module)) {
1312
- collectKeys(runtime);
1313
- }
1314
- for (const key of keys) {
1315
- if (!parent.has(key)) parent.set(key, key);
1316
- }
1317
- // Every runtime this binary reaches answers with the first one.
1318
- for (let i = 1; i < keys.length; i++) {
1319
- parent.set(find(keys[i]), find(keys[0]));
1320
- }
1321
- }
1322
- // Flattened once, so a lookup is a single `get` rather than a chain walk.
1323
- for (const key of parent.keys()) parent.set(key, find(key));
1324
- this._wasmRuntimeGroupsByKey = parent;
1325
- }
1326
- return this._wasmRuntimeGroupsByKey;
1327
- }
1328
-
1329
- /**
1330
- * @param {Map<string, string>} groups runtime key to the group it answers with
1331
- * @param {string} key a runtime key
1332
- * @returns {string} the group it answers with, or itself when it shares nothing
1333
- */
1334
- _wasmGroupOf(groups, key) {
1335
- const group = groups.get(key);
1336
- // An entry named "" makes "" a runtime key, so test absence, not a falsy value.
1337
- return group === undefined ? key : group;
1306
+ return this._wasmGroupsOf(runtime).every(
1307
+ (group) => known.get(group) === true
1308
+ );
1338
1309
  }
1339
1310
 
1340
1311
  supportTemplateLiteral() {
@@ -2019,6 +1990,9 @@ class RuntimeTemplate {
2019
1990
  (m) => chunkGraph.getModuleId(m)
2020
1991
  ).filter((id) => id !== null);
2021
1992
  if (asyncDeps.length) {
1993
+ runtimeRequirements.add(
1994
+ RuntimeGlobals.deferredModuleAsyncTransitiveDependencies
1995
+ );
2022
1996
  if (header) {
2023
1997
  appending = `.then(${this.basicFunction(
2024
1998
  "",
@@ -2255,11 +2229,14 @@ class RuntimeTemplate {
2255
2229
  (dependency instanceof HarmonyImportDependency ||
2256
2230
  dependency instanceof ImportDependency) &&
2257
2231
  ImportPhaseUtils.isDefer(dependency.phase) &&
2258
- !(/** @type {BuildMeta} */ (module.buildMeta).async);
2232
+ !isAsyncExternal(module);
2259
2233
 
2260
2234
  if (isModuleDeferred) {
2261
2235
  /** @type {Set<Module>} */
2262
2236
  const outgoingAsyncModules = getOutgoingAsyncModules(moduleGraph, module);
2237
+ // A module deferring itself is already evaluating, so awaiting it here
2238
+ // would deadlock; forcing the namespace throws instead.
2239
+ outgoingAsyncModules.delete(originModule);
2263
2240
 
2264
2241
  importContent = `/* deferred harmony import */ ${optDeclaration}${importVar} = ${getOptimizedDeferredModule(
2265
2242
  moduleId,
@@ -2344,7 +2321,7 @@ class RuntimeTemplate {
2344
2321
  (dependency instanceof HarmonyImportDependency ||
2345
2322
  dependency instanceof ImportDependency) &&
2346
2323
  ImportPhaseUtils.isDefer(dependency.phase) &&
2347
- !(/** @type {BuildMeta} */ (module.buildMeta).async);
2324
+ !isAsyncExternal(module);
2348
2325
 
2349
2326
  if (defaultInterop) {
2350
2327
  // when the defaultInterop is used (when a ESM imports a CJS module),
@@ -2684,6 +2661,22 @@ class RuntimeTemplate {
2684
2661
  return `Promise.resolve(${comment.trim()})`;
2685
2662
  }
2686
2663
 
2664
+ /**
2665
+ * Whether any module of `chunk` carries a source type with a chunk handler of its
2666
+ * own, which `.ei` dispatches alongside the javascript one.
2667
+ * @param {Chunk} chunk the chunk being imported
2668
+ * @param {ChunkGraph} chunkGraph the chunk graph
2669
+ * @returns {boolean} true when a handler beyond the javascript one is reached
2670
+ */
2671
+ _chunkLoadsBeyondJs(chunk, chunkGraph) {
2672
+ for (const module of chunkGraph.getChunkModulesIterable(chunk)) {
2673
+ for (const type of chunkGraph.getModuleSourceTypes(module)) {
2674
+ if (!TYPES_WITHOUT_CHUNK_HANDLER.has(type)) return true;
2675
+ }
2676
+ }
2677
+ return false;
2678
+ }
2679
+
2687
2680
  /**
2688
2681
  * For ESM module output, load a single statically-named chunk through the
2689
2682
  * `analyzableChunkImport` helper — a literal `import("./chunk.js")` other bundlers
@@ -2711,30 +2704,15 @@ class RuntimeTemplate {
2711
2704
  ) {
2712
2705
  return null;
2713
2706
  }
2714
- // Prefetch/preload children are injected by the runtime `.f` handlers, which `.ei`
2715
- // runs too — but they attach to `.f`, so it has to exist.
2716
- const needsHintHandlers =
2717
- chunk.hasChildByOrder(chunkGraph, "prefetch", true) ||
2718
- chunk.hasChildByOrder(chunkGraph, "preload", true) ||
2719
- chunk.hasChildByOrder(chunkGraph, "cssPreload", true);
2720
2707
  // `.ei` always performs the import, where `.e` only reaches the javascript
2721
2708
  // loader when there is javascript to load. A module federation remote is the
2722
2709
  // case that matters: its chunk is emitted by the container's build, not this one.
2723
2710
  if (!JavascriptModulesPlugin.chunkHasJs(chunk, chunkGraph)) {
2724
- this._analyzableBailout(
2711
+ return this._analyzableBailout(
2725
2712
  originModule,
2726
- "this compilation emits no javascript for the chunk, so there is nothing to import"
2713
+ "this compilation emits no javascript for the chunk, so there is nothing to import",
2714
+ null
2727
2715
  );
2728
- return null;
2729
- }
2730
- let hasNonJsTypes = false;
2731
- for (const module of chunkGraph.getChunkModulesIterable(chunk)) {
2732
- for (const type of chunkGraph.getModuleSourceTypes(module)) {
2733
- if (!TYPES_WITHOUT_CHUNK_HANDLER.has(type)) {
2734
- // Loaded by that type's own `.f` handler, which `.ei` still dispatches.
2735
- hasNonJsTypes = true;
2736
- }
2737
- }
2738
2716
  }
2739
2717
  // Relative to the consuming chunk (`import.meta.url`) for `auto`, else an
2740
2718
  // absolute publicPath prefix.
@@ -2756,9 +2734,15 @@ class RuntimeTemplate {
2756
2734
  ? specifier
2757
2735
  : `"./${specifier.slice(1)}`;
2758
2736
  runtimeRequirements.add(RuntimeGlobals.analyzableChunkImport);
2759
- if (needsHintHandlers || hasNonJsTypes) {
2760
- // Those handlers attach to the `.f` map, which has to exist — but the runtime
2761
- // `ensureChunk` around it does not, since `.ei` dispatches them itself.
2737
+ // Prefetch/preload children and every non-javascript source type are loaded by a
2738
+ // `.f` handler, which `.ei` dispatches — but they attach to `.f`, so it has to
2739
+ // exist. The runtime `ensureChunk` around it does not.
2740
+ if (
2741
+ chunk.hasChildByOrder(chunkGraph, "prefetch", true) ||
2742
+ chunk.hasChildByOrder(chunkGraph, "preload", true) ||
2743
+ chunk.hasChildByOrder(chunkGraph, "cssPreload", true) ||
2744
+ this._chunkLoadsBeyondJs(chunk, chunkGraph)
2745
+ ) {
2762
2746
  runtimeRequirements.add(RuntimeGlobals.ensureChunkHandlers);
2763
2747
  }
2764
2748
  // Drop-in for `ensureChunk(id)`: the literal `import()` can be statically
@@ -2928,6 +2912,11 @@ class RuntimeTemplate {
2928
2912
  /** @type {Set<string>} */
2929
2913
  const names = new Set();
2930
2914
  let found = false;
2915
+ // Its own copy, since `lastIndex` is walked below; reset by the loop that ends it.
2916
+ const regexp = new RegExp(
2917
+ ANALYZABLE_TOKEN_REGEXP.source,
2918
+ ANALYZABLE_TOKEN_REGEXP.flags
2919
+ );
2931
2920
  for (const module of modules) {
2932
2921
  if (!results.has(module, chunk.runtime)) continue;
2933
2922
  const source = results.getSource(module, chunk.runtime, JAVASCRIPT_TYPE);
@@ -2937,10 +2926,6 @@ class RuntimeTemplate {
2937
2926
  carriesFullHash = true;
2938
2927
  found = true;
2939
2928
  }
2940
- const regexp = new RegExp(
2941
- ANALYZABLE_TOKEN_REGEXP.source,
2942
- ANALYZABLE_TOKEN_REGEXP.flags
2943
- );
2944
2929
  /** @type {RegExpExecArray | null} */
2945
2930
  let match;
2946
2931
  while ((match = regexp.exec(content)) !== null) {
@@ -2948,11 +2933,7 @@ class RuntimeTemplate {
2948
2933
  if (parts === null) continue;
2949
2934
  found = true;
2950
2935
  for (const [kind, value] of parts) {
2951
- if (
2952
- kind === "publicPath" ||
2953
- kind === "template" ||
2954
- kind === "unserved"
2955
- ) {
2936
+ if (FULL_HASH_PART_KINDS.has(kind)) {
2956
2937
  carriesFullHash = true;
2957
2938
  } else if (CHUNK_SPECIFIER_PART_KINDS.has(kind)) {
2958
2939
  names.add(String(value));
@@ -3213,13 +3194,13 @@ class RuntimeTemplate {
3213
3194
  return template.replace(HASH_IN_FILENAME_GLOBAL, "x");
3214
3195
  }
3215
3196
  try {
3216
- return template({
3217
- chunk: createHashProbeChunk(chunk, HASH_PROBE, true),
3218
- runtime: chunk.runtime,
3219
- contentHashType: JAVASCRIPT_TYPE,
3220
- hash: HASH_PROBE,
3221
- contentHash: HASH_PROBE
3222
- }).replace(HASH_IN_FILENAME_GLOBAL, "x");
3197
+ return probeTemplateName(
3198
+ template,
3199
+ chunk,
3200
+ JAVASCRIPT_TYPE,
3201
+ HASH_PROBE,
3202
+ true
3203
+ ).replace(HASH_IN_FILENAME_GLOBAL, "x");
3223
3204
  } catch (_error) {
3224
3205
  // Nothing to report: the naming call is given strictly less than this probe, so
3225
3206
  // one that throws here fails the build where the name is actually needed.
@@ -3311,6 +3292,41 @@ class RuntimeTemplate {
3311
3292
  : { undo, loaded };
3312
3293
  }
3313
3294
 
3295
+ /**
3296
+ * Static literal specifier (already quoted) for the `new URL(<here>, import.meta.url)`
3297
+ * a worker or worklet entry chunk bakes to, or `null` to keep the runtime form. The
3298
+ * gate and the build are asked together so every call site agrees on both — the
3299
+ * `new Worker(...)` emit, and the resource hint `ResourceHintPlugin` spells for it.
3300
+ * @param {string | undefined} overridePublicPath a `publicPath` set on the worker itself
3301
+ * @param {Chunk} chunk the worker's entry chunk
3302
+ * @param {Module} module the module the reference is emitted into
3303
+ * @param {ChunkGraph} chunkGraph the chunk graph
3304
+ * @param {RuntimeRequirements} runtimeRequirements what the consuming chunk needs
3305
+ * @returns {string | null} a quoted literal, or `null` to fall back
3306
+ */
3307
+ getAnalyzableWorkerUrl(
3308
+ overridePublicPath,
3309
+ chunk,
3310
+ module,
3311
+ chunkGraph,
3312
+ runtimeRequirements
3313
+ ) {
3314
+ // Without an id nothing can name the chunk in a stand-in.
3315
+ if (
3316
+ chunk.id === null ||
3317
+ !this.supportsAnalyzable("url", chunkGraph, module)
3318
+ ) {
3319
+ return null;
3320
+ }
3321
+ return this._getAnalyzableChunkSpecifier(
3322
+ overridePublicPath,
3323
+ chunk,
3324
+ module,
3325
+ chunkGraph,
3326
+ runtimeRequirements
3327
+ );
3328
+ }
3329
+
3314
3330
  /**
3315
3331
  * Static literal specifier (already quoted) for a `new URL(<here>, import.meta.url)`
3316
3332
  * or `import(<here>)` pointing at `chunk`'s JS file, or `null` when it can't be known
@@ -3358,33 +3374,30 @@ class RuntimeTemplate {
3358
3374
  const chunks =
3359
3375
  consumingChunks ||
3360
3376
  this._moduleChunks(/** @type {Module} */ (consumingModule), chunkGraph);
3361
- // Baking makes the consuming chunk's content depend on this one's hash. A pair that
3362
- // reaches back has no fixed point if both bake, so only the lower id's name does.
3363
- const cycles =
3364
- deferred &&
3365
- this._reachesAny(chunk, chunks) &&
3366
- !this._namesBeforeAll(chunk, chunks);
3377
+ const namesFirst = this._namesBeforeAll(chunk, chunks);
3367
3378
  // An id names the chunk in the stand-in. No reference reaches here without one —
3368
3379
  // `blockPromise` drops an id-less chunk before asking, and one the whole build
3369
3380
  // keeps has no name to be emitted under — so the guard carries no reason of its
3370
- // own; it only keeps `null` out of a name.
3381
+ // own; it only keeps `null` out of a name. A pair naming each other is fine here:
3382
+ // `RealContentHashPlugin`, which deferring requires, re-hashes the pair as one
3383
+ // group, so neither hash chases the other.
3371
3384
  const canReserve =
3372
- chunk.id !== null && !cycles && this._canDeferAnalyzableName(chunks);
3385
+ chunk.id !== null && this._canDeferAnalyzableName(chunks);
3373
3386
  // A content-named consumer bakes anyway when this chunk settles first: the fold
3374
- // puts the name into its hash. Only what `_foldsWholeName` covers, though.
3375
- const canFold = !canReserve && this._namesBeforeAll(chunk, chunks);
3387
+ // puts the name into its hash. Only what `_foldsWholeName` covers, though — and
3388
+ // a pair that reaches back has no fold order, so only the one hashing first does.
3389
+ const canFold = !canReserve && namesFirst;
3376
3390
  /**
3377
3391
  * @returns {null} always, having recorded why no stand-in may be reserved
3378
3392
  */
3379
- const cannotReserve = () => {
3393
+ const cannotReserve = () =>
3380
3394
  this._analyzableBailout(
3381
3395
  consumingModule,
3382
- cycles
3383
- ? "this chunk and the one it references name each other, so neither hash could settle"
3384
- : DEFER_BAILOUT
3396
+ !namesFirst && this._reachesAny(chunk, chunks)
3397
+ ? "this chunk and the one it references name each other, which only optimization.realContentHash could settle"
3398
+ : DEFER_BAILOUT,
3399
+ null
3385
3400
  );
3386
- return null;
3387
- };
3388
3401
  if (deferred && !canReserve && !canFold) return cannotReserve();
3389
3402
  const filename = deferred
3390
3403
  ? ""
@@ -3483,24 +3496,17 @@ class RuntimeTemplate {
3483
3496
  // Each knows its own answer, so the fill asks per asset — where each has one,
3484
3497
  // and where nothing else goes between the two shapes.
3485
3498
  if (relativeBase !== undefined || !this._eachChunkServedOneWay(chunks)) {
3486
- this._analyzableBailout(module, SERVED_BAILOUT);
3487
- return null;
3499
+ return this._analyzableBailout(module, SERVED_BAILOUT, null);
3488
3500
  }
3489
3501
  return [...head, ["unserved", ""]];
3490
3502
  }
3491
3503
  if (climb !== "" && relativeBase === undefined) return head;
3492
3504
  const resolved = resolve();
3493
3505
  if (resolved === null) return null;
3494
- return [
3495
- ...head,
3496
- .../** @type {SpecifierPart[]} */ (
3497
- climb === "" ? [] : [["literal", climb]]
3498
- ),
3499
- .../** @type {SpecifierPart[]} */ (
3500
- relativeBase === undefined ? [] : [["literal", relativeBase]]
3501
- ),
3502
- ...rootedParts(resolved)
3503
- ];
3506
+ if (climb !== "") head.push(["literal", climb]);
3507
+ if (relativeBase !== undefined) head.push(["literal", relativeBase]);
3508
+ head.push(...rootedParts(resolved));
3509
+ return head;
3504
3510
  }
3505
3511
 
3506
3512
  /**
@@ -3626,11 +3632,6 @@ class RuntimeTemplate {
3626
3632
  ) {
3627
3633
  const { publicPath } = this.outputOptions;
3628
3634
  const chunks = this._moduleChunks(module, chunkGraph);
3629
- /**
3630
- * @param {SpecifierPart[]} parts the whole specifier
3631
- * @returns {string | null} it already quoted, or `null` when no stand-in may be reserved
3632
- */
3633
- const specifier = (parts) => this._specifierOf(parts, chunks, module);
3634
3635
  if (bakePublicPath && publicPath !== "auto") {
3635
3636
  const prefix = this._analyzablePathPrefix(
3636
3637
  module,
@@ -3638,25 +3639,24 @@ class RuntimeTemplate {
3638
3639
  chunks,
3639
3640
  relativeBase
3640
3641
  );
3641
- return prefix === null ? null : specifier([...prefix, ...file]);
3642
+ if (prefix === null) return null;
3643
+ return this._specifierOf([...prefix, ...file], chunks, module);
3642
3644
  }
3643
3645
  // No public path to place it against, so the `../` path back to the output root
3644
3646
  // is the whole prefix. Different depths — no one is right for every asset the
3645
3647
  // reference is emitted into, so the deferred pass builds each asset's own.
3646
3648
  const { undo } = this._modulePlacement(module, chunkGraph);
3647
- return specifier([
3648
- undo === null ? ["undo", ""] : ["literal", undo],
3649
- ...(relativeBase === undefined
3650
- ? []
3651
- : /** @type {SpecifierPart[]} */ ([["literal", relativeBase]])),
3652
- ...file
3653
- ]);
3649
+ /** @type {SpecifierPart[]} */
3650
+ const parts = [undo === null ? ["undo", ""] : ["literal", undo]];
3651
+ if (relativeBase !== undefined) parts.push(["literal", relativeBase]);
3652
+ parts.push(...file);
3653
+ return this._specifierOf(parts, chunks, module);
3654
3654
  }
3655
3655
 
3656
3656
  /**
3657
3657
  * Static `new URL(<file>, import.meta.url)` for every stylesheet a runtime can load,
3658
- * keyed by chunk id, or `null` when any of them has to keep the runtime
3659
- * `publicPath + getChunkCssFilename(id)` form. Both the runtime module reading them
3658
+ * keyed by chunk id — one that cannot be named leaves the map incomplete, and its
3659
+ * id keeps the runtime `publicPath + getChunkCssFilename(id)` form. Both the runtime module reading them
3660
3660
  * and the plugin declaring what it needs ask this, so the two always agree. The
3661
3661
  * runtime hands an absolute url string to `link.href`, and so does this — the browser
3662
3662
  * resolves the element against the document, which is not where the chunk sits, and
@@ -3666,7 +3666,7 @@ class RuntimeTemplate {
3666
3666
  * @param {ChunkGraph} chunkGraph the chunk graph
3667
3667
  * @param {ReadOnlyRuntimeRequirements} runtimeRequirements what that chunk needs
3668
3668
  * @param {Module=} consumingModule the runtime module, where one exists to report against
3669
- * @returns {Map<ChunkId, string> | null} the urls, or `null` to keep the runtime form
3669
+ * @returns {AnalyzableChunkUrls | null} the urls, or `null` to keep the runtime form
3670
3670
  */
3671
3671
  analyzableCssChunkUrls(
3672
3672
  runtimeChunk,
@@ -3698,14 +3698,14 @@ class RuntimeTemplate {
3698
3698
  /**
3699
3699
  * Static `new URL(<file>, import.meta.url).href` for every javascript chunk a
3700
3700
  * runtime can hint at with `<link rel="prefetch"/"modulepreload">`, keyed by chunk
3701
- * id, or `null` when any of them has to keep the runtime
3702
- * `publicPath + getChunkScriptFilename(id)` form. Both the runtime module reading
3701
+ * id — one that cannot be named leaves the map incomplete, and its id keeps the
3702
+ * runtime `publicPath + getChunkScriptFilename(id)` form. Both the runtime module reading
3703
3703
  * them and the plugin declaring what it needs ask this, so the two always agree.
3704
3704
  * @param {Chunk} runtimeChunk the chunk holding the hint handlers
3705
3705
  * @param {ChunkGraph} chunkGraph the chunk graph
3706
3706
  * @param {ReadOnlyRuntimeRequirements} runtimeRequirements what that chunk needs
3707
3707
  * @param {Module=} consumingModule the runtime module, where one exists to report against
3708
- * @returns {Map<ChunkId, string> | null} the urls, or `null` to keep the runtime form
3708
+ * @returns {AnalyzableChunkUrls | null} the urls, or `null` to keep the runtime form
3709
3709
  */
3710
3710
  analyzableChunkScriptUrls(
3711
3711
  runtimeChunk,
@@ -3722,11 +3722,9 @@ class RuntimeTemplate {
3722
3722
  );
3723
3723
  /** @type {Set<ChunkId>} */
3724
3724
  const hinted = new Set();
3725
- for (const order of Object.keys(orders)) {
3726
- for (const from of Object.keys(orders[order])) {
3727
- for (const id of orders[order][/** @type {ChunkId} */ (from)]) {
3728
- hinted.add(id);
3729
- }
3725
+ for (const byParent of Object.values(orders)) {
3726
+ for (const ids of Object.values(byParent)) {
3727
+ for (const id of ids) hinted.add(id);
3730
3728
  }
3731
3729
  }
3732
3730
  /** @type {Chunk[]} */
@@ -3746,14 +3744,15 @@ class RuntimeTemplate {
3746
3744
 
3747
3745
  /**
3748
3746
  * The urls of one asset of each of `chunks`, written out so they can be read by
3749
- * chunk id, or `null` as soon as one of them cannot be named here.
3747
+ * chunk id. A chunk that cannot be named here leaves the map incomplete rather
3748
+ * than losing it — the consumer keeps the runtime form for the ids the map lacks.
3750
3749
  * @param {Iterable<Chunk>} chunks the chunks whose asset is wanted
3751
3750
  * @param {ChunkGraph} chunkGraph the chunk graph
3752
3751
  * @param {Module | undefined} consumingModule the runtime module, where one exists to report against
3753
3752
  * @param {Chunk[]} consumingChunks the chunks the urls are written into
3754
3753
  * @param {string} sourceType which asset of each chunk to name
3755
3754
  * @param {ReadOnlyRuntimeRequirements} runtimeRequirements what the runtime chunk needs
3756
- * @returns {Map<ChunkId, string> | null} the urls, or `null` to keep the runtime form
3755
+ * @returns {AnalyzableChunkUrls | null} the urls, or `null` to keep the runtime form
3757
3756
  */
3758
3757
  _analyzableChunkUrls(
3759
3758
  chunks,
@@ -3773,6 +3772,7 @@ class RuntimeTemplate {
3773
3772
  }
3774
3773
  /** @type {Map<ChunkId, string>} */
3775
3774
  const urls = new Map();
3775
+ let complete = true;
3776
3776
  for (const chunk of chunks) {
3777
3777
  if (chunk.id === null) continue;
3778
3778
  const specifier = this._getAnalyzableChunkSpecifier(
@@ -3784,10 +3784,14 @@ class RuntimeTemplate {
3784
3784
  consumingChunks,
3785
3785
  sourceType
3786
3786
  );
3787
- if (specifier === null) return null;
3787
+ // `_getAnalyzableChunkSpecifier` has already recorded why.
3788
+ if (specifier === null) {
3789
+ complete = false;
3790
+ continue;
3791
+ }
3788
3792
  urls.set(chunk.id, `${this.importMetaUrl(specifier)}.href`);
3789
3793
  }
3790
- return urls.size > 0 ? urls : null;
3794
+ return urls.size > 0 ? { urls, complete } : null;
3791
3795
  }
3792
3796
 
3793
3797
  /**
@@ -3852,20 +3856,20 @@ class RuntimeTemplate {
3852
3856
  try {
3853
3857
  // Everything the naming call is given except the hashes, so only a hash can
3854
3858
  // make the two answers differ.
3855
- const template = filenameTemplate({
3856
- chunk: createHashProbeChunk(chunk, HASH_PROBE, true),
3857
- runtime: chunk.runtime,
3859
+ const template = probeTemplateName(
3860
+ filenameTemplate,
3861
+ chunk,
3858
3862
  contentHashType,
3859
- hash: HASH_PROBE,
3860
- contentHash: HASH_PROBE
3861
- });
3862
- const probe = filenameTemplate({
3863
- chunk: createHashProbeChunk(chunk, HASH_PROBE_ALTERNATE, false),
3864
- runtime: chunk.runtime,
3863
+ HASH_PROBE,
3864
+ true
3865
+ );
3866
+ const probe = probeTemplateName(
3867
+ filenameTemplate,
3868
+ chunk,
3865
3869
  contentHashType,
3866
- hash: HASH_PROBE_ALTERNATE,
3867
- contentHash: HASH_PROBE_ALTERNATE
3868
- });
3870
+ HASH_PROBE_ALTERNATE,
3871
+ false
3872
+ );
3869
3873
  return template === probe ? template : undefined;
3870
3874
  } catch (_error) {
3871
3875
  // Nothing to report: the probe is given strictly more than the naming call,
@@ -3882,21 +3886,16 @@ class RuntimeTemplate {
3882
3886
  * @returns {boolean} true when every runtime reaching it fetches
3883
3887
  */
3884
3888
  _wasmModuleFetches(module, chunkGraph) {
3885
- /** @type {string[]} */
3886
- const keys = [];
3889
+ const { onlyFetching } = this._wasmGroups();
3890
+ let placed = false;
3887
3891
  for (const moduleRuntime of chunkGraph.getModuleRuntimes(module)) {
3888
- forEachRuntime(moduleRuntime, (key) => {
3889
- keys.push(/** @type {string} */ (key));
3890
- });
3892
+ for (const group of this._wasmGroupsOf(moduleRuntime)) {
3893
+ placed = true;
3894
+ if (!onlyFetching.get(group)) return false;
3895
+ }
3891
3896
  }
3892
3897
  // In no runtime at all nothing named a loader, so `output` answers.
3893
- if (keys.length === 0) return this.outputOptions.wasmLoading === "fetch";
3894
- const only = this._wasmGroupOnlyFetches();
3895
- const groups = this._wasmRuntimeGroups();
3896
- for (const key of keys) {
3897
- if (!only.get(this._wasmGroupOf(groups, key))) return false;
3898
- }
3899
- return true;
3898
+ return placed || this.outputOptions.wasmLoading === "fetch";
3900
3899
  }
3901
3900
 
3902
3901
  /**
@@ -4073,6 +4072,18 @@ class RuntimeTemplate {
4073
4072
  let chunksById;
4074
4073
  /** @type {Map<string, Chunk> | undefined} */
4075
4074
  let chunkByAsset;
4075
+ /** @type {string | undefined} */
4076
+ let publicPathText;
4077
+ // One value for the whole pass, however many stand-ins read it.
4078
+ const resolvedPublicPath = () => {
4079
+ if (publicPathText === undefined) {
4080
+ publicPathText = compilation.getPath(
4081
+ outputOptions.publicPath || "",
4082
+ {}
4083
+ );
4084
+ }
4085
+ return publicPathText;
4086
+ };
4076
4087
  /**
4077
4088
  * @param {string} name an emitted asset's name
4078
4089
  * @returns {Chunk | undefined} the chunk it was emitted for
@@ -4127,10 +4138,7 @@ class RuntimeTemplate {
4127
4138
  {}
4128
4139
  );
4129
4140
  } else if (kind === "publicPath") {
4130
- specifier += compilation.getPath(
4131
- outputOptions.publicPath || "",
4132
- {}
4133
- );
4141
+ specifier += resolvedPublicPath();
4134
4142
  } else if (kind === "unserved") {
4135
4143
  const holder = chunkOf(assetName);
4136
4144
  if (holder === undefined) return null;
@@ -4141,10 +4149,7 @@ class RuntimeTemplate {
4141
4149
  compilation.runtimeTemplate._chunkPlacement(holder).loaded ===
4142
4150
  false
4143
4151
  ) {
4144
- const path = compilation.getPath(
4145
- outputOptions.publicPath || "",
4146
- {}
4147
- );
4152
+ const path = resolvedPublicPath();
4148
4153
  // The `../` already reached the root this is read from.
4149
4154
  specifier += path.startsWith("./") ? path.slice(2) : path;
4150
4155
  }