webpack 5.110.2 → 5.111.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 (226) hide show
  1. package/README.md +5 -2
  2. package/lib/APIPlugin.js +23 -7
  3. package/lib/AutomaticPrefetchPlugin.js +1 -1
  4. package/lib/Cache.js +8 -3
  5. package/lib/CacheFacade.js +1 -1
  6. package/lib/ChunkGraph.js +20 -18
  7. package/lib/CircularModulesPlugin.js +157 -55
  8. package/lib/CleanPlugin.js +2 -2
  9. package/lib/Compilation.js +321 -303
  10. package/lib/Compiler.js +209 -46
  11. package/lib/ConcatenationScope.js +25 -35
  12. package/lib/ContextModule.js +17 -81
  13. package/lib/ContextModuleFactory.js +1 -1
  14. package/lib/CopyPlugin.js +1054 -0
  15. package/lib/DefinePlugin.js +24 -8
  16. package/lib/DotenvPlugin.js +41 -13
  17. package/lib/ExternalModule.js +41 -14
  18. package/lib/FileSystemInfo.js +17 -14
  19. package/lib/FlagDependencyExportsPlugin.js +45 -21
  20. package/lib/FlagDependencyUsagePlugin.js +8 -2
  21. package/lib/HotModuleReplacementPlugin.js +53 -7
  22. package/lib/JavascriptMetaInfoPlugin.js +5 -3
  23. package/lib/MainTemplate.js +5 -1
  24. package/lib/Module.js +16 -7
  25. package/lib/MultiCompiler.js +1 -1
  26. package/lib/MultiWatching.js +1 -1
  27. package/lib/NormalModule.js +58 -32
  28. package/lib/NormalModuleFactory.js +1 -1
  29. package/lib/RuntimeGlobals.js +21 -7
  30. package/lib/RuntimeModule.js +28 -12
  31. package/lib/RuntimePlugin.js +33 -3
  32. package/lib/RuntimeTemplate.js +794 -477
  33. package/lib/SourceMapDevToolPlugin.js +1 -1
  34. package/lib/Watching.js +9 -0
  35. package/lib/WebpackOptionsApply.js +84 -91
  36. package/lib/asset/AssetGenerator.js +25 -8
  37. package/lib/asset/AssetModule.js +3 -0
  38. package/lib/asset/WebManifestParser.js +7 -5
  39. package/lib/async-modules/AsyncModuleHelpers.js +10 -4
  40. package/lib/cache/AddBuildDependenciesPlugin.js +33 -4
  41. package/lib/cache/IdleFileCachePlugin.js +2 -2
  42. package/lib/cache/PackFileCacheStrategy.js +14 -1
  43. package/lib/config/browserslistTargetHandler.js +19 -0
  44. package/lib/config/defaults.js +110 -64
  45. package/lib/config/normalization.js +100 -20
  46. package/lib/config/target.js +10 -0
  47. package/lib/container/ContainerReferencePlugin.js +6 -0
  48. package/lib/container/RemoteRuntimeModule.js +9 -0
  49. package/lib/container/declaredRemotes.js +39 -0
  50. package/lib/css/CssGenerator.js +8 -0
  51. package/lib/css/CssInjectStyleRuntimeModule.js +9 -0
  52. package/lib/css/CssLoadingRuntimeModule.js +94 -36
  53. package/lib/css/CssModule.js +1 -1
  54. package/lib/css/CssModulesPlugin.js +7 -6
  55. package/lib/css/CssParser.js +144 -11
  56. package/lib/css/CssServerStylesRuntimeModule.js +9 -0
  57. package/lib/css/cssMinify.js +11 -3
  58. package/lib/css/data.js +2814 -678
  59. package/lib/css/syntax.js +4082 -248
  60. package/lib/debug/ProfilingPlugin.js +4 -0
  61. package/lib/dependencies/AMDDefineDependencyParserPlugin.js +6 -3
  62. package/lib/dependencies/AMDRuntimeModules.js +18 -0
  63. package/lib/dependencies/CommonJsExportRequireDependency.js +3 -1
  64. package/lib/dependencies/CommonJsExportsParserPlugin.js +12 -13
  65. package/lib/dependencies/CommonJsImportsParserPlugin.js +1 -0
  66. package/lib/dependencies/CommonJsPlugin.js +19 -3
  67. package/lib/dependencies/CommonJsRequireDependency.js +32 -9
  68. package/lib/dependencies/ContextDependencyTemplateAsRequireCall.js +20 -7
  69. package/lib/dependencies/CssIcssExportDependency.js +7 -1
  70. package/lib/dependencies/HarmonyCompatibilityDependency.js +58 -4
  71. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +8 -3
  72. package/lib/dependencies/HarmonyExportExpressionDependency.js +44 -29
  73. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +171 -32
  74. package/lib/dependencies/HarmonyExportInitFragment.js +99 -19
  75. package/lib/dependencies/HarmonyExportSpecifierDependency.js +18 -30
  76. package/lib/dependencies/HarmonyImportBareSideEffectDependency.js +59 -0
  77. package/lib/dependencies/HarmonyImportDependency.js +99 -41
  78. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +20 -3
  79. package/lib/dependencies/HarmonyImportSpecifierDependency.js +24 -1
  80. package/lib/dependencies/HarmonyModulesPlugin.js +20 -0
  81. package/lib/dependencies/HtmlEntryDependency.js +62 -2
  82. package/lib/dependencies/ImportContextDependency.js +43 -3
  83. package/lib/dependencies/ImportDependency.js +8 -29
  84. package/lib/dependencies/ImportMetaMainDependency.js +104 -0
  85. package/lib/dependencies/ImportMetaPlugin.js +59 -15
  86. package/lib/dependencies/ImportParserPlugin.js +24 -14
  87. package/lib/dependencies/RequireHeaderDependency.js +20 -21
  88. package/lib/dependencies/SystemRuntimeModule.js +9 -0
  89. package/lib/dependencies/WorkerDependency.js +1 -2
  90. package/lib/dependencies/WorkletDependency.js +1 -2
  91. package/lib/dependencies/importOptionsCheck.js +47 -0
  92. package/lib/dll/DelegatedModule.js +18 -1
  93. package/lib/dll/DllReferencePlugin.js +11 -8
  94. package/lib/dll/LibManifestPlugin.js +1 -1
  95. package/lib/errors/AnalyzableBailoutsWarning.js +41 -0
  96. package/lib/errors/DotenvFileError.js +29 -0
  97. package/lib/errors/DuplicateModulesWarning.js +12 -7
  98. package/lib/errors/EmptyCopyPatternWarning.js +26 -0
  99. package/lib/errors/ExternalStarReexportError.js +44 -0
  100. package/lib/errors/ImportedBindingAssignmentError.js +30 -0
  101. package/lib/errors/MissingSourceMapsWarning.js +36 -0
  102. package/lib/errors/ModuleParseError.js +2 -1
  103. package/lib/errors/UnusedAssetsWarning.js +36 -0
  104. package/lib/errors/UnusedFederationWarning.js +35 -0
  105. package/lib/errors/UnusedModulesWarning.js +38 -0
  106. package/lib/esm/ExportWebpackRequireRuntimeModule.js +9 -0
  107. package/lib/esm/ModuleChunkFormatPlugin.js +28 -3
  108. package/lib/esm/ModuleChunkLoadingPlugin.js +30 -11
  109. package/lib/esm/ModuleChunkLoadingRuntimeModule.js +92 -60
  110. package/lib/hmr/HotModuleReplacement.runtime.js +8 -2
  111. package/lib/hmr/HotModuleReplacementRuntimeModule.js +9 -0
  112. package/lib/html/HtmlGenerator.js +11 -5
  113. package/lib/html/HtmlModulesPlugin.js +360 -84
  114. package/lib/html/HtmlParser.js +13 -6
  115. package/lib/html/builtinEmbeddedRenderer.js +107 -0
  116. package/lib/html/data.js +121 -7
  117. package/lib/html/htmlMinify.js +84 -113
  118. package/lib/html/syntax.js +1067 -569
  119. package/lib/ids/HashedModuleIdsPlugin.js +2 -1
  120. package/lib/index.js +3 -0
  121. package/lib/javascript/JavascriptModule.js +1 -0
  122. package/lib/javascript/JavascriptModulesPlugin.js +52 -22
  123. package/lib/javascript/JavascriptParser.js +1255 -728
  124. package/lib/javascript/StartupHelpers.js +27 -3
  125. package/lib/javascript/data.js +172 -0
  126. package/lib/javascript/grammar.js +5546 -0
  127. package/lib/javascript/parser.js +1494 -0
  128. package/lib/javascript/regexp.js +1762 -0
  129. package/lib/javascript/regexpData.js +51 -0
  130. package/lib/javascript/syntax.js +8696 -5225
  131. package/lib/library/ModuleLibraryPlugin.js +116 -5
  132. package/lib/node/ReadFileChunkLoadingRuntimeModule.js +22 -0
  133. package/lib/node/RequireChunkLoadingRuntimeModule.js +22 -0
  134. package/lib/optimize/ConcatenatedModule.js +133 -25
  135. package/lib/optimize/ConstExportsPlugin.js +50 -39
  136. package/lib/optimize/InlineExports.js +20 -123
  137. package/lib/optimize/MangleExportsPlugin.js +10 -0
  138. package/lib/optimize/ModuleConcatenationPlugin.js +24 -1
  139. package/lib/optimize/RealContentHashPlugin.js +68 -9
  140. package/lib/optimize/SideEffectsFlagPlugin.js +17 -9
  141. package/lib/performance/AnalyzableBailoutsPlugin.js +115 -0
  142. package/lib/performance/CacheEffectivenessPlugin.js +4 -3
  143. package/lib/performance/DuplicateModulesPlugin.js +57 -13
  144. package/lib/performance/EvalUsagePlugin.js +5 -2
  145. package/lib/performance/MissingSourceMapsPlugin.js +115 -0
  146. package/lib/performance/PureAnnotationsPlugin.js +6 -3
  147. package/lib/performance/TopLevelThisPlugin.js +5 -4
  148. package/lib/performance/UnusedAssetsPlugin.js +109 -0
  149. package/lib/performance/UnusedFederationPlugin.js +147 -0
  150. package/lib/performance/UnusedModulesPlugin.js +141 -0
  151. package/lib/performance/UnusedReexportsPlugin.js +1 -55
  152. package/lib/performance/isOnlyReexported.js +65 -0
  153. package/lib/prefetch/ChunkPrefetchFunctionRuntimeModule.js +25 -5
  154. package/lib/prefetch/ChunkPrefetchPreloadPlugin.js +2 -0
  155. package/lib/prefetch/ChunkPrefetchStartupRuntimeModule.js +9 -0
  156. package/lib/prefetch/ChunkPrefetchTriggerRuntimeModule.js +9 -0
  157. package/lib/prefetch/ChunkPreloadTriggerRuntimeModule.js +9 -0
  158. package/lib/prefetch/ResourceHintPlugin.js +7 -3
  159. package/lib/prefetch/ResourceHintRuntimeModule.js +9 -0
  160. package/lib/prefetch/StartupAssetHintRuntimeModule.js +9 -0
  161. package/lib/rules/RuleSetCompiler.js +211 -4
  162. package/lib/runtime/AsyncModuleRuntimeModule.js +105 -67
  163. package/lib/runtime/AutoPublicPathRuntimeModule.js +9 -0
  164. package/lib/runtime/BaseUriRuntimeModule.js +9 -0
  165. package/lib/runtime/ChunkNameRuntimeModule.js +9 -0
  166. package/lib/runtime/EnsureChunkRuntimeModule.js +35 -16
  167. package/lib/runtime/GetChunkFilenameRuntimeModule.js +9 -0
  168. package/lib/runtime/GetFullHashRuntimeModule.js +9 -0
  169. package/lib/runtime/GetMainFilenameRuntimeModule.js +9 -0
  170. package/lib/runtime/GetWorkletBootstrapRuntimeModule.js +9 -0
  171. package/lib/runtime/GlobalRuntimeModule.js +9 -0
  172. package/lib/runtime/HasOwnPropertyRuntimeModule.js +9 -0
  173. package/lib/runtime/HelperRuntimeModule.js +10 -0
  174. package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +338 -217
  175. package/lib/runtime/NonceRuntimeModule.js +9 -0
  176. package/lib/runtime/OnChunksLoadedRuntimeModule.js +67 -20
  177. package/lib/runtime/PublicPathRuntimeModule.js +9 -0
  178. package/lib/runtime/RuntimeIdRuntimeModule.js +9 -0
  179. package/lib/runtime/SpecNamespaceObjectRuntimeModule.js +90 -0
  180. package/lib/runtime/StartupChunkDependenciesRuntimeModule.js +9 -0
  181. package/lib/runtime/StartupEntrypointRuntimeModule.js +9 -0
  182. package/lib/runtime/SystemContextRuntimeModule.js +9 -0
  183. package/lib/runtime/ToBinaryRuntimeModule.js +9 -0
  184. package/lib/schemes/HttpUriPlugin.js +22 -5
  185. package/lib/serialization/FileMiddleware.js +15 -2
  186. package/lib/serialization/ObjectMiddleware.js +138 -106
  187. package/lib/sharing/ConsumeSharedPlugin.js +12 -0
  188. package/lib/sharing/ConsumeSharedRuntimeModule.js +11 -0
  189. package/lib/sharing/ShareRuntimeModule.js +9 -0
  190. package/lib/sharing/declaredShared.js +47 -0
  191. package/lib/stats/DefaultStatsFactoryPlugin.js +3 -1
  192. package/lib/stats/DefaultStatsPrinterPlugin.js +3 -0
  193. package/lib/util/ArrayQueue.js +3 -3
  194. package/lib/util/SourceProcessor.js +76 -69
  195. package/lib/util/URLAbsoluteSpecifier.js +3 -1
  196. package/lib/util/async.js +292 -0
  197. package/lib/util/buildDiagnostics.js +69 -0
  198. package/lib/util/conventions.js +5 -1
  199. package/lib/util/fs.js +27 -1
  200. package/lib/util/handlerKeys.js +40 -0
  201. package/lib/util/identifier.js +20 -0
  202. package/lib/util/internalSerializables.js +6 -0
  203. package/lib/util/registerExternalSerializer.js +6 -45
  204. package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +9 -0
  205. package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +9 -0
  206. package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +9 -0
  207. package/lib/web/JsonpChunkLoadingRuntimeModule.js +36 -0
  208. package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +19 -0
  209. package/package.json +22 -16
  210. package/schemas/WebpackOptions.check.js +1 -1
  211. package/schemas/WebpackOptions.json +388 -76
  212. package/schemas/plugins/LoaderOptionsPlugin.check.js +1 -1
  213. package/schemas/plugins/ManifestPlugin.check.js +1 -1
  214. package/schemas/plugins/SourceMapDevToolPlugin.check.js +1 -1
  215. package/schemas/plugins/asset/AssetGeneratorOptions.check.js +1 -1
  216. package/schemas/plugins/asset/AssetResourceGeneratorOptions.check.js +1 -1
  217. package/schemas/plugins/container/ContainerPlugin.check.js +1 -1
  218. package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
  219. package/schemas/plugins/debug/ProfilingPlugin.check.js +1 -1
  220. package/schemas/plugins/dll/DllReferencePlugin.check.js +1 -1
  221. package/schemas/plugins/ids/HashedModuleIdsPlugin.check.js +1 -1
  222. package/schemas/plugins/schemes/HttpUriPlugin.check.js +1 -1
  223. package/types.d.ts +1440 -331
  224. package/lib/dependencies/ExportBindingInitFragment.js +0 -191
  225. package/lib/errors/EntrypointOverlapWarning.js +0 -45
  226. package/lib/performance/EntrypointOverlapPlugin.js +0 -122
@@ -9,14 +9,7 @@ const { CachedSource, ReplaceSource } = require("webpack-sources");
9
9
  const APIPlugin = require("./APIPlugin");
10
10
 
11
11
  const InitFragment = require("./InitFragment");
12
- const {
13
- ASSET_TYPE,
14
- ASSET_URL_TYPE,
15
- CSS_TYPE,
16
- HTML_TYPE,
17
- JAVASCRIPT_TYPE,
18
- RUNTIME_TYPE
19
- } = require("./ModuleSourceTypeConstants");
12
+ const { CSS_TYPE, JAVASCRIPT_TYPE } = require("./ModuleSourceTypeConstants");
20
13
  const { WEBASSEMBLY_MODULE_TYPE_ASYNC } = require("./ModuleTypeConstants");
21
14
  const RuntimeGlobals = require("./RuntimeGlobals");
22
15
  const Template = require("./Template");
@@ -31,6 +24,7 @@ const { InlinedUsedName } = require("./optimize/InlineExports");
31
24
  const {
32
25
  getDeferredCycleModuleIds,
33
26
  getDeferredCycleModules,
27
+ getKnownExportNames,
34
28
  getMakeDeferredNamespaceModeFromExportsType,
35
29
  getOptimizedDeferredModule
36
30
  } = require("./runtime/MakeDeferredNamespaceObjectRuntime");
@@ -42,7 +36,11 @@ const { equals } = require("./util/ArrayHelpers");
42
36
  const { getScheme } = require("./util/URLAbsoluteSpecifier");
43
37
  const { compareIds } = require("./util/comparators");
44
38
  const compileBooleanMatcher = require("./util/compileBooleanMatcher");
45
- const { getUndoPath, toJsStringLiteral } = require("./util/identifier");
39
+ const {
40
+ RESOLVABLE_SPECIFIER_REGEXP,
41
+ getUndoPath,
42
+ toJsStringLiteral
43
+ } = require("./util/identifier");
46
44
  const memoize = require("./util/memoize");
47
45
  const { propertyAccess, propertyName } = require("./util/property");
48
46
  const {
@@ -55,6 +53,7 @@ const {
55
53
  const getCssModulesPlugin = memoize(() => require("./css/CssModulesPlugin"));
56
54
  // `Compilation` requires this module, so its stage constants are read lazily.
57
55
  const getCompilation = memoize(() => require("./Compilation"));
56
+ const getExternalModule = memoize(() => require("./ExternalModule"));
58
57
 
59
58
  /**
60
59
  * @typedef {object} ChunkAssetNaming
@@ -91,6 +90,20 @@ const getTemplatedPathPlugin = memoize(() => require("./TemplatedPathPlugin"));
91
90
  const getConcatenatedModule = memoize(() =>
92
91
  require("./optimize/ConcatenatedModule")
93
92
  );
93
+ const getNormalModule = memoize(() => require("./NormalModule"));
94
+
95
+ /**
96
+ * Whether a hot update can re-run the module a reference is emitted into: the HMR
97
+ * plugin marks every normal module, and a concatenated one is read through its root.
98
+ * @param {Module | undefined} module the module a reference is emitted into
99
+ * @returns {boolean} true under HMR
100
+ */
101
+ const isHotModule = (module) => {
102
+ if (module === undefined) return false;
103
+ const normal =
104
+ module instanceof getConcatenatedModule() ? module.rootModule : module;
105
+ return normal instanceof getNormalModule() && normal.hot;
106
+ };
94
107
 
95
108
  // Any `[hash]`/`[fullhash]`/`[chunkhash]`/`[contenthash]` token, incl. a `:<length>`
96
109
  // or `:<digest>[:<length>]` suffix (e.g. `[contenthash:base64:8]`). Its value is only
@@ -103,6 +116,9 @@ const HASH_PROBE = "0123456789abcdef0123";
103
116
  const HASH_PROBE_ALTERNATE = "3210fedcba9876543210";
104
117
  const HASH_IN_FILENAME_GLOBAL = /\[(?:full|chunk|content)?hash(?::[^\]]+)?\]/g;
105
118
 
119
+ // What a recorded reason is prefixed with in `stats.optimizationBailout`.
120
+ const ANALYZABLE_BAILOUT_PREFIX = "Analyzable ESM bailout: ";
121
+
106
122
  /**
107
123
  * @param {SpecifierPart} part one piece of a reserved name
108
124
  * @returns {boolean} true when it is text the deferred pass adds nothing to
@@ -155,21 +171,6 @@ const rootedParts = (parts) =>
155
171
  ]
156
172
  : parts;
157
173
 
158
- // Types that ride the chunk itself or render their own asset, so no `.f` handler
159
- // fetches them; every other type may install one, so the map has to exist for it.
160
- /** @type {Set<string>} */
161
- const TYPES_WITHOUT_CHUNK_HANDLER = new Set([
162
- JAVASCRIPT_TYPE,
163
- RUNTIME_TYPE,
164
- ASSET_TYPE,
165
- ASSET_URL_TYPE,
166
- HTML_TYPE
167
- ]);
168
-
169
- // Why a name only the deferred pass could settle was not deferred.
170
- const DEFER_BAILOUT =
171
- "a hashed name needs optimization.realContentHash, or no emitted javascript named by its content";
172
-
173
174
  /**
174
175
  * Where a literal is read from — the `../` path back to the output root, and whether
175
176
  * the chunk loader fetched the chunk it sits in through `output.publicPath`.
@@ -178,13 +179,6 @@ const DEFER_BAILOUT =
178
179
  * @property {boolean | null} loaded whether the chunk loader fetched the chunk
179
180
  */
180
181
 
181
- /**
182
- * @typedef {object} AnalyzableChunkUrls
183
- * @property {Map<ChunkId, string>} urls the urls that could be written, by chunk id
184
- * @property {boolean} complete false when some chunk kept the runtime form, whose
185
- * ids the consumer then still resolves through the runtime name lookup
186
- */
187
-
188
182
  /**
189
183
  * @typedef {object} WasmGroups
190
184
  * @property {(key: string) => string} groupOf the group a runtime key answers with
@@ -216,7 +210,12 @@ const RUNTIME_HASH_ROUND = 2;
216
210
 
217
211
  // Why a public path needing a base could not be spelled from the chunk holding it.
218
212
  const SERVED_BAILOUT =
219
- "this module is in a chunk webpack loads through output.publicPath and in one it does not, so no one path back to the output root fits both";
213
+ 'this module is in a chunk webpack loads through output.publicPath and in one it does not, so no one path back to the output root fits both (a root-absolute or absolute output.publicPath, or "auto", is read the same from both)';
214
+
215
+ // Why a runtime's url map kept the runtime form under HMR: an update re-ships the
216
+ // runtime module only when its own code changes.
217
+ const HOT_NAME_BAILOUT =
218
+ "a hot update can move this name without changing the runtime module that spells it, so the update would not carry the new one";
220
219
 
221
220
  /**
222
221
  * @import {
@@ -237,8 +236,9 @@ const SERVED_BAILOUT =
237
236
  * One piece of a name only the deferred pass can spell: text as it stands, the `../`
238
237
  * path from the asset it sits in to the output root, a template or `output.publicPath`
239
238
  * resolved once the hashes exist, one of the assets the chunk with this id emits, or
240
- * the base everything else is resolved against once it is spelled.
241
- * @typedef {"literal" | "undo" | "template" | "publicPath" | "unserved" | "chunk" | "cssChunk" | "base"} SpecifierPartKind
239
+ * the base everything else is resolved against once it is spelled. A `repair` part
240
+ * spells nothing: it marks a name the repair pass has to bring back in line.
241
+ * @typedef {"literal" | "undo" | "template" | "publicPath" | "unserved" | "chunk" | "cssChunk" | "base" | "repair"} SpecifierPartKind
242
242
  */
243
243
  /** @typedef {[SpecifierPartKind, string | number]} SpecifierPart */
244
244
 
@@ -250,7 +250,8 @@ const SPECIFIER_PART_KINDS = new Set([
250
250
  "unserved",
251
251
  "base",
252
252
  "chunk",
253
- "cssChunk"
253
+ "cssChunk",
254
+ "repair"
254
255
  ]);
255
256
 
256
257
  // Only these carry a chunk id, which is the one part value that may be a number.
@@ -261,7 +262,10 @@ const CHUNK_SPECIFIER_PART_KINDS = new Set(["chunk", "cssChunk"]);
261
262
  const FULL_HASH_PART_KINDS = new Set(["publicPath", "template", "unserved"]);
262
263
 
263
264
  // What the deferred pass scans for — the other half of what the class spells.
265
+ const ANALYZABLE_TOKEN_PREFIX = "./@@webpackAnalyzableChunk:";
264
266
  const ANALYZABLE_TOKEN_REGEXP = /\.\/@@webpackAnalyzableChunk:([\w-]+)@@/g;
267
+ // The same token read back out of one quoted specifier.
268
+ const ANALYZABLE_TOKEN_PAYLOAD = /@@webpackAnalyzableChunk:([\w-]+)@@/;
265
269
 
266
270
  // Stands in for the compilation hash inside an otherwise resolved filename, with the
267
271
  // requested length when the placeholder asked for one.
@@ -304,8 +308,7 @@ const PASS_NAME = "analyzableChunkNaming";
304
308
  /** @import { Source } from "webpack-sources" */
305
309
  /** @import Compiler from "./Compiler" */
306
310
  /** @import Chunk, { ChunkFilenameTemplate, ChunkId } from "./Chunk" */
307
- /** @import ChunkGroup from "./ChunkGroup" */
308
- /** @import ChunkGraph from "./ChunkGraph" */
311
+ /** @import ChunkGraph, { ModuleId } from "./ChunkGraph" */
309
312
  /** @import Compilation from "./Compilation" */
310
313
  /** @import Dependency from "./Dependency" */
311
314
  /** @import ModuleGraph from "./ModuleGraph" */
@@ -422,6 +425,48 @@ function getGlobalObject(definition) {
422
425
  return `Object(${trimmed})`;
423
426
  }
424
427
 
428
+ // An async external is a promise, not a module record with an evaluating state.
429
+ const isAsyncExternal = (/** @type {Module} */ module) =>
430
+ Boolean(/** @type {BuildMeta} */ (module.buildMeta).async) &&
431
+ module instanceof getExternalModule();
432
+
433
+ // `createFakeNamespaceObject`'s "merge all properties" bit, the only one that
434
+ // changes which names the namespace it builds carries.
435
+ const MERGE_PROPERTIES_MODE = 2;
436
+
437
+ /**
438
+ * The imported module decides, never the importer: two importers of one
439
+ * module have to be handed the same namespace object.
440
+ * @param {Module} module the imported module
441
+ * @returns {boolean} true when its namespace has to be a spec exotic object
442
+ */
443
+ const usesSpecNamespaceObject = (module) =>
444
+ Boolean(module.buildInfo && module.buildInfo.specNamespaceObject);
445
+
446
+ /**
447
+ * A module with no ESM namespace of its own — JSON, text, CommonJS — has one
448
+ * synthesized at each import site, so there the importer decides.
449
+ * @param {Module=} originModule the module the import is emitted into
450
+ * @returns {boolean} true when a namespace synthesized here has to be a spec object
451
+ */
452
+ const synthesizesSpecNamespaceObject = (originModule) =>
453
+ originModule !== undefined && usesSpecNamespaceObject(originModule);
454
+
455
+ /**
456
+ * Names the record a synthesized namespace belongs to and the shape it is built in,
457
+ * so every importer of one module reaches the same object and a reloaded module is
458
+ * not handed the namespace of the exports it replaced.
459
+ * @param {ModuleId | null} moduleId id of the imported module
460
+ * @param {number} mode the createFakeNamespaceObject mode the namespace is built with
461
+ * @returns {string} the arguments identifying it, ready to append to the call
462
+ */
463
+ const synthesizedNamespaceOwner = (moduleId, mode) =>
464
+ moduleId === null
465
+ ? ""
466
+ : `, ${RuntimeGlobals.moduleCache}[${JSON.stringify(moduleId)}], ${
467
+ mode & MERGE_PROPERTIES_MODE
468
+ }`;
469
+
425
470
  class RuntimeTemplate {
426
471
  /**
427
472
  * Creates an instance of RuntimeTemplate.
@@ -441,8 +486,8 @@ class RuntimeTemplate {
441
486
  (getGlobalObject(outputOptions.globalObject));
442
487
  /** @type {string} */
443
488
  this.contentHashReplacement = "X".repeat(outputOptions.hashDigestLength);
444
- /** @type {boolean | undefined} */
445
- this._javascriptNamedWithoutContent = undefined;
489
+ /** @type {Set<string>} */
490
+ this._analyzableRepairedAssets = new Set();
446
491
  /** @type {WeakMap<Chunk, boolean>} */
447
492
  this._chunkNamedWithoutContent = new WeakMap();
448
493
  /** @type {WeakMap<Chunk, string>} */
@@ -461,10 +506,18 @@ class RuntimeTemplate {
461
506
  this._publicPathClimbText = undefined;
462
507
  /** @type {WeakMap<Chunk, Placement>} */
463
508
  this._placementByChunk = new WeakMap();
509
+ /** @type {WeakMap<Chunk, string>} */
510
+ this._hotUpdateUndoByChunk = new WeakMap();
511
+ /** @type {string | undefined} */
512
+ this._hotUpdateShapeText = undefined;
464
513
  /** @type {Map<string, string | null | undefined>} */
465
514
  this._entryBaseUriByRuntime = new Map();
466
515
  /** @type {WasmGroups | undefined} */
467
516
  this._wasmGroupsValue = undefined;
517
+ /** @type {string[] | undefined} */
518
+ this._analyzableBailoutSink = undefined;
519
+ /** @type {Module | undefined} */
520
+ this._analyzableGeneratingModule = undefined;
468
521
  /** @type {Map<string, boolean> | undefined} */
469
522
  this._wasmAnchorKnownByGroup = undefined;
470
523
  }
@@ -563,6 +616,10 @@ class RuntimeTemplate {
563
616
  return this.outputOptions.environment.generator;
564
617
  }
565
618
 
619
+ supportsTopLevelAwait() {
620
+ return this.outputOptions.environment.topLevelAwait;
621
+ }
622
+
566
623
  supportsOptionalChaining() {
567
624
  return this.outputOptions.environment.optionalChaining;
568
625
  }
@@ -611,6 +668,41 @@ class RuntimeTemplate {
611
668
  return this.outputOptions.environment.modulePreload;
612
669
  }
613
670
 
671
+ /**
672
+ * Whether the deferred fill rewrote a chunk whose hashed name would otherwise go
673
+ * stale, which `RealContentHashPlugin` reads to run — and to repair `[chunkhash]`
674
+ * names — where `optimization.realContentHash` did not ask for it.
675
+ * @returns {boolean} true once the fill has marked such a chunk
676
+ */
677
+ needsAnalyzableRepair() {
678
+ return this._analyzableRepairedAssets.size > 0;
679
+ }
680
+
681
+ /**
682
+ * The assets the deferred fill rewrote under a name that has to be repaired: an
683
+ * on-demand repair renames only these and whatever names them.
684
+ * @returns {ReadonlySet<string>} their names
685
+ */
686
+ analyzableRepairedAssets() {
687
+ return this._analyzableRepairedAssets;
688
+ }
689
+
690
+ /**
691
+ * The `devtool` value wrapping a javascript module in `eval(...)`, read through both
692
+ * spellings — a plain string, and the per-asset-type array.
693
+ * @returns {string | undefined} the value, or undefined when no devtool wraps modules
694
+ */
695
+ _evalWrappingDevtool() {
696
+ const { devtool } = this.compilation.options;
697
+ const items = Array.isArray(devtool)
698
+ ? devtool
699
+ : [{ type: "all", use: devtool }];
700
+ for (const { type, use } of items) {
701
+ if (type !== "all" && type !== "javascript") continue;
702
+ if (typeof use === "string" && use.includes("eval")) return use;
703
+ }
704
+ }
705
+
614
706
  /**
615
707
  * Whether a reference a foreign bundler can follow without running webpack's runtime
616
708
  * may be emitted — the one question every caller asks, in the form it is asking for:
@@ -660,7 +752,8 @@ class RuntimeTemplate {
660
752
  APIPlugin.runtimeUsesPublicPathOverride(
661
753
  this.compilation,
662
754
  scoped ? chunkGraph : undefined,
663
- placedModule
755
+ placedModule,
756
+ runtime
664
757
  )
665
758
  ) {
666
759
  return this._analyzableBailout(
@@ -672,36 +765,12 @@ class RuntimeTemplate {
672
765
 
673
766
  if (form === "import") {
674
767
  // Read through a native `import()`, or it is not this feature at all.
675
- if (outputOptions.chunkFormat !== "module") {
676
- return this._analyzableBailout(
677
- module,
678
- `output.chunkFormat is ${JSON.stringify(outputOptions.chunkFormat)}, so chunks are not read through a native import()`,
679
- false
680
- );
681
- }
682
- if (outputOptions.importFunctionName !== "import") {
683
- return this._analyzableBailout(
684
- module,
685
- `output.importFunctionName is ${JSON.stringify(outputOptions.importFunctionName)}, so the call site is not a native import()`,
686
- false
687
- );
688
- }
689
- // A worker loading its own chunks some other way keeps that runtime; one on
690
- // `import` uses the same ESM loader as the main graph, so it can be analyzable.
691
- for (const originChunk of /** @type {ChunkGraph} */ (
692
- chunkGraph
693
- ).getModuleChunksIterable(/** @type {Module} */ (placedModule))) {
694
- const entryOptions = originChunk.getEntryOptions();
695
- if (!entryOptions || !entryOptions.worker) continue;
696
- // `WorkerAndWorkletPlugin` always seeds this from `output.workerChunkLoading`.
697
- if (entryOptions.chunkLoading !== "import") {
698
- return this._analyzableBailout(
699
- module,
700
- `this worker loads its chunks with ${JSON.stringify(entryOptions.chunkLoading)}, not "import"`,
701
- false
702
- );
703
- }
768
+ const outputReason = this._chunkImportOutputReason();
769
+ if (outputReason !== null) {
770
+ return this._analyzableBailout(module, outputReason, false);
704
771
  }
772
+ // A worker loading its own chunks some other way needs no answer here: it gets
773
+ // a chunk loader of its own, and only the ESM one asks this.
705
774
  return true;
706
775
  }
707
776
 
@@ -714,15 +783,12 @@ class RuntimeTemplate {
714
783
  if (form === "url-inline") return true;
715
784
  // `eval` devtool wraps each module in `eval(...)`, where `import.meta` is a
716
785
  // syntax error. Runtime modules are emitted beside them, never wrapped.
717
- const { devtool } = this.compilation.options;
718
- if (
719
- form !== "url-runtime" &&
720
- typeof devtool === "string" &&
721
- devtool.includes("eval")
722
- ) {
786
+ const evalDevtool =
787
+ form === "url-runtime" ? undefined : this._evalWrappingDevtool();
788
+ if (evalDevtool !== undefined) {
723
789
  return this._analyzableBailout(
724
790
  module,
725
- `devtool ${JSON.stringify(devtool)} wraps the module in eval(), where import.meta does not parse`,
791
+ `devtool ${JSON.stringify(evalDevtool)} wraps the module in eval(), where import.meta does not parse`,
726
792
  false
727
793
  );
728
794
  }
@@ -740,14 +806,14 @@ class RuntimeTemplate {
740
806
 
741
807
  // The rest is the `"wasm"` form: whether the whole binary url can be spelled
742
808
  // here, rather than built at runtime under an `import.meta.url` base.
743
- const { publicPath, webassemblyModuleFilename } = outputOptions;
809
+ const { publicPath } = outputOptions;
744
810
  if (publicPath !== "auto") {
745
811
  // A public path that needs no base names the same place from the chunk as from
746
812
  // the document, so a literal spells what `fetch` would have reached.
747
813
  if (this._publicPathNeedsNoBase()) {
748
814
  // Settled no earlier than the hash it reads or is called with, so it is
749
815
  // baked only where the deferred pass may finish it.
750
- if (this._resolvePublicPathPrefix(publicPath, module) === null) {
816
+ if (this._resolvePublicPathPrefix(publicPath) === null) {
751
817
  return false;
752
818
  }
753
819
  } else if (
@@ -761,17 +827,6 @@ class RuntimeTemplate {
761
827
  );
762
828
  }
763
829
  }
764
- // The compilation hash is settled after code generation, so a name carrying one
765
- // is baked only where the deferred pass can fill it in. `[hash]` is the module's
766
- // own here, which code generation already knows.
767
- if (
768
- getTemplatedPathPlugin()
769
- .getPresentKinds(/** @type {string} */ (webassemblyModuleFilename))
770
- .has("fullhash") &&
771
- !this._canDeferOrBakeFullHash(true, undefined, module)
772
- ) {
773
- return false;
774
- }
775
830
  return true;
776
831
  }
777
832
 
@@ -790,38 +845,97 @@ class RuntimeTemplate {
790
845
  */
791
846
  _analyzableBailout(module, reason, answer) {
792
847
  if (module !== undefined && this.outputOptions.module) {
793
- const text = `Analyzable ESM bailout: ${reason}`;
848
+ // Recorded on the module whose code is being generated, which is the one a
849
+ // cached result is restored for — an inner module of a concatenation is not.
850
+ const target = this._analyzableGeneratingModule || module;
851
+ const text = `${ANALYZABLE_BAILOUT_PREFIX}${reason}`;
794
852
  const bailouts =
795
- this.compilation.moduleGraph.getOptimizationBailout(module);
853
+ this.compilation.moduleGraph.getOptimizationBailout(target);
796
854
  if (!bailouts.includes(text)) bailouts.push(text);
855
+ // Each generation records its own: another runtime's result may be all the
856
+ // cache restores later, so what the graph holds must not hide a reason.
857
+ if (this._analyzableGeneratingModule !== undefined) {
858
+ // The sink exists only once a reason is recorded, so a module that
859
+ // bakes every reference — nearly all of them — allocates nothing here.
860
+ const sink =
861
+ this._analyzableBailoutSink || (this._analyzableBailoutSink = []);
862
+ if (!sink.includes(reason)) sink.push(reason);
863
+ }
797
864
  }
798
865
  return answer;
799
866
  }
800
867
 
801
868
  /**
802
- * Whether a name that code generation cannot settle may be reserved as a stand-in
803
- * and filled in once the hashes exist. Substituting rewrites a chunk after its own
804
- * content hash was taken, so either `RealContentHashPlugin` has to bring the two
805
- * back in line, or no emitted javascript may be named by its content in the first
806
- * place — with a name like `[name].js` there is nothing to go stale. Asked of the
807
- * names the chunks actually carry rather than of `output`, so a template nothing is
808
- * emitted under does not rule the rewrite out and a `chunk.filenameTemplate` does
809
- * not slip past it.
810
- * @param {Iterable<Chunk>=} chunks the chunks the stand-in is written into; omit
811
- * where the answer has to hold for the whole compilation, as it does for a gate a
812
- * runtime module asks from the other end
813
- * @returns {boolean} true when deferring is safe
869
+ * Names the module whose code generation starts, so the reasons its references
870
+ * record are handed back by `finishAnalyzableBailouts` and attributed to it.
871
+ * @param {Module} module the module being generated
872
+ * @returns {void}
814
873
  */
815
- _canDeferAnalyzableName(chunks) {
816
- if (chunks === undefined) {
817
- // Memoized: the chunk graph is settled before any caller asks compilation-wide.
818
- if (this._javascriptNamedWithoutContent === undefined) {
819
- this._javascriptNamedWithoutContent = this._canDeferAnalyzableName(
820
- this.compilation.chunks
821
- );
874
+ beginAnalyzableBailouts(module) {
875
+ this._analyzableGeneratingModule = module;
876
+ }
877
+
878
+ /**
879
+ * Ends the collection `beginAnalyzableBailouts` started, so the reasons can be
880
+ * stored beside the result and reapplied when the cache restores it.
881
+ * @returns {string[] | undefined} the reasons recorded since, or undefined for none
882
+ */
883
+ finishAnalyzableBailouts() {
884
+ const sink = this._analyzableBailoutSink;
885
+ this._analyzableGeneratingModule = undefined;
886
+ this._analyzableBailoutSink = undefined;
887
+ return sink;
888
+ }
889
+
890
+ /**
891
+ * Reapplies the bailouts a cached code generation result was stored with, so a
892
+ * restored module reports the same reasons a generated one does.
893
+ * @param {Module} module the module the result was restored for
894
+ * @param {string[]} reasons the reasons stored beside it
895
+ * @returns {void}
896
+ */
897
+ restoreAnalyzableBailouts(module, reasons) {
898
+ const bailouts =
899
+ this.compilation.moduleGraph.getOptimizationBailout(module);
900
+ for (const reason of reasons) {
901
+ const text = `${ANALYZABLE_BAILOUT_PREFIX}${reason}`;
902
+ if (!bailouts.includes(text)) bailouts.push(text);
903
+ }
904
+ }
905
+
906
+ /**
907
+ * The reasons a module's references keep the runtime form, as recorded while its
908
+ * code was generated.
909
+ * @param {Module} module the module, a runtime module included
910
+ * @returns {string[]} the reasons, none where every reference bakes
911
+ */
912
+ analyzableBailoutsOf(module) {
913
+ /** @type {string[]} */
914
+ const reasons = [];
915
+ for (const text of this.compilation.moduleGraph.getOptimizationBailout(
916
+ module
917
+ )) {
918
+ if (
919
+ typeof text === "string" &&
920
+ text.startsWith(ANALYZABLE_BAILOUT_PREFIX)
921
+ ) {
922
+ reasons.push(text.slice(ANALYZABLE_BAILOUT_PREFIX.length));
822
923
  }
823
- return this._javascriptNamedWithoutContent;
824
924
  }
925
+ return reasons;
926
+ }
927
+
928
+ /**
929
+ * Whether a name that code generation cannot settle may be reserved as a stand-in
930
+ * and filled in once the hashes exist without a repair behind it. Substituting
931
+ * rewrites a chunk after its own content hash was taken, so either no emitted
932
+ * javascript may be named by its content — with a name like `[name].js` there is
933
+ * nothing to go stale — or `RealContentHashPlugin` has to bring the two back in
934
+ * line, which is what a name this says no to gets: see `_needsRepair`.
935
+ * @param {Iterable<Chunk>} chunks the chunks the stand-in is written into
936
+ * @returns {boolean} true when no name moves with the bytes the fill leaves
937
+ */
938
+ _canDeferAnalyzableName(chunks) {
825
939
  let found = false;
826
940
  for (const chunk of chunks) {
827
941
  found = true;
@@ -832,15 +946,15 @@ class RuntimeTemplate {
832
946
  }
833
947
 
834
948
  /**
835
- * `_canDeferAnalyzableName`, recording why not when the answer is no — what every
836
- * caller with a module to report against does with it.
837
- * @param {Iterable<Chunk>=} chunks the chunks the stand-in is written into
838
- * @param {Module=} module the module the reference is emitted into
839
- * @returns {boolean} true when deferring is safe
949
+ * Whether a stand-in written into `chunks` needs the repair pass behind it: their
950
+ * hashed names would otherwise go stale once the fill moves their bytes. The pass
951
+ * is `RealContentHashPlugin`, run on demand where `optimization.realContentHash`
952
+ * did not ask for it, and repairing `[chunkhash]` names too while it is at it.
953
+ * @param {Iterable<Chunk>} chunks the chunks the stand-in is written into
954
+ * @returns {boolean} true when the fill has to mark the name for repair
840
955
  */
841
- _canDeferOrBail(chunks, module) {
842
- if (this._canDeferAnalyzableName(chunks)) return true;
843
- return this._analyzableBailout(module, DEFER_BAILOUT, false);
956
+ _needsRepair(chunks) {
957
+ return !this._canDeferAnalyzableName(chunks);
844
958
  }
845
959
 
846
960
  /**
@@ -848,37 +962,28 @@ class RuntimeTemplate {
848
962
  * and a stand-in for the deferred pass where they do not.
849
963
  * @param {SpecifierPart[]} parts the whole specifier
850
964
  * @param {Iterable<Chunk>} chunks the chunks it is written into
851
- * @param {Module=} module the module the reference is emitted into
852
- * @returns {string | null} it already quoted, or `null` when no stand-in may be
853
- * reserved
965
+ * @returns {string} it already quoted
854
966
  */
855
- _specifierOf(parts, chunks, module) {
967
+ _specifierOf(parts, chunks) {
856
968
  const text = literalText(parts);
857
969
  if (text !== null) return toJsStringLiteral(text);
858
- if (
859
- !this._canDeferOrBakeFullHash(
860
- this._partsBakeFullHash(parts),
861
- chunks,
862
- module
863
- )
864
- ) {
865
- return null;
866
- }
867
- return toJsStringLiteral(this._reserveAnalyzableSpecifier(parts));
970
+ // One built out of the compilation hash moves its chunk into the round after
971
+ // that hash, where the name is taken from the filled bytes anyway.
972
+ const repair = !this._partsBakeFullHash(parts) && this._needsRepair(chunks);
973
+ return toJsStringLiteral(
974
+ this._reserveAnalyzableSpecifier(this._markedForRepair(repair, parts))
975
+ );
868
976
  }
869
977
 
870
978
  /**
871
- * `_canDeferOrBail` for a name the fill builds out of the compilation hash. Such a
872
- * stand-in settles the name it lands in for us: `_markChunksBakingFullHash` reads it
873
- * back and moves that chunk into the round after that hash, which is the same place a
874
- * chunk reaching for `__webpack_require__.p` ends up without asking.
875
- * @param {boolean} carriesFullHash whether the fill builds this one out of that hash
876
- * @param {Iterable<Chunk>=} chunks the chunks the stand-in is written into
877
- * @param {Module=} module the module the reference is emitted into
878
- * @returns {boolean} true when deferring is safe
979
+ * The parts a stand-in is reserved with, marked where the name it lands in has to
980
+ * be repaired after the fill.
981
+ * @param {boolean} repair what `_needsRepair` answered
982
+ * @param {SpecifierPart[]} parts what the stand-in resolves to
983
+ * @returns {SpecifierPart[]} the parts, marked where the repair is needed
879
984
  */
880
- _canDeferOrBakeFullHash(carriesFullHash, chunks, module) {
881
- return carriesFullHash || this._canDeferOrBail(chunks, module);
985
+ _markedForRepair(repair, parts) {
986
+ return repair ? [["repair", ""], ...parts] : parts;
882
987
  }
883
988
 
884
989
  /**
@@ -1017,7 +1122,7 @@ class RuntimeTemplate {
1017
1122
  );
1018
1123
  }
1019
1124
  const chunks = this._moduleChunks(module, chunkGraph);
1020
- const parts = this._resolvePublicPathPrefix(publicPath, module, chunks);
1125
+ const parts = this._resolvePublicPathPrefix(publicPath);
1021
1126
  if (parts === null) return null;
1022
1127
  // A protocol-relative base names a host but no scheme, and the runtime reads it
1023
1128
  // against the chunk's own url to get one — the very url this literal is
@@ -1026,8 +1131,7 @@ class RuntimeTemplate {
1026
1131
  if (base.startsWith("//")) {
1027
1132
  return this._specifierOf(
1028
1133
  [["literal", base], ...rootedParts(parts), ["literal", filename]],
1029
- chunks,
1030
- module
1134
+ chunks
1031
1135
  );
1032
1136
  }
1033
1137
  if (!isAbsoluteBaseUri(base)) {
@@ -1059,13 +1163,14 @@ class RuntimeTemplate {
1059
1163
  );
1060
1164
  }
1061
1165
  if (text !== null) return toJsStringLiteral(resolved.href);
1062
- if (!this._canDeferOrBail(chunks, module)) return null;
1063
1166
  return toJsStringLiteral(
1064
- this._reserveAnalyzableSpecifier([
1065
- ["base", base],
1066
- ...parts,
1067
- ["literal", filename]
1068
- ])
1167
+ this._reserveAnalyzableSpecifier(
1168
+ this._markedForRepair(this._needsRepair(chunks), [
1169
+ ["base", base],
1170
+ ...parts,
1171
+ ["literal", filename]
1172
+ ])
1173
+ )
1069
1174
  );
1070
1175
  }
1071
1176
 
@@ -1862,14 +1967,21 @@ class RuntimeTemplate {
1862
1967
  });
1863
1968
  const exportsType = module.getExportsType(chunkGraph.moduleGraph, strict);
1864
1969
  switch (exportsType) {
1865
- case "namespace":
1866
- return this.moduleRaw({
1970
+ case "namespace": {
1971
+ const raw = this.moduleRaw({
1867
1972
  module,
1868
1973
  chunkGraph,
1869
1974
  request,
1870
1975
  weak,
1871
1976
  runtimeRequirements
1872
1977
  });
1978
+
1979
+ if (!usesSpecNamespaceObject(module)) return raw;
1980
+
1981
+ runtimeRequirements.add(RuntimeGlobals.specNamespaceObject);
1982
+
1983
+ return `${RuntimeGlobals.specNamespaceObject}(${raw})`;
1984
+ }
1873
1985
  case "default-with-named":
1874
1986
  runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
1875
1987
  return `${RuntimeGlobals.createFakeNamespaceObject}(${moduleId}, 3)`;
@@ -1978,12 +2090,30 @@ class RuntimeTemplate {
1978
2090
 
1979
2091
  let mode = getMakeDeferredNamespaceModeFromExportsType(exportsType);
1980
2092
  if (mode) mode = `${mode} | 16`;
2093
+ // The spec's namespace keys are the sorted exports, so hand them over
2094
+ // when they are fully known; otherwise the runtime stays permissive.
2095
+ const deferredExportNames = getKnownExportNames(
2096
+ chunkGraph.moduleGraph,
2097
+ module,
2098
+ exportsType
2099
+ );
2100
+ runtimeRequirements.add(
2101
+ deferredExportNames
2102
+ ? RuntimeGlobals.hasSealedDeferredNamespace
2103
+ : RuntimeGlobals.hasSynthesizedDeferredNamespace
2104
+ );
2105
+ mode += deferredExportNames
2106
+ ? `, ${JSON.stringify(deferredExportNames)}`
2107
+ : "";
1981
2108
 
1982
2109
  const asyncDeps = Array.from(
1983
2110
  getOutgoingAsyncModules(chunkGraph.moduleGraph, module),
1984
2111
  (m) => chunkGraph.getModuleId(m)
1985
2112
  ).filter((id) => id !== null);
1986
2113
  if (asyncDeps.length) {
2114
+ runtimeRequirements.add(
2115
+ RuntimeGlobals.deferredModuleAsyncTransitiveDependencies
2116
+ );
1987
2117
  if (header) {
1988
2118
  appending = `.then(${this.basicFunction(
1989
2119
  "",
@@ -2018,15 +2148,22 @@ class RuntimeTemplate {
2018
2148
  } else {
2019
2149
  let fakeType = 16;
2020
2150
  switch (exportsType) {
2021
- case "namespace":
2022
- if (header) {
2023
- const rawModule = this.moduleRaw({
2151
+ case "namespace": {
2152
+ const specNamespace = usesSpecNamespaceObject(module);
2153
+ const asyncModule = chunkGraph.moduleGraph.isAsync(module);
2154
+ if (header || (specNamespace && !asyncModule)) {
2155
+ let rawModule = this.moduleRaw({
2024
2156
  module,
2025
2157
  chunkGraph,
2026
2158
  request,
2027
2159
  weak,
2028
2160
  runtimeRequirements
2029
2161
  });
2162
+ if (specNamespace && !asyncModule) {
2163
+ runtimeRequirements.add(RuntimeGlobals.specNamespaceObject);
2164
+ // Wrap before promise resolution assimilates an exported `then`.
2165
+ rawModule = `${RuntimeGlobals.specNamespaceObject}(${rawModule})`;
2166
+ }
2030
2167
  appending = `.then(${this.basicFunction(
2031
2168
  "",
2032
2169
  `${header}return ${rawModule};`
@@ -2038,7 +2175,14 @@ class RuntimeTemplate {
2038
2175
  `${comment}${idExpr}`
2039
2176
  )})`;
2040
2177
  }
2178
+
2179
+ if (specNamespace && asyncModule) {
2180
+ runtimeRequirements.add(RuntimeGlobals.specNamespaceObject);
2181
+ appending += `.then(${RuntimeGlobals.specNamespaceObject})`;
2182
+ }
2183
+
2041
2184
  break;
2185
+ }
2042
2186
  case "dynamic":
2043
2187
  fakeType |= 4;
2044
2188
  /* fall through */
@@ -2092,6 +2236,15 @@ class RuntimeTemplate {
2092
2236
  )})`;
2093
2237
  }
2094
2238
  }
2239
+ if (synthesizesSpecNamespaceObject(originModule)) {
2240
+ runtimeRequirements.add(RuntimeGlobals.specNamespaceObject);
2241
+ runtimeRequirements.add(RuntimeGlobals.moduleCache);
2242
+ const owner = synthesizedNamespaceOwner(moduleId, fakeType);
2243
+ appending += `.then(${this.returningFunction(
2244
+ `${RuntimeGlobals.specNamespaceObject}(m${owner})`,
2245
+ "m"
2246
+ )})`;
2247
+ }
2095
2248
  break;
2096
2249
  }
2097
2250
  }
@@ -2220,11 +2373,14 @@ class RuntimeTemplate {
2220
2373
  (dependency instanceof HarmonyImportDependency ||
2221
2374
  dependency instanceof ImportDependency) &&
2222
2375
  ImportPhaseUtils.isDefer(dependency.phase) &&
2223
- !(/** @type {BuildMeta} */ (module.buildMeta).async);
2376
+ !isAsyncExternal(module);
2224
2377
 
2225
2378
  if (isModuleDeferred) {
2226
2379
  /** @type {Set<Module>} */
2227
2380
  const outgoingAsyncModules = getOutgoingAsyncModules(moduleGraph, module);
2381
+ // A module deferring itself is already evaluating, so awaiting it here
2382
+ // would deadlock; forcing the namespace throws instead.
2383
+ outgoingAsyncModules.delete(originModule);
2228
2384
 
2229
2385
  importContent = `/* deferred harmony import */ ${optDeclaration}${importVar} = ${getOptimizedDeferredModule(
2230
2386
  moduleId,
@@ -2309,7 +2465,7 @@ class RuntimeTemplate {
2309
2465
  (dependency instanceof HarmonyImportDependency ||
2310
2466
  dependency instanceof ImportDependency) &&
2311
2467
  ImportPhaseUtils.isDefer(dependency.phase) &&
2312
- !(/** @type {BuildMeta} */ (module.buildMeta).async);
2468
+ !isAsyncExternal(module);
2313
2469
 
2314
2470
  if (defaultInterop) {
2315
2471
  // when the defaultInterop is used (when a ESM imports a CJS module),
@@ -2395,16 +2551,38 @@ class RuntimeTemplate {
2395
2551
  `${importVar}_namespace_cache`
2396
2552
  )
2397
2553
  );
2554
+ const fakeMode = exportsType === "default-only" ? 0 : 2;
2555
+ const fakeArguments = fakeMode === 0 ? "" : ", 2";
2556
+ let fakeNamespace = `${RuntimeGlobals.createFakeNamespaceObject}(${importVar}${fakeArguments})`;
2557
+ if (synthesizesSpecNamespaceObject(originModule)) {
2558
+ runtimeRequirements.add(RuntimeGlobals.specNamespaceObject);
2559
+ runtimeRequirements.add(RuntimeGlobals.moduleCache);
2560
+ const owner = synthesizedNamespaceOwner(
2561
+ chunkGraph.getModuleId(module),
2562
+ fakeMode
2563
+ );
2564
+ fakeNamespace = `${RuntimeGlobals.specNamespaceObject}(${fakeNamespace}${owner})`;
2565
+ }
2398
2566
  return `/*#__PURE__*/ ${
2399
2567
  asiSafe ? "" : asiSafe === false ? ";" : "Object"
2400
- }(${importVar}_namespace_cache || (${importVar}_namespace_cache = ${
2401
- RuntimeGlobals.createFakeNamespaceObject
2402
- }(${importVar}${exportsType === "default-only" ? "" : ", 2"})))`;
2568
+ }(${importVar}_namespace_cache || (${importVar}_namespace_cache = ${fakeNamespace}))`;
2403
2569
  }
2404
2570
  }
2405
2571
 
2406
2572
  if (exportName.length > 0) {
2407
2573
  const exportsInfo = moduleGraph.getExportsInfo(module);
2574
+ // A name the module does not export is the namespace's to answer — reading
2575
+ // it is undefined and writing it fails — not the exports object's.
2576
+ if (
2577
+ !isModuleDeferred &&
2578
+ usesSpecNamespaceObject(module) &&
2579
+ exportsInfo.isExportProvided(exportName) === false
2580
+ ) {
2581
+ runtimeRequirements.add(RuntimeGlobals.specNamespaceObject);
2582
+ const ns = `${RuntimeGlobals.specNamespaceObject}(${importVar})`;
2583
+
2584
+ return `${ns}${propertyAccess(exportName)}`;
2585
+ }
2408
2586
  // in some case the exported item is renamed (get this by getUsedName). for example,
2409
2587
  // x.default might be emitted as x.Z (default is renamed to Z)
2410
2588
  const used = exportsInfo.getUsedName(exportName, runtime);
@@ -2449,9 +2627,19 @@ class RuntimeTemplate {
2449
2627
  runtimeRequirements.add(RuntimeGlobals.makeDeferredNamespaceObject);
2450
2628
  const id = chunkGraph.getModuleId(module);
2451
2629
  const type = getMakeDeferredNamespaceModeFromExportsType(exportsType);
2452
- const init = `${
2453
- RuntimeGlobals.makeDeferredNamespaceObject
2454
- }(${JSON.stringify(id)}, ${type})`;
2630
+ const names = getKnownExportNames(
2631
+ chunkGraph.moduleGraph,
2632
+ module,
2633
+ exportsType
2634
+ );
2635
+ runtimeRequirements.add(
2636
+ names
2637
+ ? RuntimeGlobals.hasSealedDeferredNamespace
2638
+ : RuntimeGlobals.hasSynthesizedDeferredNamespace
2639
+ );
2640
+ const init = `${RuntimeGlobals.makeDeferredNamespaceObject}(${JSON.stringify(
2641
+ id
2642
+ )}, ${type}${names ? `, ${JSON.stringify(names)}` : ""})`;
2455
2643
 
2456
2644
  return `/*#__PURE__*/ ${
2457
2645
  asiSafe ? "" : asiSafe === false ? ";" : "Object"
@@ -2478,6 +2666,16 @@ class RuntimeTemplate {
2478
2666
  // if we hit here, the importVar is either
2479
2667
  // - already a ES module namespace object
2480
2668
  // - or imported by a way that does not need interop.
2669
+ if (
2670
+ exportsType === "namespace" &&
2671
+ exportName.length === 0 &&
2672
+ usesSpecNamespaceObject(module)
2673
+ ) {
2674
+ runtimeRequirements.add(RuntimeGlobals.specNamespaceObject);
2675
+
2676
+ return `${RuntimeGlobals.specNamespaceObject}(${importVar})`;
2677
+ }
2678
+
2481
2679
  return importVar;
2482
2680
  }
2483
2681
 
@@ -2558,7 +2756,7 @@ class RuntimeTemplate {
2558
2756
  * @param {string} options.message the message
2559
2757
  * @param {ChunkGraph} options.chunkGraph the chunk graph
2560
2758
  * @param {RuntimeRequirements} options.runtimeRequirements if set, will be filled with runtime requirements
2561
- * @param {Module=} options.originModule the module the `import()` is emitted into
2759
+ * @param {Module=} options.originModule the module the `import()` is emitted into, to report against
2562
2760
  * @returns {string} expression
2563
2761
  */
2564
2762
  blockPromise({
@@ -2588,156 +2786,32 @@ class RuntimeTemplate {
2588
2786
  message,
2589
2787
  chunkName: block.chunkName
2590
2788
  });
2591
- // `fetchPriority` is unsupported for ESM output (a native `import()` can't carry it,
2592
- // and the ESM chunk loader ignores the argument), so it never blocks the analyzable form.
2593
- // TODO recheck: browsers honor `fetchpriority` on `modulepreload` only when the
2594
- // preload scanner sees it, so a runtime-injected hint is inert; support it here if
2595
- // that changes.
2596
- const fetchPriority = chunkGroup.options.fetchPriority;
2597
- if (chunks.length === 1) {
2598
- const analyzable = this.analyzableChunkImport(
2599
- chunks[0],
2600
- comment,
2601
- runtimeRequirements,
2602
- originModule,
2603
- chunkGraph
2604
- );
2605
- if (analyzable !== null) {
2606
- return analyzable;
2607
- }
2608
- const chunkId = JSON.stringify(chunks[0].id);
2609
- runtimeRequirements.add(RuntimeGlobals.ensureChunk);
2610
-
2611
- if (fetchPriority) {
2612
- runtimeRequirements.add(RuntimeGlobals.hasFetchPriority);
2613
- }
2614
-
2615
- return `${RuntimeGlobals.ensureChunk}(${comment}${chunkId}${
2616
- fetchPriority ? `, ${JSON.stringify(fetchPriority)}` : ""
2617
- })`;
2618
- } else if (chunks.length > 0) {
2619
- let needEnsureChunk = false;
2620
- /**
2621
- * Analyzable `import()` for a solely-owned JS chunk, else runtime ensureChunk.
2622
- * @param {Chunk} chunk chunk
2623
- * @returns {string} require chunk id code
2624
- */
2625
- const requireChunkId = (chunk) => {
2626
- const analyzable = this.analyzableChunkImport(
2627
- chunk,
2628
- "",
2629
- runtimeRequirements,
2630
- originModule,
2631
- chunkGraph
2632
- );
2633
- if (analyzable !== null) return analyzable;
2634
- needEnsureChunk = true;
2635
- return `${RuntimeGlobals.ensureChunk}(${JSON.stringify(chunk.id)}${
2636
- fetchPriority ? `, ${JSON.stringify(fetchPriority)}` : ""
2637
- })`;
2638
- };
2639
- const items = chunks.map(requireChunkId);
2640
- if (needEnsureChunk) {
2641
- runtimeRequirements.add(RuntimeGlobals.ensureChunk);
2642
- // Only needed when an `ensureChunk(id, priority)` call is actually emitted.
2643
- if (fetchPriority) {
2644
- runtimeRequirements.add(RuntimeGlobals.hasFetchPriority);
2645
- }
2646
- }
2647
- return `Promise.all(${comment.trim()}[${items.join(", ")}])`;
2648
- }
2649
- return `Promise.resolve(${comment.trim()})`;
2650
- }
2651
-
2652
- /**
2653
- * Whether any module of `chunk` carries a source type with a chunk handler of its
2654
- * own, which `.ei` dispatches alongside the javascript one.
2655
- * @param {Chunk} chunk the chunk being imported
2656
- * @param {ChunkGraph} chunkGraph the chunk graph
2657
- * @returns {boolean} true when a handler beyond the javascript one is reached
2658
- */
2659
- _chunkLoadsBeyondJs(chunk, chunkGraph) {
2660
- for (const module of chunkGraph.getChunkModulesIterable(chunk)) {
2661
- for (const type of chunkGraph.getModuleSourceTypes(module)) {
2662
- if (!TYPES_WITHOUT_CHUNK_HANDLER.has(type)) return true;
2789
+ // An output that cannot use a native `import()` gets no ESM chunk loader to ask,
2790
+ // so the reason is recorded here, where any build generates its imports.
2791
+ if (this.outputOptions.module && originModule !== undefined) {
2792
+ const outputReason = this._chunkImportOutputReason();
2793
+ if (outputReason !== null) {
2794
+ this._analyzableBailout(originModule, outputReason, false);
2663
2795
  }
2664
2796
  }
2665
- return false;
2666
- }
2667
-
2668
- /**
2669
- * For ESM module output, load a single statically-named chunk through the
2670
- * `analyzableChunkImport` helper — a literal `import("./chunk.js")` other bundlers
2671
- * and webpack itself can follow, wrapped to keep `ensureChunk` timing and deduplication.
2672
- * Returns `null` to fall back to the runtime `ensureChunk` form.
2673
- * @param {Chunk} chunk the chunk to load
2674
- * @param {string} comment leading comment (chunk name / message)
2675
- * @param {RuntimeRequirements} runtimeRequirements runtime requirements
2676
- * @param {Module | undefined} originModule the module the `import()` is emitted into
2677
- * @param {ChunkGraph} chunkGraph the chunk graph
2678
- * @returns {string | null} the import expression, or `null`
2679
- */
2680
- analyzableChunkImport(
2681
- chunk,
2682
- comment,
2683
- runtimeRequirements,
2684
- originModule,
2685
- chunkGraph
2686
- ) {
2687
- // `blockPromise` has already dropped any chunk without an id. Without an origin
2688
- // there is nothing the specifier could be relative to.
2689
- if (
2690
- !originModule ||
2691
- !this.supportsAnalyzable("import", chunkGraph, originModule)
2692
- ) {
2693
- return null;
2797
+ const fetchPriority = chunkGroup.options.fetchPriority;
2798
+ const priorityArg = fetchPriority
2799
+ ? `, ${JSON.stringify(fetchPriority)}`
2800
+ : "";
2801
+ if (chunks.length === 0) return `Promise.resolve(${comment.trim()})`;
2802
+ runtimeRequirements.add(RuntimeGlobals.ensureChunk);
2803
+ if (fetchPriority) {
2804
+ runtimeRequirements.add(RuntimeGlobals.hasFetchPriority);
2694
2805
  }
2695
- // `.ei` always performs the import, where `.e` only reaches the javascript
2696
- // loader when there is javascript to load. A module federation remote is the
2697
- // case that matters: its chunk is emitted by the container's build, not this one.
2698
- if (!JavascriptModulesPlugin.chunkHasJs(chunk, chunkGraph)) {
2699
- return this._analyzableBailout(
2700
- originModule,
2701
- "this compilation emits no javascript for the chunk, so there is nothing to import",
2702
- null
2703
- );
2806
+ if (chunks.length === 1) {
2807
+ const chunkId = JSON.stringify(chunks[0].id);
2808
+ return `${RuntimeGlobals.ensureChunk}(${comment}${chunkId}${priorityArg})`;
2704
2809
  }
2705
- // Relative to the consuming chunk (`import.meta.url`) for `auto`, else an
2706
- // absolute publicPath prefix.
2707
- const specifier = this._getAnalyzableChunkSpecifier(
2708
- undefined,
2709
- chunk,
2710
- originModule,
2711
- chunkGraph
2810
+ const items = chunks.map(
2811
+ (chunk) =>
2812
+ `${RuntimeGlobals.ensureChunk}(${JSON.stringify(chunk.id)}${priorityArg})`
2712
2813
  );
2713
- if (specifier === null) {
2714
- return null;
2715
- }
2716
- // `import()` needs a resolvable specifier — relative (`./`, `../`), absolute (`/`)
2717
- // or a URL scheme. A bare one is a package name, so make it explicitly relative
2718
- // the way the chunk loader does. (`new URL(...)` callers accept bare vs their base.)
2719
- const resolvableSpecifier = /^"(?:\.{0,2}\/|[a-zA-Z][\w+.-]*:)/.test(
2720
- specifier
2721
- )
2722
- ? specifier
2723
- : `"./${specifier.slice(1)}`;
2724
- runtimeRequirements.add(RuntimeGlobals.analyzableChunkImport);
2725
- // Prefetch/preload children and every non-javascript source type are loaded by a
2726
- // `.f` handler, which `.ei` dispatches — but they attach to `.f`, so it has to
2727
- // exist. The runtime `ensureChunk` around it does not.
2728
- if (
2729
- chunk.hasChildByOrder(chunkGraph, "prefetch", true) ||
2730
- chunk.hasChildByOrder(chunkGraph, "preload", true) ||
2731
- chunk.hasChildByOrder(chunkGraph, "cssPreload", true) ||
2732
- this._chunkLoadsBeyondJs(chunk, chunkGraph)
2733
- ) {
2734
- runtimeRequirements.add(RuntimeGlobals.ensureChunkHandlers);
2735
- }
2736
- // Drop-in for `ensureChunk(id)`: the literal `import()` can be statically
2737
- // followed, the helper keeps webpack's install timing and deduplication.
2738
- return `${RuntimeGlobals.analyzableChunkImport}(${JSON.stringify(
2739
- chunk.id
2740
- )}, ${this.returningFunction(`import(${comment}${resolvableSpecifier})`)})`;
2814
+ return `Promise.all(${comment.trim()}[${items.join(", ")}])`;
2741
2815
  }
2742
2816
 
2743
2817
  /**
@@ -2773,6 +2847,17 @@ class RuntimeTemplate {
2773
2847
  const late = this._settlesLate(chunk);
2774
2848
  if (late !== this._settlesLate(target)) return !late;
2775
2849
  }
2850
+ return this._hashRoundBefore(chunk, target);
2851
+ }
2852
+
2853
+ /**
2854
+ * Whether the four rounds alone settle `chunk` before `target`, leaving out what the
2855
+ * full-hash marks move: the order a runtime module generated in its chunk's round sees.
2856
+ * @param {Chunk} chunk the chunk whose hash would be read
2857
+ * @param {Chunk} target the chunk reading it
2858
+ * @returns {boolean} true when the hash exists by the time the target is hashed
2859
+ */
2860
+ _hashRoundBefore(chunk, target) {
2776
2861
  const chunkGraph = /** @type {ChunkGraph} */ (this.compilation.chunkGraph);
2777
2862
  const round = hashRound(chunk, chunkGraph);
2778
2863
  const targetRound = hashRound(target, chunkGraph);
@@ -2795,6 +2880,42 @@ class RuntimeTemplate {
2795
2880
  );
2796
2881
  }
2797
2882
 
2883
+ /**
2884
+ * Whether a `[chunkhash]`/`[contenthash]` name is settled before every chunk it is
2885
+ * written into, so a runtime module generated in its chunk's round can spell it.
2886
+ * @param {Chunk} chunk the chunk named
2887
+ * @param {string} template what names it
2888
+ * @param {Iterable<Chunk>} targets the chunks the name is written into
2889
+ * @returns {boolean} true when the hashes it reads exist by then
2890
+ */
2891
+ _hashedNameSettledBefore(chunk, template, targets) {
2892
+ const kinds = getTemplatedPathPlugin().getPresentKinds(template);
2893
+ // The compilation hash exists in no round a chunk is hashed in.
2894
+ if (kinds.has("fullhash") || kinds.has("hash")) return false;
2895
+ for (const target of targets) {
2896
+ if (!this._hashRoundBefore(chunk, target)) return false;
2897
+ }
2898
+ return true;
2899
+ }
2900
+
2901
+ /**
2902
+ * Whether the hashes a name reads are on the chunk now, which they are only once its
2903
+ * round of `createHash` has run — never while modules are being generated.
2904
+ * @param {Chunk} chunk the chunk named
2905
+ * @param {string} template what names it
2906
+ * @param {string} sourceType which asset of the chunk it names
2907
+ * @returns {boolean} true when the name can be resolved here
2908
+ */
2909
+ _hashedNamePresent(chunk, template, sourceType) {
2910
+ const kinds = getTemplatedPathPlugin().getPresentKinds(template);
2911
+ return (
2912
+ !kinds.has("fullhash") &&
2913
+ !kinds.has("hash") &&
2914
+ (!kinds.has("chunkhash") || chunk.hash !== undefined) &&
2915
+ (!kinds.has("contenthash") || chunk.contentHash[sourceType] !== undefined)
2916
+ );
2917
+ }
2918
+
2798
2919
  /**
2799
2920
  * Whether `createHash` settles this chunk's hash in the round that runs after the
2800
2921
  * compilation hash rather than in one of the four before it. Asked of the chunk
@@ -3056,35 +3177,6 @@ class RuntimeTemplate {
3056
3177
  this._chunksByIdForFold = undefined;
3057
3178
  }
3058
3179
 
3059
- /**
3060
- * Whether `chunk` can reach any of `targets` by following chunk groups, which is
3061
- * what turns a baked hashed name into a hash that depends on itself.
3062
- * @param {Chunk} chunk the referenced chunk
3063
- * @param {Iterable<Chunk>} targets the chunks the reference is written into
3064
- * @returns {boolean} true when one of them is reachable from `chunk`
3065
- */
3066
- _reachesAny(chunk, targets) {
3067
- const wanted = new Set(targets);
3068
- if (wanted.size === 0) return false;
3069
- if (wanted.has(chunk)) return true;
3070
- /** @type {Set<ChunkGroup>} */
3071
- const seen = new Set();
3072
- /** @type {ChunkGroup[]} */
3073
- const queue = [...chunk.groupsIterable];
3074
- for (const group of queue) seen.add(group);
3075
- for (let i = 0; i < queue.length; i++) {
3076
- for (const child of queue[i].childrenIterable) {
3077
- if (seen.has(child)) continue;
3078
- seen.add(child);
3079
- for (const candidate of child.chunks) {
3080
- if (wanted.has(candidate)) return true;
3081
- }
3082
- queue.push(child);
3083
- }
3084
- }
3085
- return false;
3086
- }
3087
-
3088
3180
  /**
3089
3181
  * The chunks a reference emitted into `module` is written into — the assets a
3090
3182
  * stand-in of ours would land in, and so the only ones a deferred fill could go
@@ -3200,7 +3292,7 @@ class RuntimeTemplate {
3200
3292
  * The `../` path from a chunk's own asset back to the output root. Hashes are
3201
3293
  * neutralized first: a runtime module is generated once to be hashed, before any
3202
3294
  * hash exists, so resolving one there throws — and `RuntimeModule.updateHash`
3203
- * swallows that, pinning the module's hash to the message.
3295
+ * lets that throw fail the build rather than hashing it.
3204
3296
  * @param {Chunk} chunk the chunk whose asset holds the reference
3205
3297
  * @param {boolean} enforceRelative whether the answer keeps a leading `./`
3206
3298
  * @returns {string} the path back to the output root
@@ -3231,7 +3323,10 @@ class RuntimeTemplate {
3231
3323
  * @returns {Placement} where a literal in it is read from
3232
3324
  */
3233
3325
  _modulePlacement(module, chunkGraph) {
3234
- return this._placementOf(this._moduleChunks(module, chunkGraph));
3326
+ return this._placementOf(
3327
+ this._moduleChunks(module, chunkGraph),
3328
+ isHotModule(module)
3329
+ );
3235
3330
  }
3236
3331
 
3237
3332
  /**
@@ -3250,11 +3345,12 @@ class RuntimeTemplate {
3250
3345
 
3251
3346
  /**
3252
3347
  * The one answer every chunk in `chunks` agrees on, with either half `null` where
3253
- * they do not.
3348
+ * they do not — under HMR the update chunk, which re-runs the module, has a say too.
3254
3349
  * @param {Iterable<Chunk>} chunks the chunks a literal is written into
3350
+ * @param {boolean=} hot whether a hot update can re-run what is written
3255
3351
  * @returns {Placement} where a literal in them is read from
3256
3352
  */
3257
- _placementOf(chunks) {
3353
+ _placementOf(chunks, hot = false) {
3258
3354
  /** @type {Placement | undefined} */
3259
3355
  let first;
3260
3356
  /** @type {string | null} */
@@ -3263,13 +3359,17 @@ class RuntimeTemplate {
3263
3359
  let loaded = null;
3264
3360
  for (const chunk of chunks) {
3265
3361
  const own = this._chunkPlacement(chunk);
3362
+ const ownUndo =
3363
+ hot && own.undo !== null && own.undo !== this._hotUpdateUndo(chunk)
3364
+ ? null
3365
+ : own.undo;
3266
3366
  if (first === undefined) {
3267
3367
  first = own;
3268
- undo = own.undo;
3368
+ undo = ownUndo;
3269
3369
  loaded = own.loaded;
3270
3370
  continue;
3271
3371
  }
3272
- if (undo !== own.undo) undo = null;
3372
+ if (undo !== ownUndo) undo = null;
3273
3373
  if (loaded !== own.loaded) loaded = null;
3274
3374
  }
3275
3375
  if (first === undefined) return NO_PLACEMENT;
@@ -3289,16 +3389,9 @@ class RuntimeTemplate {
3289
3389
  * @param {Chunk} chunk the worker's entry chunk
3290
3390
  * @param {Module} module the module the reference is emitted into
3291
3391
  * @param {ChunkGraph} chunkGraph the chunk graph
3292
- * @param {RuntimeRequirements} runtimeRequirements what the consuming chunk needs
3293
3392
  * @returns {string | null} a quoted literal, or `null` to fall back
3294
3393
  */
3295
- getAnalyzableWorkerUrl(
3296
- overridePublicPath,
3297
- chunk,
3298
- module,
3299
- chunkGraph,
3300
- runtimeRequirements
3301
- ) {
3394
+ getAnalyzableWorkerUrl(overridePublicPath, chunk, module, chunkGraph) {
3302
3395
  // Without an id nothing can name the chunk in a stand-in.
3303
3396
  if (
3304
3397
  chunk.id === null ||
@@ -3310,8 +3403,7 @@ class RuntimeTemplate {
3310
3403
  overridePublicPath,
3311
3404
  chunk,
3312
3405
  module,
3313
- chunkGraph,
3314
- runtimeRequirements
3406
+ chunkGraph
3315
3407
  );
3316
3408
  }
3317
3409
 
@@ -3324,12 +3416,11 @@ class RuntimeTemplate {
3324
3416
  * @param {Module | undefined} consumingModule the module the reference is emitted
3325
3417
  * into, or `undefined` when `consumingChunks` names where it goes instead
3326
3418
  * @param {ChunkGraph} chunkGraph the chunk graph
3327
- * @param {RuntimeRequirements=} runtimeRequirements set when the caller wraps the result in
3328
- * `new URL(...)` and so accepts a runtime public path prefix around the literal filename
3329
3419
  * @param {Chunk[]=} consumingChunks the chunks the reference is written into, where
3330
3420
  * they are known without a module to read them from
3331
3421
  * @param {string=} sourceType which half of the chunk is named — its javascript by
3332
3422
  * default, or its stylesheet
3423
+ * @param {boolean=} hot whether a hot update can re-run what is written
3333
3424
  * @returns {string | null} a JS string literal or expression, or `null` to fall back to the runtime form
3334
3425
  */
3335
3426
  _getAnalyzableChunkSpecifier(
@@ -3337,20 +3428,16 @@ class RuntimeTemplate {
3337
3428
  chunk,
3338
3429
  consumingModule,
3339
3430
  chunkGraph,
3340
- runtimeRequirements,
3341
3431
  consumingChunks,
3342
- sourceType = JAVASCRIPT_TYPE
3432
+ sourceType = JAVASCRIPT_TYPE,
3433
+ hot = isHotModule(consumingModule)
3343
3434
  ) {
3344
3435
  const { compilation } = this;
3345
3436
  const { outputOptions } = compilation;
3346
3437
  const naming = CHUNK_ASSET_NAMING.get(sourceType);
3347
3438
  // Nothing here names this type's asset, so nothing can spell where it will be.
3348
3439
  if (naming === undefined) return null;
3349
- const template = this._resolveChunkFilenameTemplate(
3350
- naming.template(chunk, outputOptions),
3351
- chunk,
3352
- sourceType
3353
- );
3440
+ const template = this._chunkAssetTemplate(chunk, sourceType);
3354
3441
  if (template === null) return null;
3355
3442
  // A hashed name is settled long after this code is generated, so a stand-in is
3356
3443
  // emitted and filled in once the hash exists.
@@ -3362,92 +3449,93 @@ class RuntimeTemplate {
3362
3449
  const chunks =
3363
3450
  consumingChunks ||
3364
3451
  this._moduleChunks(/** @type {Module} */ (consumingModule), chunkGraph);
3365
- const namesFirst = this._namesBeforeAll(chunk, chunks);
3366
- // An id names the chunk in the stand-in. No reference reaches here without one —
3367
- // `blockPromise` drops an id-less chunk before asking, and one the whole build
3368
- // keeps has no name to be emitted under — so the guard carries no reason of its
3369
- // own; it only keeps `null` out of a name. A pair naming each other is fine here:
3370
- // `RealContentHashPlugin`, which deferring requires, re-hashes the pair as one
3371
- // group, so neither hash chases the other.
3372
- const canReserve =
3373
- chunk.id !== null && this._canDeferAnalyzableName(chunks);
3374
- // A content-named consumer bakes anyway when this chunk settles first: the fold
3375
- // puts the name into its hash. Only what `_foldsWholeName` covers, though — and
3376
- // a pair that reaches back has no fold order, so only the one hashing first does.
3377
- const canFold = !canReserve && namesFirst;
3378
- /**
3379
- * @returns {null} always, having recorded why no stand-in may be reserved
3380
- */
3381
- const cannotReserve = () =>
3382
- this._analyzableBailout(
3383
- consumingModule,
3384
- !namesFirst && this._reachesAny(chunk, chunks)
3385
- ? "this chunk and the one it references name each other, which only optimization.realContentHash could settle"
3386
- : DEFER_BAILOUT,
3387
- null
3388
- );
3389
- if (deferred && !canReserve && !canFold) return cannotReserve();
3390
- const filename = deferred
3391
- ? ""
3392
- : compilation.getPath(/** @type {string} */ (template), {
3393
- chunk,
3394
- // Matches what names the asset, or a placeholder resolved here would
3395
- // not be the one on disk.
3396
- runtime: chunk.runtime,
3397
- contentHashType: sourceType
3398
- });
3452
+ // An id names the chunk in the stand-in; `blockPromise` drops an id-less chunk
3453
+ // before asking, so the guard only keeps `null` out of a name.
3454
+ if (chunk.id === null) return null;
3455
+ // A runtime module is generated in its chunk's round of `createHash`, where a
3456
+ // hashed name settled earlier exists: spelled, so a hot update carries the new one.
3457
+ const settled =
3458
+ deferred &&
3459
+ template !== undefined &&
3460
+ this._hashedNamePresent(chunk, template, sourceType) &&
3461
+ this._namesBeforeAll(chunk, chunks);
3462
+ const canReserve = this._canDeferAnalyzableName(chunks);
3463
+ // A content-named consumer folds this name into its hash when it settles first;
3464
+ // what the fold cannot cover is repaired instead.
3465
+ const canFold = !canReserve && this._namesBeforeAll(chunk, chunks);
3466
+ const filename =
3467
+ deferred && !settled
3468
+ ? ""
3469
+ : compilation.getPath(/** @type {string} */ (template), {
3470
+ chunk,
3471
+ // Matches what names the asset, or a placeholder resolved here would
3472
+ // not be the one on disk.
3473
+ runtime: chunk.runtime,
3474
+ contentHashType: sourceType
3475
+ });
3399
3476
  /**
3400
3477
  * @param {SpecifierPart[]} prefix what goes in front of the chunk's filename
3401
- * @returns {string | null} the specifier already quoted, or `null` when the
3402
- * recipe needs a stand-in that cannot be reserved
3478
+ * @returns {string} the specifier already quoted
3403
3479
  */
3404
3480
  const specifier = (prefix) => {
3405
- if (!deferred) {
3481
+ if (!deferred || settled) {
3406
3482
  const text = literalText(prefix);
3407
3483
  if (text !== null) return toJsStringLiteral(text + filename);
3408
3484
  }
3409
- if (!canReserve && !(canFold && this._foldsWholeName(prefix, chunks))) {
3410
- return cannotReserve();
3411
- }
3485
+ const repair =
3486
+ !canReserve && !(canFold && this._foldsWholeName(prefix, chunks));
3412
3487
  return toJsStringLiteral(
3413
- this._reserveAnalyzableSpecifier([
3414
- ...prefix,
3415
- [naming.standIn, /** @type {ChunkId} */ (chunk.id)]
3416
- ])
3488
+ this._reserveAnalyzableSpecifier(
3489
+ this._markedForRepair(repair, [
3490
+ ...prefix,
3491
+ // A settled name is carried as text: the fill would spell the same,
3492
+ // but only text of its own moves the runtime module's hash.
3493
+ settled
3494
+ ? ["literal", filename]
3495
+ : [naming.standIn, /** @type {ChunkId} */ (chunk.id)]
3496
+ ])
3497
+ )
3417
3498
  );
3418
3499
  };
3419
3500
  if (overridePublicPath) {
3420
- const resolved = this._resolvePublicPathPrefix(
3421
- overridePublicPath,
3422
- consumingModule,
3423
- chunks
3424
- );
3501
+ const resolved = this._resolvePublicPathPrefix(overridePublicPath);
3425
3502
  return resolved === null ? null : specifier(resolved);
3426
3503
  }
3427
3504
  const { publicPath } = outputOptions;
3428
3505
  if (publicPath === "auto") {
3429
- const { undo } = this._placementOf(chunks);
3506
+ const { undo } = this._placementOf(chunks, hot);
3430
3507
  if (undo !== null) return specifier([["literal", undo]]);
3431
3508
  // Different depths — no one `../` path is right for every asset the reference
3432
3509
  // lands in, so the deferred pass builds each asset's own.
3433
- const perAsset = specifier([["undo", ""]]);
3434
- if (perAsset !== null) return perAsset;
3435
- // Only a stand-in that could not be reserved reaches here, and `specifier`
3436
- // has already recorded why.
3437
- if (deferred || !runtimeRequirements) return null;
3438
- // No bundler follows a concatenation, but a `new URL(...)` caller still sheds
3439
- // the `.u(id)` lookup this way; `import()` needs a static specifier.
3440
- runtimeRequirements.add(RuntimeGlobals.publicPath);
3441
- return `${RuntimeGlobals.publicPath} + ${toJsStringLiteral(filename)}`;
3510
+ return specifier([["undo", ""]]);
3442
3511
  }
3443
3512
  const prefix = this._analyzablePathPrefix(
3444
3513
  consumingModule,
3445
3514
  chunkGraph,
3446
- chunks
3515
+ chunks,
3516
+ undefined,
3517
+ hot
3447
3518
  );
3448
3519
  return prefix === null ? null : specifier(prefix);
3449
3520
  }
3450
3521
 
3522
+ /**
3523
+ * The template this chunk's asset of `sourceType` is named by: `undefined` where a
3524
+ * function reads a hash, `null` where nothing here names that type or it answers nothing.
3525
+ * @param {Chunk} chunk the chunk
3526
+ * @param {string} sourceType which asset of the chunk
3527
+ * @returns {string | undefined | null} the template
3528
+ */
3529
+ _chunkAssetTemplate(chunk, sourceType) {
3530
+ const naming = CHUNK_ASSET_NAMING.get(sourceType);
3531
+ if (naming === undefined) return null;
3532
+ return this._resolveChunkFilenameTemplate(
3533
+ naming.template(chunk, this.outputOptions),
3534
+ chunk,
3535
+ sourceType
3536
+ );
3537
+ }
3538
+
3451
3539
  /**
3452
3540
  * The parts that go in front of a name so a literal read from the chunk holding it
3453
3541
  * reaches what the runtime would. A public path needing no base is the whole answer.
@@ -3461,18 +3549,24 @@ class RuntimeTemplate {
3461
3549
  * @param {ChunkGraph} chunkGraph the chunk graph
3462
3550
  * @param {Iterable<Chunk>} chunks the chunks a stand-in would be written into
3463
3551
  * @param {string=} relativeBase an entry `baseUri` read against the output root
3552
+ * @param {boolean=} hot whether a hot update can re-run what is written
3464
3553
  * @returns {SpecifierPart[] | null} the parts, or `null` having recorded why not
3465
3554
  */
3466
- _analyzablePathPrefix(module, chunkGraph, chunks, relativeBase) {
3555
+ _analyzablePathPrefix(
3556
+ module,
3557
+ chunkGraph,
3558
+ chunks,
3559
+ relativeBase,
3560
+ hot = isHotModule(module)
3561
+ ) {
3467
3562
  const publicPath = /** @type {PublicPath} */ (
3468
3563
  this.outputOptions.publicPath
3469
3564
  );
3470
3565
  const shape = this._publicPathShape();
3471
3566
  if (shape === undefined) return null;
3472
- const resolve = () =>
3473
- this._resolvePublicPathPrefix(publicPath, module, chunks);
3567
+ const resolve = () => this._resolvePublicPathPrefix(publicPath);
3474
3568
  if (isBaseIndependent(shape)) return resolve();
3475
- const { undo, loaded } = this._placementOf(chunks);
3569
+ const { undo, loaded } = this._placementOf(chunks, hot);
3476
3570
  // What a chunk the loader fetched climbs back out of. One of no depth leaves
3477
3571
  // nothing to climb, and then both answers put the same text in front.
3478
3572
  const served = this._publicPathClimb();
@@ -3506,12 +3600,9 @@ class RuntimeTemplate {
3506
3600
  * the deferred pass whole instead. A function is called for its value rather than
3507
3601
  * read as a template, so one whose answer moves with the hash is called again there.
3508
3602
  * @param {PublicPath} publicPath the configured public path
3509
- * @param {Module=} module the module the reference is emitted into, to record why
3510
- * a name no stand-in may be reserved for kept the runtime form
3511
- * @param {Iterable<Chunk>=} chunks the chunks a stand-in would be written into
3512
3603
  * @returns {SpecifierPart[] | null} the parts, or `null` when it cannot be known
3513
3604
  */
3514
- _resolvePublicPathPrefix(publicPath, module, chunks) {
3605
+ _resolvePublicPathPrefix(publicPath) {
3515
3606
  if (typeof publicPath === "function") {
3516
3607
  // One that answers nothing to a probe cannot be placed, absolute or not.
3517
3608
  if (this._publicPathShape() === undefined) return null;
@@ -3522,28 +3613,18 @@ class RuntimeTemplate {
3522
3613
  if (resolved !== null) return [["literal", resolved]];
3523
3614
  // Called again by the fill against the compilation hash, so what it answers
3524
3615
  // there is that hash by another name.
3525
- return this._canDeferOrBakeFullHash(true, chunks, module)
3526
- ? [["publicPath", ""]]
3527
- : null;
3616
+ return [["publicPath", ""]];
3528
3617
  }
3529
3618
  if (!publicPath.includes("[")) return [["literal", publicPath]];
3530
3619
  // A re-encoded digest cannot be spelled by a stand-in, so it stays a template.
3531
3620
  if (getTemplatedPathPlugin().usesFullHashDigest(publicPath)) {
3532
- return this._canDeferOrBakeFullHash(true, chunks, module)
3533
- ? [["template", publicPath]]
3534
- : null;
3621
+ return [["template", publicPath]];
3535
3622
  }
3536
3623
  const literal = this.compilation.getPath(
3537
3624
  publicPath,
3538
3625
  this._deferredFullHashPathData()
3539
3626
  );
3540
- return this._canDeferOrBakeFullHash(
3541
- this._hasReservedFullHash(literal),
3542
- chunks,
3543
- module
3544
- )
3545
- ? [["literal", literal]]
3546
- : null;
3627
+ return [["literal", literal]];
3547
3628
  }
3548
3629
 
3549
3630
  /**
@@ -3628,7 +3709,7 @@ class RuntimeTemplate {
3628
3709
  relativeBase
3629
3710
  );
3630
3711
  if (prefix === null) return null;
3631
- return this._specifierOf([...prefix, ...file], chunks, module);
3712
+ return this._specifierOf([...prefix, ...file], chunks);
3632
3713
  }
3633
3714
  // No public path to place it against, so the `../` path back to the output root
3634
3715
  // is the whole prefix. Different depths — no one is right for every asset the
@@ -3638,23 +3719,22 @@ class RuntimeTemplate {
3638
3719
  const parts = [undo === null ? ["undo", ""] : ["literal", undo]];
3639
3720
  if (relativeBase !== undefined) parts.push(["literal", relativeBase]);
3640
3721
  parts.push(...file);
3641
- return this._specifierOf(parts, chunks, module);
3722
+ return this._specifierOf(parts, chunks);
3642
3723
  }
3643
3724
 
3644
3725
  /**
3645
3726
  * Static `new URL(<file>, import.meta.url)` for every stylesheet a runtime can load,
3646
- * keyed by chunk id — one that cannot be named leaves the map incomplete, and its
3647
- * id keeps the runtime `publicPath + getChunkCssFilename(id)` form. Both the runtime module reading them
3727
+ * keyed by chunk id, or `null` when they have to keep the runtime
3728
+ * `publicPath + getChunkCssFilename(id)` form. Both the runtime module reading them
3648
3729
  * and the plugin declaring what it needs ask this, so the two always agree. The
3649
3730
  * runtime hands an absolute url string to `link.href`, and so does this — the browser
3650
3731
  * resolves the element against the document, which is not where the chunk sits, and
3651
- * the loader reads the url as text either way. Nothing is written out for a runtime
3652
- * that also carries the hot handler; see below.
3732
+ * the loader reads the url as text either way.
3653
3733
  * @param {Chunk} runtimeChunk the chunk holding the stylesheet loader
3654
3734
  * @param {ChunkGraph} chunkGraph the chunk graph
3655
3735
  * @param {ReadOnlyRuntimeRequirements} runtimeRequirements what that chunk needs
3656
3736
  * @param {Module=} consumingModule the runtime module, where one exists to report against
3657
- * @returns {AnalyzableChunkUrls | null} the urls, or `null` to keep the runtime form
3737
+ * @returns {Map<ChunkId, string> | null} the urls, or `null` to keep the runtime form
3658
3738
  */
3659
3739
  analyzableCssChunkUrls(
3660
3740
  runtimeChunk,
@@ -3686,14 +3766,14 @@ class RuntimeTemplate {
3686
3766
  /**
3687
3767
  * Static `new URL(<file>, import.meta.url).href` for every javascript chunk a
3688
3768
  * runtime can hint at with `<link rel="prefetch"/"modulepreload">`, keyed by chunk
3689
- * id — one that cannot be named leaves the map incomplete, and its id keeps the
3690
- * runtime `publicPath + getChunkScriptFilename(id)` form. Both the runtime module reading
3769
+ * id, or `null` when they have to keep the runtime
3770
+ * `publicPath + getChunkScriptFilename(id)` form. Both the runtime module reading
3691
3771
  * them and the plugin declaring what it needs ask this, so the two always agree.
3692
3772
  * @param {Chunk} runtimeChunk the chunk holding the hint handlers
3693
3773
  * @param {ChunkGraph} chunkGraph the chunk graph
3694
3774
  * @param {ReadOnlyRuntimeRequirements} runtimeRequirements what that chunk needs
3695
3775
  * @param {Module=} consumingModule the runtime module, where one exists to report against
3696
- * @returns {AnalyzableChunkUrls | null} the urls, or `null` to keep the runtime form
3776
+ * @returns {Map<ChunkId, string> | null} the urls, or `null` to keep the runtime form
3697
3777
  */
3698
3778
  analyzableChunkScriptUrls(
3699
3779
  runtimeChunk,
@@ -3730,17 +3810,156 @@ class RuntimeTemplate {
3730
3810
  );
3731
3811
  }
3732
3812
 
3813
+ /**
3814
+ * Why no chunk import of this build can be a native `import()`, read from the
3815
+ * output configuration alone, or `null` when nothing in it rules one out.
3816
+ * @returns {string | null} the reason
3817
+ */
3818
+ _chunkImportOutputReason() {
3819
+ const { chunkFormat, importFunctionName } = this.outputOptions;
3820
+ if (chunkFormat !== "module") {
3821
+ return `output.chunkFormat is ${JSON.stringify(chunkFormat)}, so chunks are not read through a native import()`;
3822
+ }
3823
+ if (importFunctionName !== "import") {
3824
+ return `output.importFunctionName is ${JSON.stringify(importFunctionName)}, so the call site is not a native import()`;
3825
+ }
3826
+ return null;
3827
+ }
3828
+
3829
+ /**
3830
+ * Records that this runtime reads its chunks some way other than a native
3831
+ * `import()`, so no literal can name what it loads. Recorded on the entries that
3832
+ * chose it, which is where the setting behind it lives.
3833
+ * @param {Chunk} chunk the runtime chunk
3834
+ * @param {ChunkGraph} chunkGraph the chunk graph
3835
+ * @returns {void}
3836
+ */
3837
+ reportChunkImportBailout(chunk, chunkGraph) {
3838
+ if (!this.outputOptions.module) return;
3839
+ const entryOptions = chunk.getEntryOptions();
3840
+ const chunkLoading =
3841
+ entryOptions && entryOptions.chunkLoading !== undefined
3842
+ ? entryOptions.chunkLoading
3843
+ : this.outputOptions.chunkLoading;
3844
+ if (chunkLoading === "import") return;
3845
+ const subject = entryOptions && entryOptions.worker ? "worker" : "runtime";
3846
+ const reason = `this ${subject} loads its chunks with ${JSON.stringify(chunkLoading)}, not "import"`;
3847
+ for (const module of chunkGraph.getChunkEntryModulesIterable(chunk)) {
3848
+ this._analyzableBailout(module, reason, false);
3849
+ }
3850
+ }
3851
+
3852
+ /**
3853
+ * The chunks this loader can be handed an id for: the ones held by a group outside
3854
+ * the initial wave, which is what an `import()` or a context map turns into an id.
3855
+ * @param {Chunk} runtimeChunk the chunk holding the chunk loader
3856
+ * @returns {Set<Chunk>} the chunks reachable from here through a non-initial group
3857
+ */
3858
+ _loadableChunks(runtimeChunk) {
3859
+ const queue = new Set(runtimeChunk.groupsIterable);
3860
+ /** @type {Set<Chunk>} */
3861
+ const chunks = new Set();
3862
+ for (const group of queue) {
3863
+ if (!group.isInitial()) {
3864
+ for (const chunk of group.chunks) chunks.add(chunk);
3865
+ }
3866
+ for (const child of group.childrenIterable) queue.add(child);
3867
+ }
3868
+ return chunks;
3869
+ }
3870
+
3871
+ /**
3872
+ * Whether the loader still needs to build a url from a chunk id, which is the case
3873
+ * for a chunk it can be asked for that `analyzableChunkImports` leaves unnamed: one
3874
+ * carrying a runtime of its own, or one a second entrypoint has at startup.
3875
+ * @param {Chunk} runtimeChunk the chunk holding the chunk loader
3876
+ * @param {ChunkGraph} chunkGraph the chunk graph
3877
+ * @returns {boolean} true when an id the map does not hold can reach the loader
3878
+ */
3879
+ chunkImportsNeedRuntimeUrl(runtimeChunk, chunkGraph) {
3880
+ const installed = new Set(runtimeChunk.getAllInitialChunks());
3881
+ for (const chunk of this._loadableChunks(runtimeChunk)) {
3882
+ if (chunk === runtimeChunk) continue;
3883
+ if (!chunk.hasRuntime() && !installed.has(chunk)) continue;
3884
+ if (JavascriptModulesPlugin.chunkHasJs(chunk, chunkGraph)) return true;
3885
+ }
3886
+ return false;
3887
+ }
3888
+
3889
+ /**
3890
+ * A literal `import()` specifier for every javascript chunk a runtime loads on
3891
+ * demand, keyed by chunk id, or `null` when one of them cannot be named here. The
3892
+ * map is written into the runtime chunk rather than at each import site, so a
3893
+ * hashed chunk name reaches no module that imports it.
3894
+ * @param {Chunk} runtimeChunk the chunk holding the chunk loader
3895
+ * @param {ChunkGraph} chunkGraph the chunk graph
3896
+ * @param {ReadOnlyRuntimeRequirements} runtimeRequirements what that chunk needs
3897
+ * @param {Module=} consumingModule the runtime module, where one exists to report against
3898
+ * @returns {Map<ChunkId, string> | null} the specifiers, or `null` to keep the runtime form
3899
+ */
3900
+ analyzableChunkImports(
3901
+ runtimeChunk,
3902
+ chunkGraph,
3903
+ runtimeRequirements,
3904
+ consumingModule
3905
+ ) {
3906
+ // The runtime names the scope `__webpack_public_path__` belongs to, so the
3907
+ // answer holds whether or not a runtime module is there to ask about.
3908
+ if (
3909
+ !this.supportsAnalyzable(
3910
+ "import",
3911
+ chunkGraph,
3912
+ consumingModule,
3913
+ runtimeChunk.runtime
3914
+ )
3915
+ ) {
3916
+ return null;
3917
+ }
3918
+ // An initial chunk and one carrying a runtime are both reached by a static
3919
+ // import, so naming either here would only make two hashes chase each other.
3920
+ const installed = new Set(runtimeChunk.getAllInitialChunks());
3921
+ /** @type {Chunk[]} */
3922
+ const chunks = [];
3923
+ for (const chunk of runtimeChunk.getAllReferencedChunks()) {
3924
+ if (
3925
+ chunk === runtimeChunk ||
3926
+ chunk.hasRuntime() ||
3927
+ installed.has(chunk) ||
3928
+ !JavascriptModulesPlugin.chunkHasJs(chunk, chunkGraph)
3929
+ ) {
3930
+ continue;
3931
+ }
3932
+ chunks.push(chunk);
3933
+ }
3934
+ const specifiers = this._analyzableChunkSpecifiers(
3935
+ chunks,
3936
+ chunkGraph,
3937
+ consumingModule,
3938
+ [runtimeChunk],
3939
+ JAVASCRIPT_TYPE,
3940
+ runtimeRequirements
3941
+ );
3942
+ if (specifiers === null) return null;
3943
+ for (const [id, specifier] of specifiers) {
3944
+ // A bare specifier is a package name, so spell it relative the way the
3945
+ // runtime form's `.p + <file>` would have resolved it.
3946
+ if (!RESOLVABLE_SPECIFIER_REGEXP.test(specifier.slice(1))) {
3947
+ specifiers.set(id, `"./${specifier.slice(1)}`);
3948
+ }
3949
+ }
3950
+ return specifiers;
3951
+ }
3952
+
3733
3953
  /**
3734
3954
  * The urls of one asset of each of `chunks`, written out so they can be read by
3735
- * chunk id. A chunk that cannot be named here leaves the map incomplete rather
3736
- * than losing it — the consumer keeps the runtime form for the ids the map lacks.
3955
+ * chunk id, or `null` as soon as one of them cannot be named here.
3737
3956
  * @param {Iterable<Chunk>} chunks the chunks whose asset is wanted
3738
3957
  * @param {ChunkGraph} chunkGraph the chunk graph
3739
3958
  * @param {Module | undefined} consumingModule the runtime module, where one exists to report against
3740
3959
  * @param {Chunk[]} consumingChunks the chunks the urls are written into
3741
3960
  * @param {string} sourceType which asset of each chunk to name
3742
3961
  * @param {ReadOnlyRuntimeRequirements} runtimeRequirements what the runtime chunk needs
3743
- * @returns {AnalyzableChunkUrls | null} the urls, or `null` to keep the runtime form
3962
+ * @returns {Map<ChunkId, string> | null} the urls, or `null` to keep the runtime form
3744
3963
  */
3745
3964
  _analyzableChunkUrls(
3746
3965
  chunks,
@@ -3750,17 +3969,51 @@ class RuntimeTemplate {
3750
3969
  sourceType,
3751
3970
  runtimeRequirements
3752
3971
  ) {
3753
- // A map written now cannot answer for whatever id an update names, so it yields
3754
- // to HMR — analyzable output is what ships.
3755
- if (
3756
- runtimeRequirements.has(RuntimeGlobals.hmrDownloadUpdateHandlers) ||
3757
- !this.supportsAnalyzable("url-runtime", chunkGraph, consumingModule)
3758
- ) {
3972
+ if (!this.supportsAnalyzable("url-runtime", chunkGraph, consumingModule)) {
3759
3973
  return null;
3760
3974
  }
3975
+ const urls = this._analyzableChunkSpecifiers(
3976
+ chunks,
3977
+ chunkGraph,
3978
+ consumingModule,
3979
+ consumingChunks,
3980
+ sourceType,
3981
+ runtimeRequirements
3982
+ );
3983
+ if (urls === null) return null;
3984
+ for (const [id, specifier] of urls) {
3985
+ urls.set(id, `${this.importMetaUrl(specifier)}.href`);
3986
+ }
3987
+ return urls;
3988
+ }
3989
+
3990
+ /**
3991
+ * The quoted specifier naming one asset of each of `chunks`, keyed by chunk id, or
3992
+ * `null` as soon as one of them cannot be named here. What the name is read against
3993
+ * is the caller's: a url resolves it, an `import()` hands it to the loader.
3994
+ * @param {Iterable<Chunk>} chunks the chunks whose asset is wanted
3995
+ * @param {ChunkGraph} chunkGraph the chunk graph
3996
+ * @param {Module | undefined} consumingModule the runtime module, where one exists to report against
3997
+ * @param {Chunk[]} consumingChunks the chunks the specifiers are written into
3998
+ * @param {string} sourceType which asset of each chunk to name
3999
+ * @param {ReadOnlyRuntimeRequirements} runtimeRequirements what the runtime chunk needs
4000
+ * @returns {Map<ChunkId, string> | null} the specifiers, or `null` to keep the runtime form
4001
+ */
4002
+ _analyzableChunkSpecifiers(
4003
+ chunks,
4004
+ chunkGraph,
4005
+ consumingModule,
4006
+ consumingChunks,
4007
+ sourceType,
4008
+ runtimeRequirements
4009
+ ) {
4010
+ // A hot update re-ships the runtime module only when its own code changes, so
4011
+ // each name has to be text nothing but an id joining or leaving the map can move.
4012
+ const hot = runtimeRequirements.has(
4013
+ RuntimeGlobals.hmrDownloadUpdateHandlers
4014
+ );
3761
4015
  /** @type {Map<ChunkId, string>} */
3762
- const urls = new Map();
3763
- let complete = true;
4016
+ const specifiers = new Map();
3764
4017
  for (const chunk of chunks) {
3765
4018
  if (chunk.id === null) continue;
3766
4019
  const specifier = this._getAnalyzableChunkSpecifier(
@@ -3768,18 +4021,74 @@ class RuntimeTemplate {
3768
4021
  chunk,
3769
4022
  consumingModule,
3770
4023
  chunkGraph,
3771
- undefined,
3772
4024
  consumingChunks,
3773
- sourceType
4025
+ sourceType,
4026
+ hot
3774
4027
  );
3775
- // `_getAnalyzableChunkSpecifier` has already recorded why.
3776
- if (specifier === null) {
3777
- complete = false;
4028
+ if (specifier === null) return null;
4029
+ if (
4030
+ hot &&
4031
+ !this._settledUnderHot(specifier, chunk, sourceType, consumingChunks)
4032
+ ) {
4033
+ return this._analyzableBailout(consumingModule, HOT_NAME_BAILOUT, null);
4034
+ }
4035
+ specifiers.set(chunk.id, specifier);
4036
+ }
4037
+ return specifiers.size > 0 ? specifiers : null;
4038
+ }
4039
+
4040
+ /**
4041
+ * Whether a url a hot update re-ships moves only with the runtime module's own text:
4042
+ * spelled here, or a stand-in of each asset's depth and a hash-free or settled name.
4043
+ * @param {string} specifier the specifier already quoted
4044
+ * @param {Chunk} chunk the chunk it names
4045
+ * @param {string} sourceType which asset of the chunk it names
4046
+ * @param {Chunk[]} consumingChunks the chunks the map is written into
4047
+ * @returns {boolean} true when no update moves what the fill spells
4048
+ */
4049
+ _settledUnderHot(specifier, chunk, sourceType, consumingChunks) {
4050
+ const match = ANALYZABLE_TOKEN_PAYLOAD.exec(specifier);
4051
+ if (match === null) return true;
4052
+ const parts = RuntimeTemplate._readAnalyzableSpecifier(match[1]);
4053
+ if (parts === null) return false;
4054
+ for (const [kind, value] of parts) {
4055
+ if (kind === "undo") continue;
4056
+ if (kind === "literal" && !this._hasReservedFullHash(String(value))) {
3778
4057
  continue;
3779
4058
  }
3780
- urls.set(chunk.id, `${this.importMetaUrl(specifier)}.href`);
4059
+ if (!CHUNK_SPECIFIER_PART_KINDS.has(kind)) return false;
3781
4060
  }
3782
- return urls.size > 0 ? { urls, complete } : null;
4061
+ const template = this._chunkAssetTemplate(chunk, sourceType);
4062
+ return (
4063
+ typeof template === "string" &&
4064
+ (!HASH_IN_FILENAME.test(template) ||
4065
+ this._hashedNameSettledBefore(chunk, template, consumingChunks))
4066
+ );
4067
+ }
4068
+
4069
+ /**
4070
+ * The `../` path from a chunk's hot update file, which re-runs its modules, back to
4071
+ * the output root. Hashes are neutralized; the chunk's own id may add a directory.
4072
+ * @param {Chunk} chunk the chunk being updated
4073
+ * @returns {string} the path back to the output root
4074
+ */
4075
+ _hotUpdateUndo(chunk) {
4076
+ const cached = this._hotUpdateUndoByChunk.get(chunk);
4077
+ if (cached !== undefined) return cached;
4078
+ const { outputOptions } = this;
4079
+ if (this._hotUpdateShapeText === undefined) {
4080
+ this._hotUpdateShapeText = outputOptions.hotUpdateChunkFilename.replace(
4081
+ HASH_IN_FILENAME_GLOBAL,
4082
+ "x"
4083
+ );
4084
+ }
4085
+ const undo = getUndoPath(
4086
+ this.compilation.getPath(this._hotUpdateShapeText, { chunk }),
4087
+ /** @type {string} */ (outputOptions.path),
4088
+ true
4089
+ );
4090
+ this._hotUpdateUndoByChunk.set(chunk, undo);
4091
+ return undo;
3783
4092
  }
3784
4093
 
3785
4094
  /**
@@ -3985,7 +4294,7 @@ class RuntimeTemplate {
3985
4294
  * @returns {string} the stand-in to emit
3986
4295
  */
3987
4296
  _reserveAnalyzableSpecifier(parts) {
3988
- return `./@@webpackAnalyzableChunk:${Buffer.from(JSON.stringify(parts))
4297
+ return `${ANALYZABLE_TOKEN_PREFIX}${Buffer.from(JSON.stringify(parts))
3989
4298
  .toString("base64")
3990
4299
  .replace(/\+/g, "-")
3991
4300
  .replace(/\//g, "_")
@@ -4056,6 +4365,10 @@ class RuntimeTemplate {
4056
4365
  async () => {
4057
4366
  const { outputOptions } = compilation;
4058
4367
  const fullHash = compilation.hash;
4368
+ // The assets a stand-in landed in whose hashed name has to be brought
4369
+ // back in line, which `RealContentHashPlugin` then does.
4370
+ /** @type {Set<string>} */
4371
+ const repairedAssets = new Set();
4059
4372
  /** @type {Map<string, Chunk> | undefined} */
4060
4373
  let chunksById;
4061
4374
  /** @type {Map<string, Chunk> | undefined} */
@@ -4110,7 +4423,9 @@ class RuntimeTemplate {
4110
4423
  let base;
4111
4424
  let specifier = "";
4112
4425
  for (const [kind, value] of parts) {
4113
- if (kind === "base") {
4426
+ if (kind === "repair") {
4427
+ repairedAssets.add(assetName);
4428
+ } else if (kind === "base") {
4114
4429
  base = /** @type {string} */ (value);
4115
4430
  } else if (kind === "literal") {
4116
4431
  specifier += value;
@@ -4181,7 +4496,7 @@ class RuntimeTemplate {
4181
4496
  // the chunk loader does. A literal part may still carry a stand-in of its own,
4182
4497
  // so finish those here rather than scanning again.
4183
4498
  return fillFullHash(
4184
- /^(?:\.{0,2}\/|[a-zA-Z][\w+.-]*:)/.test(specifier)
4499
+ RESOLVABLE_SPECIFIER_REGEXP.test(specifier)
4185
4500
  ? specifier
4186
4501
  : `./${specifier}`
4187
4502
  );
@@ -4234,6 +4549,8 @@ class RuntimeTemplate {
4234
4549
 
4235
4550
  tasks.push({ name, source, replacements });
4236
4551
  }
4552
+ compilation.runtimeTemplate._analyzableRepairedAssets =
4553
+ repairedAssets;
4237
4554
  if (tasks.length === 0) return;
4238
4555
 
4239
4556
  await Promise.all(