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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) hide show
  1. package/README.md +7 -8
  2. package/changes.json +7 -11
  3. package/lib/APIPlugin.js +26 -11
  4. package/lib/AutomaticPrefetchPlugin.js +1 -1
  5. package/lib/Cache.js +8 -3
  6. package/lib/CacheFacade.js +1 -1
  7. package/lib/ChunkGraph.js +20 -18
  8. package/lib/CircularModulesPlugin.js +157 -55
  9. package/lib/CleanPlugin.js +2 -2
  10. package/lib/CompatibilityPlugin.js +3 -4
  11. package/lib/Compilation.js +337 -338
  12. package/lib/Compiler.js +213 -51
  13. package/lib/ConcatenationScope.js +25 -35
  14. package/lib/ConstPlugin.js +16 -132
  15. package/lib/CopyPlugin.js +1054 -0
  16. package/lib/DefinePlugin.js +24 -8
  17. package/lib/DependencyTemplate.js +2 -1
  18. package/lib/DotenvPlugin.js +64 -40
  19. package/lib/ErrorHelpers.js +113 -0
  20. package/lib/ExportsInfo.js +30 -16
  21. package/lib/ExternalModule.js +47 -22
  22. package/lib/FileSystemInfo.js +17 -14
  23. package/lib/FlagDependencyExportsPlugin.js +53 -34
  24. package/lib/FlagDependencyUsagePlugin.js +17 -15
  25. package/lib/Generator.js +24 -2
  26. package/lib/HotModuleReplacementPlugin.js +55 -7
  27. package/lib/IgnorePlugin.js +1 -1
  28. package/lib/JavascriptMetaInfoPlugin.js +5 -3
  29. package/lib/LoaderOptionsPlugin.js +1 -1
  30. package/lib/MainTemplate.js +5 -1
  31. package/lib/Module.js +19 -10
  32. package/lib/ModuleGraph.js +6 -9
  33. package/lib/MultiCompiler.js +7 -17
  34. package/lib/MultiWatching.js +1 -1
  35. package/lib/NormalModule.js +148 -70
  36. package/lib/NormalModuleFactory.js +1 -1
  37. package/lib/RuntimeGlobals.js +21 -7
  38. package/lib/RuntimeModule.js +28 -12
  39. package/lib/RuntimePlugin.js +33 -3
  40. package/lib/RuntimeTemplate.js +1147 -847
  41. package/lib/SourceMapDevToolPlugin.js +54 -104
  42. package/lib/TemplatedPathPlugin.js +16 -59
  43. package/lib/Watching.js +9 -0
  44. package/lib/WebpackOptionsApply.js +84 -91
  45. package/lib/asset/AssetBytesGenerator.js +7 -1
  46. package/lib/asset/AssetGenerator.js +50 -24
  47. package/lib/asset/AssetModule.js +3 -0
  48. package/lib/asset/AssetSourceGenerator.js +7 -1
  49. package/lib/asset/WebManifestParser.js +7 -5
  50. package/lib/async-modules/AsyncModuleHelpers.js +10 -4
  51. package/lib/buildChunkGraph.js +11 -15
  52. package/lib/cache/AddBuildDependenciesPlugin.js +33 -4
  53. package/lib/cache/IdleFileCachePlugin.js +2 -2
  54. package/lib/cache/MemoryCachePlugin.js +4 -4
  55. package/lib/cache/MemoryWithGcCachePlugin.js +7 -8
  56. package/lib/cache/PackFileCacheStrategy.js +17 -19
  57. package/lib/cache/getLazyHashedEtag.js +3 -5
  58. package/lib/config/browserslistTargetHandler.js +17 -4
  59. package/lib/config/defaults.js +162 -142
  60. package/lib/config/getClaimedAssetTypes.js +10 -11
  61. package/lib/config/normalization.js +100 -20
  62. package/lib/config/target.js +13 -4
  63. package/lib/container/ContainerReferencePlugin.js +6 -0
  64. package/lib/container/RemoteRuntimeModule.js +9 -0
  65. package/lib/container/declaredRemotes.js +39 -0
  66. package/lib/{ContextExclusionPlugin.js → context/ContextExclusionPlugin.js} +1 -1
  67. package/lib/{ContextModule.js → context/ContextModule.js} +54 -115
  68. package/lib/{ContextModuleFactory.js → context/ContextModuleFactory.js} +19 -21
  69. package/lib/{ContextReplacementPlugin.js → context/ContextReplacementPlugin.js} +4 -4
  70. package/lib/css/CssGenerator.js +27 -11
  71. package/lib/css/CssInjectStyleRuntimeModule.js +9 -0
  72. package/lib/css/CssLoadingRuntimeModule.js +92 -15
  73. package/lib/css/CssModule.js +1 -1
  74. package/lib/css/CssModulesPlugin.js +3 -10
  75. package/lib/css/CssParser.js +162 -31
  76. package/lib/css/CssServerStylesRuntimeModule.js +9 -0
  77. package/lib/css/cssMinify.js +43 -32
  78. package/lib/css/data.js +3526 -871
  79. package/lib/css/syntax.js +6455 -1237
  80. package/lib/debug/ProfilingPlugin.js +7 -4
  81. package/lib/dependencies/AMDDefineDependencyParserPlugin.js +6 -3
  82. package/lib/dependencies/AMDRuntimeModules.js +18 -0
  83. package/lib/dependencies/CommonJsExportRequireDependency.js +13 -12
  84. package/lib/dependencies/CommonJsExportsParserPlugin.js +12 -13
  85. package/lib/dependencies/CommonJsImportsParserPlugin.js +5 -5
  86. package/lib/dependencies/CommonJsPlugin.js +19 -3
  87. package/lib/dependencies/CommonJsRequireDependency.js +32 -9
  88. package/lib/dependencies/CommonJsSelfReferenceDependency.js +2 -4
  89. package/lib/dependencies/ContextDependency.js +1 -1
  90. package/lib/dependencies/ContextDependencyHelpers.js +3 -9
  91. package/lib/dependencies/ContextDependencyTemplateAsRequireCall.js +20 -7
  92. package/lib/dependencies/ContextElementDependency.js +1 -1
  93. package/lib/dependencies/CssIcssExportDependency.js +13 -9
  94. package/lib/dependencies/CssIcssImportDependency.js +3 -6
  95. package/lib/dependencies/CssUrlDependency.js +3 -5
  96. package/lib/dependencies/HarmonyCompatibilityDependency.js +67 -22
  97. package/lib/dependencies/HarmonyDetectionParserPlugin.js +3 -4
  98. package/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js +10 -4
  99. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +12 -7
  100. package/lib/dependencies/HarmonyExportExpressionDependency.js +47 -33
  101. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +217 -34
  102. package/lib/dependencies/HarmonyExportInitFragment.js +110 -23
  103. package/lib/dependencies/HarmonyExportSpecifierDependency.js +25 -58
  104. package/lib/dependencies/HarmonyImportBareSideEffectDependency.js +59 -0
  105. package/lib/dependencies/HarmonyImportDependency.js +99 -41
  106. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +198 -25
  107. package/lib/dependencies/HarmonyImportSideEffectDependency.js +117 -4
  108. package/lib/dependencies/HarmonyImportSpecifierDependency.js +40 -3
  109. package/lib/dependencies/HarmonyModulesPlugin.js +20 -0
  110. package/lib/dependencies/HtmlEntryDependency.js +136 -104
  111. package/lib/dependencies/HtmlInlineScriptDependency.js +3 -4
  112. package/lib/dependencies/HtmlInlineStyleDependency.js +3 -4
  113. package/lib/dependencies/ImportContextDependency.js +43 -3
  114. package/lib/dependencies/ImportDependency.js +8 -29
  115. package/lib/dependencies/ImportMetaContextDependencyParserPlugin.js +1 -1
  116. package/lib/dependencies/ImportMetaGlobHelpers.js +1 -1
  117. package/lib/dependencies/ImportMetaMainDependency.js +104 -0
  118. package/lib/dependencies/ImportMetaPlugin.js +67 -25
  119. package/lib/dependencies/ImportParserPlugin.js +25 -15
  120. package/lib/dependencies/LoaderPlugin.js +26 -1
  121. package/lib/dependencies/RequireContextDependencyParserPlugin.js +1 -1
  122. package/lib/dependencies/RequireHeaderDependency.js +20 -21
  123. package/lib/dependencies/SystemRuntimeModule.js +9 -0
  124. package/lib/dependencies/URLContextDependency.js +1 -1
  125. package/lib/dependencies/URLDependency.js +6 -5
  126. package/lib/dependencies/WorkerAndWorkletPlugin.js +16 -21
  127. package/lib/dependencies/WorkerDependency.js +12 -18
  128. package/lib/dependencies/WorkletDependency.js +12 -20
  129. package/lib/dependencies/importOptionsCheck.js +47 -0
  130. package/lib/dll/DelegatedModule.js +18 -1
  131. package/lib/dll/DllReferencePlugin.js +11 -8
  132. package/lib/dll/LibManifestPlugin.js +1 -1
  133. package/lib/errors/AnalyzableBailoutsWarning.js +41 -0
  134. package/lib/errors/DotenvFileError.js +29 -0
  135. package/lib/errors/DuplicateModulesWarning.js +12 -7
  136. package/lib/errors/EmptyCopyPatternWarning.js +26 -0
  137. package/lib/errors/ExternalStarReexportError.js +44 -0
  138. package/lib/errors/HookWebpackError.js +3 -2
  139. package/lib/errors/ImportedBindingAssignmentError.js +30 -0
  140. package/lib/errors/MissingSourceMapsWarning.js +36 -0
  141. package/lib/errors/ModuleParseError.js +2 -1
  142. package/lib/errors/UnusedAssetsWarning.js +36 -0
  143. package/lib/errors/UnusedFederationWarning.js +35 -0
  144. package/lib/errors/UnusedModulesWarning.js +38 -0
  145. package/lib/esm/ExportWebpackRequireRuntimeModule.js +9 -0
  146. package/lib/esm/ModuleChunkFormatPlugin.js +28 -3
  147. package/lib/esm/ModuleChunkLoadingPlugin.js +21 -5
  148. package/lib/esm/ModuleChunkLoadingRuntimeModule.js +91 -53
  149. package/lib/hmr/HotModuleReplacement.runtime.js +8 -2
  150. package/lib/hmr/HotModuleReplacementRuntimeModule.js +9 -0
  151. package/lib/hmr/LazyCompilationPlugin.js +3 -5
  152. package/lib/html/HtmlGenerator.js +72 -87
  153. package/lib/html/HtmlModulesPlugin.js +479 -215
  154. package/lib/html/HtmlParser.js +110 -147
  155. package/lib/html/builtinEmbeddedRenderer.js +107 -0
  156. package/lib/html/data.js +142 -7
  157. package/lib/html/htmlMinify.js +92 -122
  158. package/lib/html/syntax.js +2031 -1556
  159. package/lib/ids/HashedModuleIdsPlugin.js +2 -1
  160. package/lib/index.js +49 -3
  161. package/lib/javascript/ChunkFormatHelpers.js +4 -3
  162. package/lib/javascript/JavascriptGenerator.js +8 -1
  163. package/lib/javascript/JavascriptModule.js +1 -0
  164. package/lib/javascript/JavascriptModulesPlugin.js +76 -80
  165. package/lib/javascript/JavascriptParser.js +1970 -900
  166. package/lib/javascript/StartupHelpers.js +27 -3
  167. package/lib/javascript/data.js +172 -0
  168. package/lib/javascript/grammar.js +5546 -0
  169. package/lib/javascript/parser.js +1500 -0
  170. package/lib/javascript/regexp.js +1762 -0
  171. package/lib/javascript/regexpData.js +51 -0
  172. package/lib/javascript/syntax.js +9061 -5262
  173. package/lib/json/JsonGenerator.js +7 -1
  174. package/lib/library/ModuleLibraryPlugin.js +171 -132
  175. package/lib/loaders/LoaderRunner.js +170 -14
  176. package/lib/node/NodeWatchFileSystem.js +3 -9
  177. package/lib/node/ReadFileChunkLoadingRuntimeModule.js +22 -0
  178. package/lib/node/ReadFileCompileAsyncWasmPlugin.js +8 -15
  179. package/lib/node/RequireChunkLoadingRuntimeModule.js +22 -0
  180. package/lib/optimize/AggressiveMergingPlugin.js +3 -5
  181. package/lib/optimize/ConcatenatedModule.js +222 -125
  182. package/lib/optimize/ConstExportsPlugin.js +50 -34
  183. package/lib/optimize/FlagIncludedChunksPlugin.js +3 -11
  184. package/lib/optimize/InlineExports.js +20 -2
  185. package/lib/optimize/InnerGraphPlugin.js +7 -13
  186. package/lib/optimize/LimitChunkCountPlugin.js +5 -8
  187. package/lib/optimize/MangleExportsPlugin.js +13 -4
  188. package/lib/optimize/ModuleConcatenationPlugin.js +27 -5
  189. package/lib/optimize/RealContentHashPlugin.js +179 -44
  190. package/lib/optimize/SideEffectsFlagPlugin.js +24 -28
  191. package/lib/optimize/SplitChunksPlugin.js +2 -4
  192. package/lib/performance/AnalyzableBailoutsPlugin.js +115 -0
  193. package/lib/performance/BroadContextsPlugin.js +1 -1
  194. package/lib/performance/CacheEffectivenessPlugin.js +7 -7
  195. package/lib/performance/DuplicateModulesPlugin.js +57 -13
  196. package/lib/performance/EvalUsagePlugin.js +5 -2
  197. package/lib/performance/MissingSourceMapsPlugin.js +115 -0
  198. package/lib/performance/PureAnnotationsPlugin.js +6 -3
  199. package/lib/performance/SizeLimitsPlugin.js +2 -4
  200. package/lib/performance/TopLevelThisPlugin.js +5 -4
  201. package/lib/performance/UnusedAssetsPlugin.js +109 -0
  202. package/lib/performance/UnusedFederationPlugin.js +147 -0
  203. package/lib/performance/UnusedModulesPlugin.js +141 -0
  204. package/lib/performance/UnusedReexportsPlugin.js +1 -55
  205. package/lib/performance/isOnlyReexported.js +65 -0
  206. package/lib/prefetch/ChunkPrefetchFunctionRuntimeModule.js +25 -5
  207. package/lib/prefetch/ChunkPrefetchPreloadPlugin.js +8 -8
  208. package/lib/prefetch/ChunkPrefetchStartupRuntimeModule.js +9 -0
  209. package/lib/prefetch/ChunkPrefetchTriggerRuntimeModule.js +9 -0
  210. package/lib/prefetch/ChunkPreloadTriggerRuntimeModule.js +9 -0
  211. package/lib/prefetch/ResourceHintPlugin.js +29 -35
  212. package/lib/prefetch/ResourceHintRuntimeModule.js +9 -0
  213. package/lib/prefetch/StartupAssetHintRuntimeModule.js +16 -10
  214. package/lib/rules/RuleSetCompiler.js +211 -4
  215. package/lib/runtime/AsyncModuleRuntimeModule.js +103 -67
  216. package/lib/runtime/AutoPublicPathRuntimeModule.js +9 -0
  217. package/lib/runtime/BaseUriRuntimeModule.js +9 -0
  218. package/lib/runtime/ChunkNameRuntimeModule.js +9 -0
  219. package/lib/runtime/EnsureChunkRuntimeModule.js +35 -16
  220. package/lib/runtime/GetChunkFilenameRuntimeModule.js +12 -4
  221. package/lib/runtime/GetFullHashRuntimeModule.js +9 -0
  222. package/lib/runtime/GetMainFilenameRuntimeModule.js +9 -0
  223. package/lib/runtime/GetWorkletBootstrapRuntimeModule.js +9 -0
  224. package/lib/runtime/GlobalRuntimeModule.js +11 -4
  225. package/lib/runtime/HasOwnPropertyRuntimeModule.js +9 -0
  226. package/lib/runtime/HelperRuntimeModule.js +10 -0
  227. package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +313 -222
  228. package/lib/runtime/NonceRuntimeModule.js +9 -0
  229. package/lib/runtime/OnChunksLoadedRuntimeModule.js +67 -20
  230. package/lib/runtime/PublicPathRuntimeModule.js +9 -0
  231. package/lib/runtime/RuntimeIdRuntimeModule.js +9 -0
  232. package/lib/runtime/SpecNamespaceObjectRuntimeModule.js +90 -0
  233. package/lib/runtime/StartupChunkDependenciesRuntimeModule.js +9 -0
  234. package/lib/runtime/StartupEntrypointRuntimeModule.js +9 -0
  235. package/lib/runtime/SystemContextRuntimeModule.js +9 -0
  236. package/lib/runtime/ToBinaryRuntimeModule.js +9 -0
  237. package/lib/schemes/DataUriPlugin.js +3 -6
  238. package/lib/schemes/HttpUriPlugin.js +22 -5
  239. package/lib/schemes/VirtualUrlPlugin.js +1 -1
  240. package/lib/serialization/BinaryMiddleware.js +9 -9
  241. package/lib/serialization/FileMiddleware.js +31 -18
  242. package/lib/serialization/ObjectMiddleware.js +168 -139
  243. package/lib/sharing/ConsumeSharedPlugin.js +12 -0
  244. package/lib/sharing/ConsumeSharedRuntimeModule.js +11 -0
  245. package/lib/sharing/ShareRuntimeModule.js +9 -0
  246. package/lib/sharing/declaredShared.js +47 -0
  247. package/lib/stats/DefaultStatsFactoryPlugin.js +6 -5
  248. package/lib/stats/DefaultStatsPrinterPlugin.js +3 -0
  249. package/lib/typescript/TypeScriptPlugin.js +10 -13
  250. package/lib/url/URLParserPlugin.js +6 -8
  251. package/lib/util/ArrayQueue.js +3 -3
  252. package/lib/util/LocConverter.js +4 -7
  253. package/lib/util/SourceProcessor.js +115 -99
  254. package/lib/util/URLAbsoluteSpecifier.js +3 -1
  255. package/lib/util/async.js +292 -0
  256. package/lib/util/buildDiagnostics.js +69 -0
  257. package/lib/util/chainedImports.js +3 -4
  258. package/lib/util/concatenate.js +4 -4
  259. package/lib/util/conventions.js +5 -1
  260. package/lib/util/createHooksRegistry.js +1 -2
  261. package/lib/util/createMappings.js +25 -3
  262. package/lib/util/dataURL.js +2 -3
  263. package/lib/util/deterministicGrouping.js +8 -13
  264. package/lib/util/findGraphRoots.js +6 -13
  265. package/lib/util/fs.js +27 -1
  266. package/lib/util/globUtils.js +1 -1
  267. package/lib/util/handlerKeys.js +40 -0
  268. package/lib/util/hash/wasm-hash.js +3 -4
  269. package/lib/util/identifier.js +20 -0
  270. package/lib/util/internalSerializables.js +7 -1
  271. package/lib/util/registerExternalSerializer.js +6 -45
  272. package/lib/util/removeBOM.js +82 -1
  273. package/lib/util/semver.js +5 -3
  274. package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +14 -3
  275. package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +14 -3
  276. package/lib/wasm-async/AsyncWebAssemblyGenerator.js +6 -1
  277. package/lib/wasm-async/AsyncWebAssemblyJavascriptGenerator.js +5 -1
  278. package/lib/wasm-async/UniversalCompileAsyncWasmPlugin.js +7 -12
  279. package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +12 -4
  280. package/lib/wasm-sync/WebAssemblyGenerator.js +6 -1
  281. package/lib/wasm-sync/WebAssemblyJavascriptGenerator.js +5 -1
  282. package/lib/wasm-sync/WebAssemblyUtils.js +2 -4
  283. package/lib/web/FetchCompileAsyncWasmPlugin.js +10 -6
  284. package/lib/web/FetchCompileWasmPlugin.js +4 -2
  285. package/lib/web/JsonpChunkLoadingRuntimeModule.js +36 -0
  286. package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +19 -0
  287. package/package.json +55 -46
  288. package/schemas/WebpackOptions.check.js +1 -1
  289. package/schemas/WebpackOptions.json +388 -76
  290. package/schemas/plugins/LoaderOptionsPlugin.check.js +1 -1
  291. package/schemas/plugins/ManifestPlugin.check.js +1 -1
  292. package/schemas/plugins/SourceMapDevToolPlugin.check.js +1 -1
  293. package/schemas/plugins/asset/AssetGeneratorOptions.check.js +1 -1
  294. package/schemas/plugins/asset/AssetResourceGeneratorOptions.check.js +1 -1
  295. package/schemas/plugins/container/ContainerPlugin.check.js +1 -1
  296. package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
  297. package/schemas/plugins/debug/ProfilingPlugin.check.js +1 -1
  298. package/schemas/plugins/dll/DllReferencePlugin.check.js +1 -1
  299. package/schemas/plugins/ids/HashedModuleIdsPlugin.check.js +1 -1
  300. package/schemas/plugins/schemes/HttpUriPlugin.check.js +1 -1
  301. package/types.d.ts +1795 -608
  302. package/lib/dependencies/ExportBindingInitFragment.js +0 -191
  303. package/lib/errors/EntrypointOverlapWarning.js +0 -45
  304. 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,6 +179,13 @@ const DEFER_BAILOUT =
178
179
  * @property {boolean | null} loaded whether the chunk loader fetched the chunk
179
180
  */
180
181
 
182
+ /**
183
+ * @typedef {object} WasmGroups
184
+ * @property {(key: string) => string} groupOf the group a runtime key answers with
185
+ * @property {Set<string>} fetching groups an entry of which reads binaries with `fetch`
186
+ * @property {Map<string, boolean>} onlyFetching groups every entry of which fetches
187
+ */
188
+
181
189
  // A reference in no chunk at all: nothing is known about where it is read from.
182
190
  /** @type {Placement} */
183
191
  const NO_PLACEMENT = { undo: null, loaded: null };
@@ -202,7 +210,12 @@ const RUNTIME_HASH_ROUND = 2;
202
210
 
203
211
  // Why a public path needing a base could not be spelled from the chunk holding it.
204
212
  const SERVED_BAILOUT =
205
- "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";
206
219
 
207
220
  /**
208
221
  * @import {
@@ -223,8 +236,9 @@ const SERVED_BAILOUT =
223
236
  * One piece of a name only the deferred pass can spell: text as it stands, the `../`
224
237
  * path from the asset it sits in to the output root, a template or `output.publicPath`
225
238
  * resolved once the hashes exist, one of the assets the chunk with this id emits, or
226
- * the base everything else is resolved against once it is spelled.
227
- * @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
228
242
  */
229
243
  /** @typedef {[SpecifierPartKind, string | number]} SpecifierPart */
230
244
 
@@ -236,14 +250,22 @@ const SPECIFIER_PART_KINDS = new Set([
236
250
  "unserved",
237
251
  "base",
238
252
  "chunk",
239
- "cssChunk"
253
+ "cssChunk",
254
+ "repair"
240
255
  ]);
241
256
 
242
257
  // Only these carry a chunk id, which is the one part value that may be a number.
243
258
  const CHUNK_SPECIFIER_PART_KINDS = new Set(["chunk", "cssChunk"]);
244
259
 
260
+ // Only these are spelled out of the compilation hash, so a stand-in holding one moves
261
+ // the chunk it lands in into the round after that hash settles.
262
+ const FULL_HASH_PART_KINDS = new Set(["publicPath", "template", "unserved"]);
263
+
245
264
  // What the deferred pass scans for — the other half of what the class spells.
265
+ const ANALYZABLE_TOKEN_PREFIX = "./@@webpackAnalyzableChunk:";
246
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-]+)@@/;
247
269
 
248
270
  // Stands in for the compilation hash inside an otherwise resolved filename, with the
249
271
  // requested length when the placeholder asked for one.
@@ -286,8 +308,7 @@ const PASS_NAME = "analyzableChunkNaming";
286
308
  /** @import { Source } from "webpack-sources" */
287
309
  /** @import Compiler from "./Compiler" */
288
310
  /** @import Chunk, { ChunkFilenameTemplate, ChunkId } from "./Chunk" */
289
- /** @import ChunkGroup from "./ChunkGroup" */
290
- /** @import ChunkGraph from "./ChunkGraph" */
311
+ /** @import ChunkGraph, { ModuleId } from "./ChunkGraph" */
291
312
  /** @import Compilation from "./Compilation" */
292
313
  /** @import Dependency from "./Dependency" */
293
314
  /** @import ModuleGraph from "./ModuleGraph" */
@@ -331,6 +352,25 @@ const createHashProbeChunk = (chunk, hash, present) => {
331
352
  return probeChunk;
332
353
  };
333
354
 
355
+ /**
356
+ * A filename function's answer to the stand-in hash, which says what shape the name
357
+ * has before any real hash exists.
358
+ * @param {Exclude<ChunkFilenameTemplate, string>} template the filename function
359
+ * @param {Chunk} chunk the chunk being referenced
360
+ * @param {string} contentHashType which of the chunk's hashes the name reads
361
+ * @param {string} hash the stand-in hash
362
+ * @param {boolean} present whether the chunk is claimed to carry content hashes
363
+ * @returns {string} what the function names it
364
+ */
365
+ const probeTemplateName = (template, chunk, contentHashType, hash, present) =>
366
+ template({
367
+ chunk: createHashProbeChunk(chunk, hash, present),
368
+ runtime: chunk.runtime,
369
+ contentHashType,
370
+ hash,
371
+ contentHash: hash
372
+ });
373
+
334
374
  /**
335
375
  * No module id error message.
336
376
  * @param {Module} module the module
@@ -385,6 +425,48 @@ function getGlobalObject(definition) {
385
425
  return `Object(${trimmed})`;
386
426
  }
387
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
+
388
470
  class RuntimeTemplate {
389
471
  /**
390
472
  * Creates an instance of RuntimeTemplate.
@@ -404,8 +486,8 @@ class RuntimeTemplate {
404
486
  (getGlobalObject(outputOptions.globalObject));
405
487
  /** @type {string} */
406
488
  this.contentHashReplacement = "X".repeat(outputOptions.hashDigestLength);
407
- /** @type {boolean | undefined} */
408
- this._javascriptNamedWithoutContent = undefined;
489
+ /** @type {Set<string>} */
490
+ this._analyzableRepairedAssets = new Set();
409
491
  /** @type {WeakMap<Chunk, boolean>} */
410
492
  this._chunkNamedWithoutContent = new WeakMap();
411
493
  /** @type {WeakMap<Chunk, string>} */
@@ -424,16 +506,20 @@ class RuntimeTemplate {
424
506
  this._publicPathClimbText = undefined;
425
507
  /** @type {WeakMap<Chunk, Placement>} */
426
508
  this._placementByChunk = new WeakMap();
509
+ /** @type {WeakMap<Chunk, string>} */
510
+ this._hotUpdateUndoByChunk = new WeakMap();
511
+ /** @type {string | undefined} */
512
+ this._hotUpdateShapeText = undefined;
427
513
  /** @type {Map<string, string | null | undefined>} */
428
514
  this._entryBaseUriByRuntime = new Map();
429
- /** @type {Map<string, string> | undefined} */
430
- this._wasmRuntimeGroupsByKey = undefined;
431
- /** @type {Set<string> | false | undefined} */
432
- this._wasmFetchingGroupSet = undefined;
515
+ /** @type {WasmGroups | undefined} */
516
+ this._wasmGroupsValue = undefined;
517
+ /** @type {string[] | undefined} */
518
+ this._analyzableBailoutSink = undefined;
519
+ /** @type {Module | undefined} */
520
+ this._analyzableGeneratingModule = undefined;
433
521
  /** @type {Map<string, boolean> | undefined} */
434
522
  this._wasmAnchorKnownByGroup = undefined;
435
- /** @type {Map<string, boolean> | undefined} */
436
- this._wasmGroupOnlyFetchesMap = undefined;
437
523
  }
438
524
 
439
525
  isIIFE() {
@@ -530,6 +616,10 @@ class RuntimeTemplate {
530
616
  return this.outputOptions.environment.generator;
531
617
  }
532
618
 
619
+ supportsTopLevelAwait() {
620
+ return this.outputOptions.environment.topLevelAwait;
621
+ }
622
+
533
623
  supportsOptionalChaining() {
534
624
  return this.outputOptions.environment.optionalChaining;
535
625
  }
@@ -578,6 +668,41 @@ class RuntimeTemplate {
578
668
  return this.outputOptions.environment.modulePreload;
579
669
  }
580
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
+
581
706
  /**
582
707
  * Whether a reference a foreign bundler can follow without running webpack's runtime
583
708
  * may be emitted — the one question every caller asks, in the form it is asking for:
@@ -620,131 +745,88 @@ class RuntimeTemplate {
620
745
  : undefined;
621
746
  // A reassigned `__webpack_public_path__` cannot reach a baked literal, and `.p`
622
747
  // belongs to a runtime, so only the runtimes it reaches keep the runtime form.
623
- // `url-inline` is unaffected: that form falls back to `.p + <file>`, which reads
624
- // the reassigned value at the call site rather than through the wrapper.
748
+ // `url-inline` is unaffected: it reads the value at the call site.
625
749
  if (
626
750
  form !== "url-inline" &&
627
751
  APIPlugin.runtimeUsesPublicPathOverride(
628
752
  this.compilation,
629
753
  scoped ? chunkGraph : undefined,
630
- placedModule
754
+ placedModule,
755
+ runtime
631
756
  )
632
757
  ) {
633
- this._analyzableBailout(
758
+ return this._analyzableBailout(
634
759
  module,
635
- "__webpack_public_path__ is reassigned in a runtime this module belongs to"
760
+ "__webpack_public_path__ is reassigned in a runtime this module belongs to",
761
+ false
636
762
  );
637
- return false;
638
763
  }
639
764
 
640
765
  if (form === "import") {
641
766
  // Read through a native `import()`, or it is not this feature at all.
642
- if (outputOptions.chunkFormat !== "module") {
643
- this._analyzableBailout(
644
- module,
645
- `output.chunkFormat is ${JSON.stringify(outputOptions.chunkFormat)}, so chunks are not read through a native import()`
646
- );
647
- return false;
648
- }
649
- if (outputOptions.importFunctionName !== "import") {
650
- this._analyzableBailout(
651
- module,
652
- `output.importFunctionName is ${JSON.stringify(outputOptions.importFunctionName)}, so the call site is not a native import()`
653
- );
654
- return false;
655
- }
656
- // A worker loading its own chunks some other way keeps that runtime; one on
657
- // `import` uses the same ESM loader as the main graph, so it can be analyzable.
658
- for (const originChunk of /** @type {ChunkGraph} */ (
659
- chunkGraph
660
- ).getModuleChunksIterable(/** @type {Module} */ (placedModule))) {
661
- const entryOptions = originChunk.getEntryOptions();
662
- if (!entryOptions || !entryOptions.worker) continue;
663
- // `WorkerAndWorkletPlugin` always seeds this from `output.workerChunkLoading`.
664
- if (entryOptions.chunkLoading !== "import") {
665
- this._analyzableBailout(
666
- module,
667
- `this worker loads its chunks with ${JSON.stringify(entryOptions.chunkLoading)}, not "import"`
668
- );
669
- return false;
670
- }
767
+ const outputReason = this._chunkImportOutputReason();
768
+ if (outputReason !== null) {
769
+ return this._analyzableBailout(module, outputReason, false);
671
770
  }
771
+ // A worker loading its own chunks some other way needs no answer here: it gets
772
+ // a chunk loader of its own, and only the ESM one asks this.
672
773
  return true;
673
774
  }
674
775
 
675
- // `url-inline` asks whether the file can be named at the call site at all, and
676
- // its `.p + <file>` fallback spells that without `import.meta` — so neither gate
677
- // below rules it out, and the asset's javascript wrapper stays dropped.
776
+ // `url-inline` asks only whether the file can be named at the call site, and
777
+ // its `.p + <file>` fallback spells that without `import.meta`, so the gate
778
+ // below does not rule it out.
779
+
780
+ // `environment.module` is deliberately not consulted: ESM output writes
781
+ // `import.meta` regardless, so the url forms match what the rest of the
782
+ // bundle already assumes the target reads.
678
783
  if (form === "url-inline") return true;
679
- // `import.meta` is ESM syntax the target has to read. A chunk `import()` is
680
- // emitted by the module chunk loader either way, so only the url forms check.
681
- if (!this.supportsEcmaScriptModuleSyntax()) {
682
- this._analyzableBailout(
683
- module,
684
- "output.environment.module is false, so the target does not read the import.meta this form needs"
685
- );
686
- return false;
687
- }
688
784
  // `eval` devtool wraps each module in `eval(...)`, where `import.meta` is a
689
785
  // syntax error. Runtime modules are emitted beside them, never wrapped.
690
- const { devtool } = this.compilation.options;
691
- if (
692
- form !== "url-runtime" &&
693
- typeof devtool === "string" &&
694
- devtool.includes("eval")
695
- ) {
696
- this._analyzableBailout(
786
+ const evalDevtool =
787
+ form === "url-runtime" ? undefined : this._evalWrappingDevtool();
788
+ if (evalDevtool !== undefined) {
789
+ return this._analyzableBailout(
697
790
  module,
698
- `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`,
792
+ false
699
793
  );
700
- return false;
701
794
  }
702
795
  if (form === "url" || form === "url-runtime") return true;
703
796
  // A bare relative URL is what `__webpack_require__.p + path` means only under an
704
797
  // `auto` public path; anything else has to be baked, which is the "wasm" form.
705
798
  if (form === "wasm-relative") {
706
799
  if (outputOptions.publicPath === "auto") return true;
707
- this._analyzableBailout(
800
+ return this._analyzableBailout(
708
801
  module,
709
- "output.publicPath is set, so a bare relative url no longer means what the public path would have resolved to"
802
+ "output.publicPath is set, so a bare relative url no longer means what the public path would have resolved to",
803
+ false
710
804
  );
711
- return false;
712
805
  }
713
806
 
714
807
  // The rest is the `"wasm"` form: whether the whole binary url can be spelled
715
808
  // here, rather than built at runtime under an `import.meta.url` base.
716
- const { publicPath, webassemblyModuleFilename } = outputOptions;
809
+ const { publicPath } = outputOptions;
717
810
  if (publicPath !== "auto") {
718
811
  // A public path that needs no base names the same place from the chunk as from
719
812
  // the document, so a literal spells what `fetch` would have reached.
720
813
  if (this._publicPathNeedsNoBase()) {
721
814
  // Settled no earlier than the hash it reads or is called with, so it is
722
815
  // baked only where the deferred pass may finish it.
723
- if (this._resolvePublicPathPrefix(publicPath, module) === null) {
816
+ if (this._resolvePublicPathPrefix(publicPath) === null) {
724
817
  return false;
725
818
  }
726
819
  } else if (
727
820
  this._anyWasmChunkFetches(runtime) &&
728
821
  !this._wasmFetchAnchorIsKnown(runtime)
729
822
  ) {
730
- this._analyzableBailout(
823
+ return this._analyzableBailout(
731
824
  module,
732
- "output.publicPath needs a base, and no one path back to the document fits every chunk `fetch` reads it from"
825
+ "output.publicPath needs a base, and no one path back to the document fits every chunk `fetch` reads it from",
826
+ false
733
827
  );
734
- return false;
735
828
  }
736
829
  }
737
- // The compilation hash is settled after code generation, so a name carrying one
738
- // is baked only where the deferred pass can fill it in. `[hash]` is the module's
739
- // own here, which code generation already knows.
740
- if (
741
- getTemplatedPathPlugin()
742
- .getPresentKinds(/** @type {string} */ (webassemblyModuleFilename))
743
- .has("fullhash") &&
744
- !this._canDeferOrBakeFullHash(true, undefined, module)
745
- ) {
746
- return false;
747
- }
748
830
  return true;
749
831
  }
750
832
 
@@ -753,45 +835,107 @@ class RuntimeTemplate {
753
835
  * the channel `ModuleConcatenationPlugin` already reports through, so it reaches
754
836
  * `stats.optimizationBailout`. Silent unless the build asked for ESM output, where
755
837
  * alone the answer is actionable; deduplicated because a module may write many.
838
+ * Answers with `answer` so a caller states the refusal and its reason at once —
839
+ * `false` where it answers a question, `null` where it was building a specifier.
840
+ * @template {false | null} T
756
841
  * @param {Module | undefined} module the module the reference is emitted into
757
842
  * @param {string} reason why no literal could be baked
843
+ * @param {T} answer what the caller hands back
844
+ * @returns {T} that answer
845
+ */
846
+ _analyzableBailout(module, reason, answer) {
847
+ if (module !== undefined && this.outputOptions.module) {
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}`;
852
+ const bailouts =
853
+ this.compilation.moduleGraph.getOptimizationBailout(target);
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
+ }
864
+ }
865
+ return answer;
866
+ }
867
+
868
+ /**
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
758
872
  * @returns {void}
759
873
  */
760
- _analyzableBailout(module, reason) {
761
- if (module === undefined || !this.outputOptions.module) return;
762
- const text = `Analyzable ESM bailout: ${reason}`;
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) {
763
898
  const bailouts =
764
899
  this.compilation.moduleGraph.getOptimizationBailout(module);
765
- for (const existing of bailouts) {
766
- if (existing === text) return;
900
+ for (const reason of reasons) {
901
+ const text = `${ANALYZABLE_BAILOUT_PREFIX}${reason}`;
902
+ if (!bailouts.includes(text)) bailouts.push(text);
767
903
  }
768
- bailouts.push(text);
769
904
  }
770
905
 
771
906
  /**
772
- * Whether a name that code generation cannot settle may be reserved as a stand-in
773
- * and filled in once the hashes exist. Substituting rewrites a chunk after its own
774
- * content hash was taken, so either `RealContentHashPlugin` has to bring the two
775
- * back in line, or no emitted javascript may be named by its content in the first
776
- * place — with a name like `[name].js` there is nothing to go stale. Asked of the
777
- * names the chunks actually carry rather than of `output`, so a template nothing is
778
- * emitted under does not rule the rewrite out and a `chunk.filenameTemplate` does
779
- * not slip past it.
780
- * @param {Iterable<Chunk>=} chunks the chunks the stand-in is written into; omit
781
- * where the answer has to hold for the whole compilation, as it does for a gate a
782
- * runtime module asks from the other end
783
- * @returns {boolean} true when deferring is safe
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
784
911
  */
785
- _canDeferAnalyzableName(chunks) {
786
- if (chunks === undefined) {
787
- // Memoized: the chunk graph is settled before any caller asks compilation-wide.
788
- if (this._javascriptNamedWithoutContent === undefined) {
789
- this._javascriptNamedWithoutContent = this._canDeferAnalyzableName(
790
- this.compilation.chunks
791
- );
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));
792
923
  }
793
- return this._javascriptNamedWithoutContent;
794
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) {
795
939
  let found = false;
796
940
  for (const chunk of chunks) {
797
941
  found = true;
@@ -802,16 +946,15 @@ class RuntimeTemplate {
802
946
  }
803
947
 
804
948
  /**
805
- * `_canDeferAnalyzableName`, recording why not when the answer is no — what every
806
- * caller with a module to report against does with it.
807
- * @param {Iterable<Chunk>=} chunks the chunks the stand-in is written into
808
- * @param {Module=} module the module the reference is emitted into
809
- * @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
810
955
  */
811
- _canDeferOrBail(chunks, module) {
812
- if (this._canDeferAnalyzableName(chunks)) return true;
813
- this._analyzableBailout(module, DEFER_BAILOUT);
814
- return false;
956
+ _needsRepair(chunks) {
957
+ return !this._canDeferAnalyzableName(chunks);
815
958
  }
816
959
 
817
960
  /**
@@ -819,37 +962,28 @@ class RuntimeTemplate {
819
962
  * and a stand-in for the deferred pass where they do not.
820
963
  * @param {SpecifierPart[]} parts the whole specifier
821
964
  * @param {Iterable<Chunk>} chunks the chunks it is written into
822
- * @param {Module=} module the module the reference is emitted into
823
- * @returns {string | null} it already quoted, or `null` when no stand-in may be
824
- * reserved
965
+ * @returns {string} it already quoted
825
966
  */
826
- _specifierOf(parts, chunks, module) {
967
+ _specifierOf(parts, chunks) {
827
968
  const text = literalText(parts);
828
969
  if (text !== null) return toJsStringLiteral(text);
829
- if (
830
- !this._canDeferOrBakeFullHash(
831
- this._partsBakeFullHash(parts),
832
- chunks,
833
- module
834
- )
835
- ) {
836
- return null;
837
- }
838
- 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
+ );
839
976
  }
840
977
 
841
978
  /**
842
- * `_canDeferOrBail` for a name the fill builds out of the compilation hash. Such a
843
- * stand-in settles the name it lands in for us: `_markChunksBakingFullHash` reads it
844
- * back and moves that chunk into the round after that hash, which is the same place a
845
- * chunk reaching for `__webpack_require__.p` ends up without asking.
846
- * @param {boolean} carriesFullHash whether the fill builds this one out of that hash
847
- * @param {Iterable<Chunk>=} chunks the chunks the stand-in is written into
848
- * @param {Module=} module the module the reference is emitted into
849
- * @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
850
984
  */
851
- _canDeferOrBakeFullHash(carriesFullHash, chunks, module) {
852
- return carriesFullHash || this._canDeferOrBail(chunks, module);
985
+ _markedForRepair(repair, parts) {
986
+ return repair ? [["repair", ""], ...parts] : parts;
853
987
  }
854
988
 
855
989
  /**
@@ -861,9 +995,7 @@ class RuntimeTemplate {
861
995
  */
862
996
  _partsBakeFullHash(parts) {
863
997
  for (const [kind, value] of parts) {
864
- if (kind === "publicPath" || kind === "template" || kind === "unserved") {
865
- return true;
866
- }
998
+ if (FULL_HASH_PART_KINDS.has(kind)) return true;
867
999
  if (kind === "literal" && this._hasReservedFullHash(String(value))) {
868
1000
  return true;
869
1001
  }
@@ -962,95 +1094,82 @@ class RuntimeTemplate {
962
1094
  const base = this.analyzableUrlReadsBaseUri()
963
1095
  ? this.entryBaseUri(runtime)
964
1096
  : undefined;
965
- if (base !== undefined) {
966
- if (base === null) {
967
- this._analyzableBailout(
968
- module,
969
- "the entries this module is generated for set different baseUri values, so no one url resolves for all of them"
970
- );
971
- return null;
972
- }
973
- // A relative one is no base of its own: the runtime reads it against the
974
- // chunk, so the literal spells it there rather than resolving against it.
975
- if (isChunkRelativeBaseUri(base)) {
976
- return this._getAnalyzableFileSpecifier(
977
- module,
978
- chunkGraph,
979
- [["literal", filename]],
980
- true,
981
- base
982
- );
983
- }
984
- const chunks = this._moduleChunks(module, chunkGraph);
985
- // A protocol-relative base names a host but no scheme, and the runtime reads it
986
- // against the chunk's own url to get one — the very url this literal is
987
- // resolved against — so it may stay protocol-relative instead of being settled
988
- // here. Nothing walks back to the output root first: the base replaces it.
989
- if (base.startsWith("//")) {
990
- const relative = this._resolvePublicPathPrefix(
991
- publicPath,
992
- module,
993
- chunks
994
- );
995
- return relative === null
996
- ? null
997
- : this._specifierOf(
998
- [
999
- ["literal", base],
1000
- ...rootedParts(relative),
1001
- ["literal", filename]
1002
- ],
1003
- chunks,
1004
- module
1005
- );
1006
- }
1007
- if (!isAbsoluteBaseUri(base)) {
1008
- this._analyzableBailout(
1009
- module,
1010
- `an entry sets a baseUri of ${JSON.stringify(base)}, whose scheme names no base anything can be resolved against`
1011
- );
1012
- return null;
1013
- }
1014
- const parts = this._resolvePublicPathPrefix(publicPath, module, chunks);
1015
- if (parts === null) return null;
1016
- // Resolved here rather than against the output root, and only an absolute base
1017
- // settles it — a relative one has no base of its own to be read against.
1018
- const spelled = literalText(parts);
1019
- // A hash the deferred pass still has to fill in cannot be resolved against the
1020
- // base here: the fill would land inside an already-resolved url, where a hash
1021
- // opening with a letter reads as a scheme and drops the base entirely.
1022
- const text =
1023
- spelled !== null && !this._hasReservedFullHash(spelled)
1024
- ? spelled
1025
- : null;
1026
- /** @type {URL} */
1027
- let resolved;
1028
- try {
1029
- // Empty where only the deferred pass can spell the rest, which still settles
1030
- // whether the base itself is one anything may be resolved against.
1031
- resolved = new URL(text === null ? "" : text + filename, base);
1032
- } catch (_error) {
1033
- this._analyzableBailout(
1034
- module,
1035
- `an entry sets a baseUri of ${JSON.stringify(base)}, which is not absolute, so no url can be resolved against it here`
1036
- );
1037
- return null;
1038
- }
1039
- if (text !== null) return toJsStringLiteral(resolved.href);
1040
- if (!this._canDeferOrBail(chunks, module)) return null;
1041
- return toJsStringLiteral(
1042
- this._reserveAnalyzableSpecifier([
1097
+ // No base of its own: the name is spelled against the output root, as a chunk is.
1098
+ if (base === undefined) {
1099
+ return this._getAnalyzableFileSpecifier(
1100
+ module,
1101
+ chunkGraph,
1102
+ [["literal", filename]],
1103
+ true
1104
+ );
1105
+ }
1106
+ if (base === null) {
1107
+ return this._analyzableBailout(
1108
+ module,
1109
+ "the entries this module is generated for set different baseUri values, so no one url resolves for all of them",
1110
+ null
1111
+ );
1112
+ }
1113
+ // A relative one is no base of its own: the runtime reads it against the
1114
+ // chunk, so the literal spells it there rather than resolving against it.
1115
+ if (isChunkRelativeBaseUri(base)) {
1116
+ return this._getAnalyzableFileSpecifier(
1117
+ module,
1118
+ chunkGraph,
1119
+ [["literal", filename]],
1120
+ true,
1121
+ base
1122
+ );
1123
+ }
1124
+ const chunks = this._moduleChunks(module, chunkGraph);
1125
+ const parts = this._resolvePublicPathPrefix(publicPath);
1126
+ if (parts === null) return null;
1127
+ // A protocol-relative base names a host but no scheme, and the runtime reads
1128
+ // it against the very url this literal resolves against, so it may stay that
1129
+ // way. Nothing walks back to the output root: the base replaces it.
1130
+ if (base.startsWith("//")) {
1131
+ return this._specifierOf(
1132
+ [["literal", base], ...rootedParts(parts), ["literal", filename]],
1133
+ chunks
1134
+ );
1135
+ }
1136
+ if (!isAbsoluteBaseUri(base)) {
1137
+ return this._analyzableBailout(
1138
+ module,
1139
+ `an entry sets a baseUri of ${JSON.stringify(base)}, whose scheme names no base anything can be resolved against`,
1140
+ null
1141
+ );
1142
+ }
1143
+ // Resolved here rather than against the output root, and only an absolute base
1144
+ // settles it — a relative one has no base of its own to be read against.
1145
+ const spelled = literalText(parts);
1146
+ // A hash the deferred pass still has to fill in cannot be resolved against the
1147
+ // base here: the fill would land inside an already-resolved url, where a hash
1148
+ // opening with a letter reads as a scheme and drops the base entirely.
1149
+ const text =
1150
+ spelled !== null && !this._hasReservedFullHash(spelled) ? spelled : null;
1151
+ /** @type {URL} */
1152
+ let resolved;
1153
+ try {
1154
+ // Empty where only the deferred pass can spell the rest, which still settles
1155
+ // whether the base itself is one anything may be resolved against.
1156
+ resolved = new URL(text === null ? "" : text + filename, base);
1157
+ } catch (_error) {
1158
+ return this._analyzableBailout(
1159
+ module,
1160
+ `an entry sets a baseUri of ${JSON.stringify(base)}, which is not absolute, so no url can be resolved against it here`,
1161
+ null
1162
+ );
1163
+ }
1164
+ if (text !== null) return toJsStringLiteral(resolved.href);
1165
+ return toJsStringLiteral(
1166
+ this._reserveAnalyzableSpecifier(
1167
+ this._markedForRepair(this._needsRepair(chunks), [
1043
1168
  ["base", base],
1044
1169
  ...parts,
1045
1170
  ["literal", filename]
1046
1171
  ])
1047
- );
1048
- }
1049
- return this._getAnalyzableFileSpecifier(
1050
- module,
1051
- chunkGraph,
1052
- [["literal", filename]],
1053
- true
1172
+ )
1054
1173
  );
1055
1174
  }
1056
1175
 
@@ -1072,10 +1191,9 @@ class RuntimeTemplate {
1072
1191
  let base;
1073
1192
  let found = false;
1074
1193
  for (const chunk of this.compilation.chunks) {
1075
- // What a literal has to agree with is the one value `BaseUriRuntimeModule`
1076
- // writes out, and it reads the entry options off the chunk it is emitted into
1077
- // — so only a chunk carrying a runtime has a say. Entries sharing one runtime
1078
- // chunk already share the base it sets, however their descriptors differ.
1194
+ // A literal must agree with the one value `BaseUriRuntimeModule` writes, and
1195
+ // that reads the entry options off the chunk it is emitted into, so only a
1196
+ // chunk carrying a runtime has a say.
1079
1197
  if (!chunk.hasRuntime()) continue;
1080
1198
  const entryOptions = chunk.getEntryOptions();
1081
1199
  // An entry this code cannot run under has no say in the base it resolves to.
@@ -1121,55 +1239,119 @@ class RuntimeTemplate {
1121
1239
  return publicPath.includes("[") ? undefined : publicPath;
1122
1240
  }
1123
1241
 
1242
+ /**
1243
+ * Runtime keys grouped by the binaries they share, with what the entries of each
1244
+ * group asked for. Code generation runs once for runtimes a module hashes alike in,
1245
+ * and nothing in a binary's hash knows which loader will read it — so a binary two
1246
+ * runtimes reach carries one shape into both, and a third runtime sharing another
1247
+ * binary with either is pulled in after them. Only an entry names a loader; every
1248
+ * other chunk of the runtime is served by the one its entry asked for. Asked of the
1249
+ * same modules the loaders are created for, and only once per compilation.
1250
+ * @returns {WasmGroups} the grouping, and which groups fetch
1251
+ */
1252
+ _wasmGroups() {
1253
+ if (this._wasmGroupsValue === undefined) {
1254
+ const { chunkGraph, chunks, modules } = this.compilation;
1255
+ /** @type {Map<string, string>} */
1256
+ const parent = new Map();
1257
+ /**
1258
+ * @param {string} key runtime key
1259
+ * @returns {string} the key naming its group
1260
+ */
1261
+ const find = (key) => {
1262
+ let root = key;
1263
+ let seen = parent.get(root);
1264
+ while (seen !== undefined && seen !== root) {
1265
+ root = seen;
1266
+ seen = parent.get(root);
1267
+ }
1268
+ // Path compression keeps a long share chain from costing more than once.
1269
+ let walk = key;
1270
+ let next = parent.get(walk);
1271
+ while (next !== undefined && next !== root) {
1272
+ parent.set(walk, root);
1273
+ walk = next;
1274
+ next = parent.get(walk);
1275
+ }
1276
+ return root;
1277
+ };
1278
+ /** @type {string[]} */
1279
+ const keys = [];
1280
+ for (const module of modules) {
1281
+ if (module.type !== WEBASSEMBLY_MODULE_TYPE_ASYNC) continue;
1282
+ keys.length = 0;
1283
+ for (const runtime of chunkGraph.getModuleRuntimes(module)) {
1284
+ forEachRuntime(runtime, (key) => {
1285
+ keys.push(/** @type {string} */ (key));
1286
+ });
1287
+ }
1288
+ for (const key of keys) if (!parent.has(key)) parent.set(key, key);
1289
+ // Every runtime this binary reaches answers with the first one.
1290
+ for (let i = 1; i < keys.length; i++) {
1291
+ parent.set(find(keys[i]), find(keys[0]));
1292
+ }
1293
+ }
1294
+ // Flattened once, so a lookup is a single `get` rather than a chain walk.
1295
+ for (const key of parent.keys()) parent.set(key, find(key));
1296
+ /**
1297
+ * @param {string} key a runtime key
1298
+ * @returns {string} the group it answers with, or itself when it shares nothing
1299
+ */
1300
+ const groupOf = (key) => {
1301
+ const group = parent.get(key);
1302
+ // An entry named "" makes "" a runtime key, so test absence, not a falsy value.
1303
+ return group === undefined ? key : group;
1304
+ };
1305
+ /** @type {Set<string>} */
1306
+ const fetching = new Set();
1307
+ /** @type {Map<string, boolean>} */
1308
+ const onlyFetching = new Map();
1309
+ for (const chunk of chunks) {
1310
+ if (!chunk.getEntryOptions()) continue;
1311
+ const fetches = this._chunkWasmLoading(chunk) === "fetch";
1312
+ forEachRuntime(chunk.runtime, (key) => {
1313
+ const group = groupOf(/** @type {string} */ (key));
1314
+ if (fetches) fetching.add(group);
1315
+ onlyFetching.set(group, onlyFetching.get(group) !== false && fetches);
1316
+ });
1317
+ }
1318
+ this._wasmGroupsValue = { groupOf, fetching, onlyFetching };
1319
+ }
1320
+ return this._wasmGroupsValue;
1321
+ }
1322
+
1323
+ /**
1324
+ * @param {RuntimeSpec} runtime the runtime being asked about
1325
+ * @returns {string[]} the wasm runtime groups it reaches
1326
+ */
1327
+ _wasmGroupsOf(runtime) {
1328
+ const { groupOf } = this._wasmGroups();
1329
+ /** @type {string[]} */
1330
+ const reached = [];
1331
+ forEachRuntime(runtime, (key) => {
1332
+ reached.push(groupOf(/** @type {string} */ (key)));
1333
+ });
1334
+ return reached;
1335
+ }
1336
+
1124
1337
  /**
1125
1338
  * Whether a chunk loads WebAssembly through `fetch`, which is the only loader the
1126
1339
  * public path reaches: `readFile` resolves the binary's name against the chunk it is
1127
1340
  * read from, exactly as a baked literal does, so a public path is irrelevant to it.
1128
- * Answered per runtime rather than per chunk on purpose — a module's generated source
1341
+ * Answered per group rather than per chunk on purpose — a module's generated source
1129
1342
  * and the runtime module of every chunk holding it have to agree on the shape, and
1130
- * they ask from opposite ends; one loader serves a whole runtime, so that is the
1131
- * finest scope on which they can. Without a runtime, or where the runtimes cannot be
1132
- * told apart, the answer covers the compilation.
1343
+ * they ask from opposite ends; one loader serves a whole group, so that is the finest
1344
+ * scope on which they can. Without a runtime the answer covers the compilation.
1133
1345
  * @param {RuntimeSpec=} runtime the runtime being asked about
1134
1346
  * @returns {boolean} true when a chunk of that runtime fetches its binaries
1135
1347
  */
1136
1348
  _anyWasmChunkFetches(runtime) {
1137
- const fetching = this._wasmFetchingGroups();
1138
- if (fetching === undefined) return false;
1349
+ const { fetching } = this._wasmGroups();
1350
+ if (fetching.size === 0) return false;
1139
1351
  // Without a runtime to place it in there is nothing to narrow by, so any chunk
1140
1352
  // answers for all of them.
1141
1353
  if (runtime === undefined) return true;
1142
- const groups = this._wasmRuntimeGroups();
1143
- if (typeof runtime === "string") {
1144
- return fetching.has(this._wasmGroupOf(groups, runtime));
1145
- }
1146
- for (const key of runtime) {
1147
- if (fetching.has(this._wasmGroupOf(groups, key))) return true;
1148
- }
1149
- return false;
1150
- }
1151
-
1152
- /**
1153
- * Whether every entry of a wasm runtime group reads its binaries with `fetch`. Only
1154
- * an entry names a loader, so the scan is of entries, as `_wasmFetchingGroups` is.
1155
- * @returns {Map<string, boolean>} whether each group fetches and nothing else
1156
- */
1157
- _wasmGroupOnlyFetches() {
1158
- if (this._wasmGroupOnlyFetchesMap === undefined) {
1159
- const groups = this._wasmRuntimeGroups();
1160
- /** @type {Map<string, boolean>} */
1161
- const only = new Map();
1162
- for (const chunk of this.compilation.chunks) {
1163
- if (!chunk.getEntryOptions()) continue;
1164
- const fetches = this._chunkWasmLoading(chunk) === "fetch";
1165
- forEachRuntime(chunk.runtime, (key) => {
1166
- const group = this._wasmGroupOf(groups, /** @type {string} */ (key));
1167
- only.set(group, only.get(group) !== false && fetches);
1168
- });
1169
- }
1170
- this._wasmGroupOnlyFetchesMap = only;
1171
- }
1172
- return this._wasmGroupOnlyFetchesMap;
1354
+ return this._wasmGroupsOf(runtime).some((group) => fetching.has(group));
1173
1355
  }
1174
1356
 
1175
1357
  /**
@@ -1182,30 +1364,31 @@ class RuntimeTemplate {
1182
1364
  _wasmFetchAnchorIsKnown(runtime) {
1183
1365
  if (this._wasmAnchorKnownByGroup === undefined) {
1184
1366
  const { chunkGraph, modules } = this.compilation;
1185
- const publicPath = /** @type {PublicPath} */ (
1186
- this.outputOptions.publicPath
1187
- );
1188
- const groups = this._wasmRuntimeGroups();
1367
+ const { groupOf, onlyFetching } = this._wasmGroups();
1189
1368
  /** @type {Map<string, boolean>} */
1190
1369
  const known = new Map();
1191
1370
  for (const module of modules) {
1192
1371
  if (module.type !== WEBASSEMBLY_MODULE_TYPE_ASYNC) continue;
1372
+ // Probed through the very builder the generator runs, so the gate is
1373
+ // exact: a prefix it can spell — the per-asset deferrals included — is
1374
+ // one the group may bake. The probe name stands in for the binary's.
1193
1375
  const spellable =
1194
- this._modulePlacement(module, chunkGraph).loaded !== null &&
1195
- this._resolvePublicPathPrefix(publicPath, module) !== null;
1376
+ this._getAnalyzableFileSpecifier(
1377
+ module,
1378
+ /** @type {ChunkGraph} */ (chunkGraph),
1379
+ [["literal", "x"]],
1380
+ true
1381
+ ) !== null;
1196
1382
  for (const moduleRuntime of chunkGraph.getModuleRuntimes(module)) {
1197
1383
  forEachRuntime(moduleRuntime, (key) => {
1198
- const group = this._wasmGroupOf(
1199
- groups,
1200
- /** @type {string} */ (key)
1201
- );
1384
+ const group = groupOf(/** @type {string} */ (key));
1202
1385
  known.set(group, known.get(group) !== false && spellable);
1203
1386
  });
1204
1387
  }
1205
1388
  }
1206
1389
  // A group one entry reads with `readFile` cannot take a prefix meant for the
1207
1390
  // document, and only an entry names its loader.
1208
- for (const [group, only] of this._wasmGroupOnlyFetches()) {
1391
+ for (const [group, only] of onlyFetching) {
1209
1392
  if (!only) known.set(group, false);
1210
1393
  }
1211
1394
  this._wasmAnchorKnownByGroup = known;
@@ -1217,124 +1400,9 @@ class RuntimeTemplate {
1217
1400
  for (const value of known.values()) if (!value) return false;
1218
1401
  return known.size > 0;
1219
1402
  }
1220
- const groups = this._wasmRuntimeGroups();
1221
- if (typeof runtime === "string") {
1222
- return known.get(this._wasmGroupOf(groups, runtime)) === true;
1223
- }
1224
- for (const key of runtime) {
1225
- if (known.get(this._wasmGroupOf(groups, key)) !== true) return false;
1226
- }
1227
- return true;
1228
- }
1229
-
1230
- /**
1231
- * The runtime groups an entry of which asked for the `fetch` loader, or `undefined`
1232
- * where none did — the cheap answer for a compilation with no fetching entry at all,
1233
- * which is every non-web target. Only an entry names a loader; every other chunk of
1234
- * the runtime is served by the one its entry asked for.
1235
- * @returns {Set<string> | undefined} the groups that fetch
1236
- */
1237
- _wasmFetchingGroups() {
1238
- if (this._wasmFetchingGroupSet === undefined) {
1239
- /** @type {Set<string>} */
1240
- const fetching = new Set();
1241
- /** @type {Map<string, string> | undefined} */
1242
- let groups;
1243
- for (const chunk of this.compilation.chunks) {
1244
- // Only an entry names a loader; every other chunk of the runtime is served
1245
- // by the one its entry asked for, so it says nothing on its own.
1246
- if (!chunk.getEntryOptions()) continue;
1247
- if (this._chunkWasmLoading(chunk) !== "fetch") continue;
1248
- // Built only once something fetches, so nothing pays for the grouping.
1249
- if (groups === undefined) groups = this._wasmRuntimeGroups();
1250
- const named = /** @type {Map<string, string>} */ (groups);
1251
- forEachRuntime(chunk.runtime, (key) => {
1252
- fetching.add(this._wasmGroupOf(named, /** @type {string} */ (key)));
1253
- });
1254
- }
1255
- this._wasmFetchingGroupSet = fetching.size === 0 ? false : fetching;
1256
- }
1257
- return this._wasmFetchingGroupSet === false
1258
- ? undefined
1259
- : this._wasmFetchingGroupSet;
1260
- }
1261
-
1262
- /**
1263
- * Runtime keys mapped to the group they answer with, named by one key of it. Code
1264
- * generation runs once for runtimes a module hashes alike in, and nothing in a
1265
- * binary's hash knows which loader will read it — so a binary two runtimes reach
1266
- * carries one shape into both, and a third runtime sharing another binary with
1267
- * either is pulled in after them. A key in no group answers as itself. Asked of the
1268
- * same modules the loaders are created for, and only once per compilation.
1269
- * @returns {Map<string, string>} runtime key to the group it answers with
1270
- */
1271
- _wasmRuntimeGroups() {
1272
- if (this._wasmRuntimeGroupsByKey === undefined) {
1273
- const { chunkGraph } = this.compilation;
1274
- /** @type {Map<string, string>} */
1275
- const parent = new Map();
1276
- /**
1277
- * @param {string} key runtime key
1278
- * @returns {string} the key naming its group
1279
- */
1280
- const find = (key) => {
1281
- let root = key;
1282
- let seen = parent.get(root);
1283
- while (seen !== undefined && seen !== root) {
1284
- root = seen;
1285
- seen = parent.get(root);
1286
- }
1287
- // Path compression keeps a long share chain from costing more than once.
1288
- let walk = key;
1289
- let next = parent.get(walk);
1290
- while (next !== undefined && next !== root) {
1291
- parent.set(walk, root);
1292
- walk = next;
1293
- next = parent.get(walk);
1294
- }
1295
- return root;
1296
- };
1297
- /** @type {string[]} */
1298
- const keys = [];
1299
- /**
1300
- * @param {RuntimeSpec} runtime a runtime the module is generated for
1301
- * @returns {void}
1302
- */
1303
- const collectKeys = (runtime) => {
1304
- forEachRuntime(runtime, (key) => {
1305
- keys.push(/** @type {string} */ (key));
1306
- });
1307
- };
1308
- for (const module of this.compilation.modules) {
1309
- if (module.type !== WEBASSEMBLY_MODULE_TYPE_ASYNC) continue;
1310
- keys.length = 0;
1311
- for (const runtime of chunkGraph.getModuleRuntimes(module)) {
1312
- collectKeys(runtime);
1313
- }
1314
- for (const key of keys) {
1315
- if (!parent.has(key)) parent.set(key, key);
1316
- }
1317
- // Every runtime this binary reaches answers with the first one.
1318
- for (let i = 1; i < keys.length; i++) {
1319
- parent.set(find(keys[i]), find(keys[0]));
1320
- }
1321
- }
1322
- // Flattened once, so a lookup is a single `get` rather than a chain walk.
1323
- for (const key of parent.keys()) parent.set(key, find(key));
1324
- this._wasmRuntimeGroupsByKey = parent;
1325
- }
1326
- return this._wasmRuntimeGroupsByKey;
1327
- }
1328
-
1329
- /**
1330
- * @param {Map<string, string>} groups runtime key to the group it answers with
1331
- * @param {string} key a runtime key
1332
- * @returns {string} the group it answers with, or itself when it shares nothing
1333
- */
1334
- _wasmGroupOf(groups, key) {
1335
- const group = groups.get(key);
1336
- // An entry named "" makes "" a runtime key, so test absence, not a falsy value.
1337
- return group === undefined ? key : group;
1403
+ return this._wasmGroupsOf(runtime).every(
1404
+ (group) => known.get(group) === true
1405
+ );
1338
1406
  }
1339
1407
 
1340
1408
  supportTemplateLiteral() {
@@ -1413,14 +1481,13 @@ class RuntimeTemplate {
1413
1481
  }
1414
1482
  if (!this.supportTemplateLiteral()) return this._es5Concatenation(args);
1415
1483
 
1416
- // cost comparison between template literal and concatenation:
1417
- // both need equal surroundings: `xxx` vs "xxx"
1418
- // template literal has constant cost of 3 chars for each expression
1419
- // es5 concatenation has cost of 3 + n chars for n expressions in row
1420
- // when a es5 concatenation ends with an expression it reduces cost by 3
1421
- // when a es5 concatenation starts with an single expression it reduces cost by 3
1422
- // e. g. `${a}${b}${c}` (3*3 = 9) is longer than ""+a+b+c ((3+3)-3 = 3)
1423
- // e. g. `x${a}x${b}x${c}x` (3*3 = 9) is shorter than "x"+a+"x"+b+"x"+c+"x" (4+4+4 = 12)
1484
+ // WHY: cost comparison between a template literal and a concatenation, both
1485
+ // needing equal surroundings (`xxx` against "xxx"). A template literal costs
1486
+ // a constant 3 chars per expression; an es5 concatenation costs 3 + n chars
1487
+ // for n expressions in a row, and reduces by 3 where it ends with an
1488
+ // expression or starts with a single one. So `${a}${b}${c}` (3*3 = 9) is
1489
+ // longer than ""+a+b+c ((3+3)-3 = 3), while `x${a}x${b}x${c}x` (3*3 = 9) is
1490
+ // shorter than "x"+a+"x"+b+"x"+c+"x" (4+4+4 = 12).
1424
1491
 
1425
1492
  let templateCost = 0;
1426
1493
  let concatenationCost = 0;
@@ -1897,14 +1964,21 @@ class RuntimeTemplate {
1897
1964
  });
1898
1965
  const exportsType = module.getExportsType(chunkGraph.moduleGraph, strict);
1899
1966
  switch (exportsType) {
1900
- case "namespace":
1901
- return this.moduleRaw({
1967
+ case "namespace": {
1968
+ const raw = this.moduleRaw({
1902
1969
  module,
1903
1970
  chunkGraph,
1904
1971
  request,
1905
1972
  weak,
1906
1973
  runtimeRequirements
1907
1974
  });
1975
+
1976
+ if (!usesSpecNamespaceObject(module)) return raw;
1977
+
1978
+ runtimeRequirements.add(RuntimeGlobals.specNamespaceObject);
1979
+
1980
+ return `${RuntimeGlobals.specNamespaceObject}(${raw})`;
1981
+ }
1908
1982
  case "default-with-named":
1909
1983
  runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
1910
1984
  return `${RuntimeGlobals.createFakeNamespaceObject}(${moduleId}, 3)`;
@@ -2013,12 +2087,30 @@ class RuntimeTemplate {
2013
2087
 
2014
2088
  let mode = getMakeDeferredNamespaceModeFromExportsType(exportsType);
2015
2089
  if (mode) mode = `${mode} | 16`;
2090
+ // The spec's namespace keys are the sorted exports, so hand them over
2091
+ // when they are fully known; otherwise the runtime stays permissive.
2092
+ const deferredExportNames = getKnownExportNames(
2093
+ chunkGraph.moduleGraph,
2094
+ module,
2095
+ exportsType
2096
+ );
2097
+ runtimeRequirements.add(
2098
+ deferredExportNames
2099
+ ? RuntimeGlobals.hasSealedDeferredNamespace
2100
+ : RuntimeGlobals.hasSynthesizedDeferredNamespace
2101
+ );
2102
+ mode += deferredExportNames
2103
+ ? `, ${JSON.stringify(deferredExportNames)}`
2104
+ : "";
2016
2105
 
2017
2106
  const asyncDeps = Array.from(
2018
2107
  getOutgoingAsyncModules(chunkGraph.moduleGraph, module),
2019
2108
  (m) => chunkGraph.getModuleId(m)
2020
2109
  ).filter((id) => id !== null);
2021
2110
  if (asyncDeps.length) {
2111
+ runtimeRequirements.add(
2112
+ RuntimeGlobals.deferredModuleAsyncTransitiveDependencies
2113
+ );
2022
2114
  if (header) {
2023
2115
  appending = `.then(${this.basicFunction(
2024
2116
  "",
@@ -2053,15 +2145,22 @@ class RuntimeTemplate {
2053
2145
  } else {
2054
2146
  let fakeType = 16;
2055
2147
  switch (exportsType) {
2056
- case "namespace":
2057
- if (header) {
2058
- const rawModule = this.moduleRaw({
2148
+ case "namespace": {
2149
+ const specNamespace = usesSpecNamespaceObject(module);
2150
+ const asyncModule = chunkGraph.moduleGraph.isAsync(module);
2151
+ if (header || (specNamespace && !asyncModule)) {
2152
+ let rawModule = this.moduleRaw({
2059
2153
  module,
2060
2154
  chunkGraph,
2061
2155
  request,
2062
2156
  weak,
2063
2157
  runtimeRequirements
2064
2158
  });
2159
+ if (specNamespace && !asyncModule) {
2160
+ runtimeRequirements.add(RuntimeGlobals.specNamespaceObject);
2161
+ // Wrap before promise resolution assimilates an exported `then`.
2162
+ rawModule = `${RuntimeGlobals.specNamespaceObject}(${rawModule})`;
2163
+ }
2065
2164
  appending = `.then(${this.basicFunction(
2066
2165
  "",
2067
2166
  `${header}return ${rawModule};`
@@ -2073,7 +2172,14 @@ class RuntimeTemplate {
2073
2172
  `${comment}${idExpr}`
2074
2173
  )})`;
2075
2174
  }
2175
+
2176
+ if (specNamespace && asyncModule) {
2177
+ runtimeRequirements.add(RuntimeGlobals.specNamespaceObject);
2178
+ appending += `.then(${RuntimeGlobals.specNamespaceObject})`;
2179
+ }
2180
+
2076
2181
  break;
2182
+ }
2077
2183
  case "dynamic":
2078
2184
  fakeType |= 4;
2079
2185
  /* fall through */
@@ -2127,6 +2233,15 @@ class RuntimeTemplate {
2127
2233
  )})`;
2128
2234
  }
2129
2235
  }
2236
+ if (synthesizesSpecNamespaceObject(originModule)) {
2237
+ runtimeRequirements.add(RuntimeGlobals.specNamespaceObject);
2238
+ runtimeRequirements.add(RuntimeGlobals.moduleCache);
2239
+ const owner = synthesizedNamespaceOwner(moduleId, fakeType);
2240
+ appending += `.then(${this.returningFunction(
2241
+ `${RuntimeGlobals.specNamespaceObject}(m${owner})`,
2242
+ "m"
2243
+ )})`;
2244
+ }
2130
2245
  break;
2131
2246
  }
2132
2247
  }
@@ -2235,10 +2350,9 @@ class RuntimeTemplate {
2235
2350
  request,
2236
2351
  weak
2237
2352
  });
2238
- // Harmony imports may be wrapped in runtime-condition `if` blocks
2239
- // but referenced outside those blocks (e.g. by harmony reexport),
2240
- // so they must remain function-scoped (`var`) rather than
2241
- // block-scoped (`let`/`const`).
2353
+ // A harmony import may sit inside a runtime-condition `if` block yet be
2354
+ // referenced outside it, so it stays function-scoped with `var` rather than
2355
+ // block-scoped.
2242
2356
  const optDeclaration = update ? "" : "var ";
2243
2357
 
2244
2358
  const exportsType = module.getExportsType(
@@ -2255,11 +2369,14 @@ class RuntimeTemplate {
2255
2369
  (dependency instanceof HarmonyImportDependency ||
2256
2370
  dependency instanceof ImportDependency) &&
2257
2371
  ImportPhaseUtils.isDefer(dependency.phase) &&
2258
- !(/** @type {BuildMeta} */ (module.buildMeta).async);
2372
+ !isAsyncExternal(module);
2259
2373
 
2260
2374
  if (isModuleDeferred) {
2261
2375
  /** @type {Set<Module>} */
2262
2376
  const outgoingAsyncModules = getOutgoingAsyncModules(moduleGraph, module);
2377
+ // A module deferring itself is already evaluating, so awaiting it here
2378
+ // would deadlock; forcing the namespace throws instead.
2379
+ outgoingAsyncModules.delete(originModule);
2263
2380
 
2264
2381
  importContent = `/* deferred harmony import */ ${optDeclaration}${importVar} = ${getOptimizedDeferredModule(
2265
2382
  moduleId,
@@ -2344,7 +2461,7 @@ class RuntimeTemplate {
2344
2461
  (dependency instanceof HarmonyImportDependency ||
2345
2462
  dependency instanceof ImportDependency) &&
2346
2463
  ImportPhaseUtils.isDefer(dependency.phase) &&
2347
- !(/** @type {BuildMeta} */ (module.buildMeta).async);
2464
+ !isAsyncExternal(module);
2348
2465
 
2349
2466
  if (defaultInterop) {
2350
2467
  // when the defaultInterop is used (when a ESM imports a CJS module),
@@ -2374,10 +2491,8 @@ class RuntimeTemplate {
2374
2491
  }
2375
2492
  // accessing the .default property is same thing as `require()` the module.
2376
2493
 
2377
- // For example:
2378
- // import mod from "cjs"; mod.default.x;
2379
- // is translated to
2380
- // var mod = require("cjs"); mod.x;
2494
+ // So `import mod from "cjs"; mod.default.x` translates to
2495
+ // `var mod = require("cjs"); mod.x`.
2381
2496
  switch (exportsType) {
2382
2497
  case "dynamic":
2383
2498
  if (isCall) {
@@ -2417,10 +2532,9 @@ class RuntimeTemplate {
2417
2532
  exportsType === "default-only" ||
2418
2533
  exportsType === "default-with-named"
2419
2534
  ) {
2420
- // now exportName.length is 0, which means the namespace object is used in an unknown way
2421
- // for example:
2422
- // import * as ns from "cjs"; console.log(ns);
2423
- // we will need to createFakeNamespaceObject that simulates ES Module namespace object
2535
+ // `exportName.length` is 0, so the namespace object is used in an unknown
2536
+ // way — `import * as ns from "cjs"; console.log(ns)` — and needs
2537
+ // `createFakeNamespaceObject` to simulate an ES module namespace.
2424
2538
  runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
2425
2539
  initFragments.push(
2426
2540
  new InitFragment(
@@ -2430,16 +2544,38 @@ class RuntimeTemplate {
2430
2544
  `${importVar}_namespace_cache`
2431
2545
  )
2432
2546
  );
2547
+ const fakeMode = exportsType === "default-only" ? 0 : 2;
2548
+ const fakeArguments = fakeMode === 0 ? "" : ", 2";
2549
+ let fakeNamespace = `${RuntimeGlobals.createFakeNamespaceObject}(${importVar}${fakeArguments})`;
2550
+ if (synthesizesSpecNamespaceObject(originModule)) {
2551
+ runtimeRequirements.add(RuntimeGlobals.specNamespaceObject);
2552
+ runtimeRequirements.add(RuntimeGlobals.moduleCache);
2553
+ const owner = synthesizedNamespaceOwner(
2554
+ chunkGraph.getModuleId(module),
2555
+ fakeMode
2556
+ );
2557
+ fakeNamespace = `${RuntimeGlobals.specNamespaceObject}(${fakeNamespace}${owner})`;
2558
+ }
2433
2559
  return `/*#__PURE__*/ ${
2434
2560
  asiSafe ? "" : asiSafe === false ? ";" : "Object"
2435
- }(${importVar}_namespace_cache || (${importVar}_namespace_cache = ${
2436
- RuntimeGlobals.createFakeNamespaceObject
2437
- }(${importVar}${exportsType === "default-only" ? "" : ", 2"})))`;
2561
+ }(${importVar}_namespace_cache || (${importVar}_namespace_cache = ${fakeNamespace}))`;
2438
2562
  }
2439
2563
  }
2440
2564
 
2441
2565
  if (exportName.length > 0) {
2442
2566
  const exportsInfo = moduleGraph.getExportsInfo(module);
2567
+ // A name the module does not export is the namespace's to answer — reading
2568
+ // it is undefined and writing it fails — not the exports object's.
2569
+ if (
2570
+ !isModuleDeferred &&
2571
+ usesSpecNamespaceObject(module) &&
2572
+ exportsInfo.isExportProvided(exportName) === false
2573
+ ) {
2574
+ runtimeRequirements.add(RuntimeGlobals.specNamespaceObject);
2575
+ const ns = `${RuntimeGlobals.specNamespaceObject}(${importVar})`;
2576
+
2577
+ return `${ns}${propertyAccess(exportName)}`;
2578
+ }
2443
2579
  // in some case the exported item is renamed (get this by getUsedName). for example,
2444
2580
  // x.default might be emitted as x.Z (default is renamed to Z)
2445
2581
  const used = exportsInfo.getUsedName(exportName, runtime);
@@ -2484,9 +2620,19 @@ class RuntimeTemplate {
2484
2620
  runtimeRequirements.add(RuntimeGlobals.makeDeferredNamespaceObject);
2485
2621
  const id = chunkGraph.getModuleId(module);
2486
2622
  const type = getMakeDeferredNamespaceModeFromExportsType(exportsType);
2487
- const init = `${
2488
- RuntimeGlobals.makeDeferredNamespaceObject
2489
- }(${JSON.stringify(id)}, ${type})`;
2623
+ const names = getKnownExportNames(
2624
+ chunkGraph.moduleGraph,
2625
+ module,
2626
+ exportsType
2627
+ );
2628
+ runtimeRequirements.add(
2629
+ names
2630
+ ? RuntimeGlobals.hasSealedDeferredNamespace
2631
+ : RuntimeGlobals.hasSynthesizedDeferredNamespace
2632
+ );
2633
+ const init = `${RuntimeGlobals.makeDeferredNamespaceObject}(${JSON.stringify(
2634
+ id
2635
+ )}, ${type}${names ? `, ${JSON.stringify(names)}` : ""})`;
2490
2636
 
2491
2637
  return `/*#__PURE__*/ ${
2492
2638
  asiSafe ? "" : asiSafe === false ? ";" : "Object"
@@ -2513,6 +2659,16 @@ class RuntimeTemplate {
2513
2659
  // if we hit here, the importVar is either
2514
2660
  // - already a ES module namespace object
2515
2661
  // - or imported by a way that does not need interop.
2662
+ if (
2663
+ exportsType === "namespace" &&
2664
+ exportName.length === 0 &&
2665
+ usesSpecNamespaceObject(module)
2666
+ ) {
2667
+ runtimeRequirements.add(RuntimeGlobals.specNamespaceObject);
2668
+
2669
+ return `${RuntimeGlobals.specNamespaceObject}(${importVar})`;
2670
+ }
2671
+
2516
2672
  return importVar;
2517
2673
  }
2518
2674
 
@@ -2593,7 +2749,7 @@ class RuntimeTemplate {
2593
2749
  * @param {string} options.message the message
2594
2750
  * @param {ChunkGraph} options.chunkGraph the chunk graph
2595
2751
  * @param {RuntimeRequirements} options.runtimeRequirements if set, will be filled with runtime requirements
2596
- * @param {Module=} options.originModule the module the `import()` is emitted into
2752
+ * @param {Module=} options.originModule the module the `import()` is emitted into, to report against
2597
2753
  * @returns {string} expression
2598
2754
  */
2599
2755
  blockPromise({
@@ -2607,165 +2763,48 @@ class RuntimeTemplate {
2607
2763
  const comment = this.comment({
2608
2764
  message
2609
2765
  });
2610
- return `Promise.resolve(${comment.trim()})`;
2611
- }
2612
- const chunkGroup = chunkGraph.getBlockChunkGroup(block);
2613
- if (!chunkGroup || chunkGroup.chunks.length === 0) {
2614
- const comment = this.comment({
2615
- message
2616
- });
2617
- return `Promise.resolve(${comment.trim()})`;
2618
- }
2619
- const chunks = chunkGroup.chunks.filter(
2620
- (chunk) => !chunk.hasRuntime() && chunk.id !== null
2621
- );
2622
- const comment = this.comment({
2623
- message,
2624
- chunkName: block.chunkName
2625
- });
2626
- // `fetchPriority` is unsupported for ESM output (a native `import()` can't carry it,
2627
- // and the ESM chunk loader ignores the argument), so it never blocks the analyzable form.
2628
- // TODO recheck: browsers honor `fetchpriority` on `modulepreload` only when the
2629
- // preload scanner sees it, so a runtime-injected hint is inert; support it here if
2630
- // that changes.
2631
- const fetchPriority = chunkGroup.options.fetchPriority;
2632
- if (chunks.length === 1) {
2633
- const analyzable = this.analyzableChunkImport(
2634
- chunks[0],
2635
- comment,
2636
- runtimeRequirements,
2637
- originModule,
2638
- chunkGraph
2639
- );
2640
- if (analyzable !== null) {
2641
- return analyzable;
2642
- }
2643
- const chunkId = JSON.stringify(chunks[0].id);
2644
- runtimeRequirements.add(RuntimeGlobals.ensureChunk);
2645
-
2646
- if (fetchPriority) {
2647
- runtimeRequirements.add(RuntimeGlobals.hasFetchPriority);
2648
- }
2649
-
2650
- return `${RuntimeGlobals.ensureChunk}(${comment}${chunkId}${
2651
- fetchPriority ? `, ${JSON.stringify(fetchPriority)}` : ""
2652
- })`;
2653
- } else if (chunks.length > 0) {
2654
- let needEnsureChunk = false;
2655
- /**
2656
- * Analyzable `import()` for a solely-owned JS chunk, else runtime ensureChunk.
2657
- * @param {Chunk} chunk chunk
2658
- * @returns {string} require chunk id code
2659
- */
2660
- const requireChunkId = (chunk) => {
2661
- const analyzable = this.analyzableChunkImport(
2662
- chunk,
2663
- "",
2664
- runtimeRequirements,
2665
- originModule,
2666
- chunkGraph
2667
- );
2668
- if (analyzable !== null) return analyzable;
2669
- needEnsureChunk = true;
2670
- return `${RuntimeGlobals.ensureChunk}(${JSON.stringify(chunk.id)}${
2671
- fetchPriority ? `, ${JSON.stringify(fetchPriority)}` : ""
2672
- })`;
2673
- };
2674
- const items = chunks.map(requireChunkId);
2675
- if (needEnsureChunk) {
2676
- runtimeRequirements.add(RuntimeGlobals.ensureChunk);
2677
- // Only needed when an `ensureChunk(id, priority)` call is actually emitted.
2678
- if (fetchPriority) {
2679
- runtimeRequirements.add(RuntimeGlobals.hasFetchPriority);
2680
- }
2681
- }
2682
- return `Promise.all(${comment.trim()}[${items.join(", ")}])`;
2683
- }
2684
- return `Promise.resolve(${comment.trim()})`;
2685
- }
2686
-
2687
- /**
2688
- * For ESM module output, load a single statically-named chunk through the
2689
- * `analyzableChunkImport` helper — a literal `import("./chunk.js")` other bundlers
2690
- * and webpack itself can follow, wrapped to keep `ensureChunk` timing and deduplication.
2691
- * Returns `null` to fall back to the runtime `ensureChunk` form.
2692
- * @param {Chunk} chunk the chunk to load
2693
- * @param {string} comment leading comment (chunk name / message)
2694
- * @param {RuntimeRequirements} runtimeRequirements runtime requirements
2695
- * @param {Module | undefined} originModule the module the `import()` is emitted into
2696
- * @param {ChunkGraph} chunkGraph the chunk graph
2697
- * @returns {string | null} the import expression, or `null`
2698
- */
2699
- analyzableChunkImport(
2700
- chunk,
2701
- comment,
2702
- runtimeRequirements,
2703
- originModule,
2704
- chunkGraph
2705
- ) {
2706
- // `blockPromise` has already dropped any chunk without an id. Without an origin
2707
- // there is nothing the specifier could be relative to.
2708
- if (
2709
- !originModule ||
2710
- !this.supportsAnalyzable("import", chunkGraph, originModule)
2711
- ) {
2712
- return null;
2766
+ return `Promise.resolve(${comment.trim()})`;
2713
2767
  }
2714
- // Prefetch/preload children are injected by the runtime `.f` handlers, which `.ei`
2715
- // runs too — but they attach to `.f`, so it has to exist.
2716
- const needsHintHandlers =
2717
- chunk.hasChildByOrder(chunkGraph, "prefetch", true) ||
2718
- chunk.hasChildByOrder(chunkGraph, "preload", true) ||
2719
- chunk.hasChildByOrder(chunkGraph, "cssPreload", true);
2720
- // `.ei` always performs the import, where `.e` only reaches the javascript
2721
- // loader when there is javascript to load. A module federation remote is the
2722
- // case that matters: its chunk is emitted by the container's build, not this one.
2723
- if (!JavascriptModulesPlugin.chunkHasJs(chunk, chunkGraph)) {
2724
- this._analyzableBailout(
2725
- originModule,
2726
- "this compilation emits no javascript for the chunk, so there is nothing to import"
2727
- );
2728
- return null;
2768
+ const chunkGroup = chunkGraph.getBlockChunkGroup(block);
2769
+ if (!chunkGroup || chunkGroup.chunks.length === 0) {
2770
+ const comment = this.comment({
2771
+ message
2772
+ });
2773
+ return `Promise.resolve(${comment.trim()})`;
2729
2774
  }
2730
- let hasNonJsTypes = false;
2731
- for (const module of chunkGraph.getChunkModulesIterable(chunk)) {
2732
- for (const type of chunkGraph.getModuleSourceTypes(module)) {
2733
- if (!TYPES_WITHOUT_CHUNK_HANDLER.has(type)) {
2734
- // Loaded by that type's own `.f` handler, which `.ei` still dispatches.
2735
- hasNonJsTypes = true;
2736
- }
2775
+ const chunks = chunkGroup.chunks.filter(
2776
+ (chunk) => !chunk.hasRuntime() && chunk.id !== null
2777
+ );
2778
+ const comment = this.comment({
2779
+ message,
2780
+ chunkName: block.chunkName
2781
+ });
2782
+ // An output that cannot use a native `import()` gets no ESM chunk loader to ask,
2783
+ // so the reason is recorded here, where any build generates its imports.
2784
+ if (this.outputOptions.module && originModule !== undefined) {
2785
+ const outputReason = this._chunkImportOutputReason();
2786
+ if (outputReason !== null) {
2787
+ this._analyzableBailout(originModule, outputReason, false);
2737
2788
  }
2738
2789
  }
2739
- // Relative to the consuming chunk (`import.meta.url`) for `auto`, else an
2740
- // absolute publicPath prefix.
2741
- const specifier = this._getAnalyzableChunkSpecifier(
2742
- undefined,
2743
- chunk,
2744
- originModule,
2745
- chunkGraph
2746
- );
2747
- if (specifier === null) {
2748
- return null;
2790
+ const fetchPriority = chunkGroup.options.fetchPriority;
2791
+ const priorityArg = fetchPriority
2792
+ ? `, ${JSON.stringify(fetchPriority)}`
2793
+ : "";
2794
+ if (chunks.length === 0) return `Promise.resolve(${comment.trim()})`;
2795
+ runtimeRequirements.add(RuntimeGlobals.ensureChunk);
2796
+ if (fetchPriority) {
2797
+ runtimeRequirements.add(RuntimeGlobals.hasFetchPriority);
2749
2798
  }
2750
- // `import()` needs a resolvable specifier — relative (`./`, `../`), absolute (`/`)
2751
- // or a URL scheme. A bare one is a package name, so make it explicitly relative
2752
- // the way the chunk loader does. (`new URL(...)` callers accept bare vs their base.)
2753
- const resolvableSpecifier = /^"(?:\.{0,2}\/|[a-zA-Z][\w+.-]*:)/.test(
2754
- specifier
2755
- )
2756
- ? specifier
2757
- : `"./${specifier.slice(1)}`;
2758
- runtimeRequirements.add(RuntimeGlobals.analyzableChunkImport);
2759
- if (needsHintHandlers || hasNonJsTypes) {
2760
- // Those handlers attach to the `.f` map, which has to exist — but the runtime
2761
- // `ensureChunk` around it does not, since `.ei` dispatches them itself.
2762
- runtimeRequirements.add(RuntimeGlobals.ensureChunkHandlers);
2799
+ if (chunks.length === 1) {
2800
+ const chunkId = JSON.stringify(chunks[0].id);
2801
+ return `${RuntimeGlobals.ensureChunk}(${comment}${chunkId}${priorityArg})`;
2763
2802
  }
2764
- // Drop-in for `ensureChunk(id)`: the literal `import()` can be statically
2765
- // followed, the helper keeps webpack's install timing and deduplication.
2766
- return `${RuntimeGlobals.analyzableChunkImport}(${JSON.stringify(
2767
- chunk.id
2768
- )}, ${this.returningFunction(`import(${comment}${resolvableSpecifier})`)})`;
2803
+ const items = chunks.map(
2804
+ (chunk) =>
2805
+ `${RuntimeGlobals.ensureChunk}(${JSON.stringify(chunk.id)}${priorityArg})`
2806
+ );
2807
+ return `Promise.all(${comment.trim()}[${items.join(", ")}])`;
2769
2808
  }
2770
2809
 
2771
2810
  /**
@@ -2801,6 +2840,17 @@ class RuntimeTemplate {
2801
2840
  const late = this._settlesLate(chunk);
2802
2841
  if (late !== this._settlesLate(target)) return !late;
2803
2842
  }
2843
+ return this._hashRoundBefore(chunk, target);
2844
+ }
2845
+
2846
+ /**
2847
+ * Whether the four rounds alone settle `chunk` before `target`, leaving out what the
2848
+ * full-hash marks move: the order a runtime module generated in its chunk's round sees.
2849
+ * @param {Chunk} chunk the chunk whose hash would be read
2850
+ * @param {Chunk} target the chunk reading it
2851
+ * @returns {boolean} true when the hash exists by the time the target is hashed
2852
+ */
2853
+ _hashRoundBefore(chunk, target) {
2804
2854
  const chunkGraph = /** @type {ChunkGraph} */ (this.compilation.chunkGraph);
2805
2855
  const round = hashRound(chunk, chunkGraph);
2806
2856
  const targetRound = hashRound(target, chunkGraph);
@@ -2823,6 +2873,42 @@ class RuntimeTemplate {
2823
2873
  );
2824
2874
  }
2825
2875
 
2876
+ /**
2877
+ * Whether a `[chunkhash]`/`[contenthash]` name is settled before every chunk it is
2878
+ * written into, so a runtime module generated in its chunk's round can spell it.
2879
+ * @param {Chunk} chunk the chunk named
2880
+ * @param {string} template what names it
2881
+ * @param {Iterable<Chunk>} targets the chunks the name is written into
2882
+ * @returns {boolean} true when the hashes it reads exist by then
2883
+ */
2884
+ _hashedNameSettledBefore(chunk, template, targets) {
2885
+ const kinds = getTemplatedPathPlugin().getPresentKinds(template);
2886
+ // The compilation hash exists in no round a chunk is hashed in.
2887
+ if (kinds.has("fullhash") || kinds.has("hash")) return false;
2888
+ for (const target of targets) {
2889
+ if (!this._hashRoundBefore(chunk, target)) return false;
2890
+ }
2891
+ return true;
2892
+ }
2893
+
2894
+ /**
2895
+ * Whether the hashes a name reads are on the chunk now, which they are only once its
2896
+ * round of `createHash` has run — never while modules are being generated.
2897
+ * @param {Chunk} chunk the chunk named
2898
+ * @param {string} template what names it
2899
+ * @param {string} sourceType which asset of the chunk it names
2900
+ * @returns {boolean} true when the name can be resolved here
2901
+ */
2902
+ _hashedNamePresent(chunk, template, sourceType) {
2903
+ const kinds = getTemplatedPathPlugin().getPresentKinds(template);
2904
+ return (
2905
+ !kinds.has("fullhash") &&
2906
+ !kinds.has("hash") &&
2907
+ (!kinds.has("chunkhash") || chunk.hash !== undefined) &&
2908
+ (!kinds.has("contenthash") || chunk.contentHash[sourceType] !== undefined)
2909
+ );
2910
+ }
2911
+
2826
2912
  /**
2827
2913
  * Whether `createHash` settles this chunk's hash in the round that runs after the
2828
2914
  * compilation hash rather than in one of the four before it. Asked of the chunk
@@ -2928,6 +3014,11 @@ class RuntimeTemplate {
2928
3014
  /** @type {Set<string>} */
2929
3015
  const names = new Set();
2930
3016
  let found = false;
3017
+ // Its own copy, since `lastIndex` is walked below; reset by the loop that ends it.
3018
+ const regexp = new RegExp(
3019
+ ANALYZABLE_TOKEN_REGEXP.source,
3020
+ ANALYZABLE_TOKEN_REGEXP.flags
3021
+ );
2931
3022
  for (const module of modules) {
2932
3023
  if (!results.has(module, chunk.runtime)) continue;
2933
3024
  const source = results.getSource(module, chunk.runtime, JAVASCRIPT_TYPE);
@@ -2937,10 +3028,6 @@ class RuntimeTemplate {
2937
3028
  carriesFullHash = true;
2938
3029
  found = true;
2939
3030
  }
2940
- const regexp = new RegExp(
2941
- ANALYZABLE_TOKEN_REGEXP.source,
2942
- ANALYZABLE_TOKEN_REGEXP.flags
2943
- );
2944
3031
  /** @type {RegExpExecArray | null} */
2945
3032
  let match;
2946
3033
  while ((match = regexp.exec(content)) !== null) {
@@ -2948,11 +3035,7 @@ class RuntimeTemplate {
2948
3035
  if (parts === null) continue;
2949
3036
  found = true;
2950
3037
  for (const [kind, value] of parts) {
2951
- if (
2952
- kind === "publicPath" ||
2953
- kind === "template" ||
2954
- kind === "unserved"
2955
- ) {
3038
+ if (FULL_HASH_PART_KINDS.has(kind)) {
2956
3039
  carriesFullHash = true;
2957
3040
  } else if (CHUNK_SPECIFIER_PART_KINDS.has(kind)) {
2958
3041
  names.add(String(value));
@@ -3087,35 +3170,6 @@ class RuntimeTemplate {
3087
3170
  this._chunksByIdForFold = undefined;
3088
3171
  }
3089
3172
 
3090
- /**
3091
- * Whether `chunk` can reach any of `targets` by following chunk groups, which is
3092
- * what turns a baked hashed name into a hash that depends on itself.
3093
- * @param {Chunk} chunk the referenced chunk
3094
- * @param {Iterable<Chunk>} targets the chunks the reference is written into
3095
- * @returns {boolean} true when one of them is reachable from `chunk`
3096
- */
3097
- _reachesAny(chunk, targets) {
3098
- const wanted = new Set(targets);
3099
- if (wanted.size === 0) return false;
3100
- if (wanted.has(chunk)) return true;
3101
- /** @type {Set<ChunkGroup>} */
3102
- const seen = new Set();
3103
- /** @type {ChunkGroup[]} */
3104
- const queue = [...chunk.groupsIterable];
3105
- for (const group of queue) seen.add(group);
3106
- for (let i = 0; i < queue.length; i++) {
3107
- for (const child of queue[i].childrenIterable) {
3108
- if (seen.has(child)) continue;
3109
- seen.add(child);
3110
- for (const candidate of child.chunks) {
3111
- if (wanted.has(candidate)) return true;
3112
- }
3113
- queue.push(child);
3114
- }
3115
- }
3116
- return false;
3117
- }
3118
-
3119
3173
  /**
3120
3174
  * The chunks a reference emitted into `module` is written into — the assets a
3121
3175
  * stand-in of ours would land in, and so the only ones a deferred fill could go
@@ -3213,13 +3267,13 @@ class RuntimeTemplate {
3213
3267
  return template.replace(HASH_IN_FILENAME_GLOBAL, "x");
3214
3268
  }
3215
3269
  try {
3216
- return template({
3217
- chunk: createHashProbeChunk(chunk, HASH_PROBE, true),
3218
- runtime: chunk.runtime,
3219
- contentHashType: JAVASCRIPT_TYPE,
3220
- hash: HASH_PROBE,
3221
- contentHash: HASH_PROBE
3222
- }).replace(HASH_IN_FILENAME_GLOBAL, "x");
3270
+ return probeTemplateName(
3271
+ template,
3272
+ chunk,
3273
+ JAVASCRIPT_TYPE,
3274
+ HASH_PROBE,
3275
+ true
3276
+ ).replace(HASH_IN_FILENAME_GLOBAL, "x");
3223
3277
  } catch (_error) {
3224
3278
  // Nothing to report: the naming call is given strictly less than this probe, so
3225
3279
  // one that throws here fails the build where the name is actually needed.
@@ -3231,7 +3285,7 @@ class RuntimeTemplate {
3231
3285
  * The `../` path from a chunk's own asset back to the output root. Hashes are
3232
3286
  * neutralized first: a runtime module is generated once to be hashed, before any
3233
3287
  * hash exists, so resolving one there throws — and `RuntimeModule.updateHash`
3234
- * swallows that, pinning the module's hash to the message.
3288
+ * lets that throw fail the build rather than hashing it.
3235
3289
  * @param {Chunk} chunk the chunk whose asset holds the reference
3236
3290
  * @param {boolean} enforceRelative whether the answer keeps a leading `./`
3237
3291
  * @returns {string} the path back to the output root
@@ -3262,7 +3316,10 @@ class RuntimeTemplate {
3262
3316
  * @returns {Placement} where a literal in it is read from
3263
3317
  */
3264
3318
  _modulePlacement(module, chunkGraph) {
3265
- return this._placementOf(this._moduleChunks(module, chunkGraph));
3319
+ return this._placementOf(
3320
+ this._moduleChunks(module, chunkGraph),
3321
+ isHotModule(module)
3322
+ );
3266
3323
  }
3267
3324
 
3268
3325
  /**
@@ -3281,11 +3338,12 @@ class RuntimeTemplate {
3281
3338
 
3282
3339
  /**
3283
3340
  * The one answer every chunk in `chunks` agrees on, with either half `null` where
3284
- * they do not.
3341
+ * they do not — under HMR the update chunk, which re-runs the module, has a say too.
3285
3342
  * @param {Iterable<Chunk>} chunks the chunks a literal is written into
3343
+ * @param {boolean=} hot whether a hot update can re-run what is written
3286
3344
  * @returns {Placement} where a literal in them is read from
3287
3345
  */
3288
- _placementOf(chunks) {
3346
+ _placementOf(chunks, hot = false) {
3289
3347
  /** @type {Placement | undefined} */
3290
3348
  let first;
3291
3349
  /** @type {string | null} */
@@ -3294,13 +3352,17 @@ class RuntimeTemplate {
3294
3352
  let loaded = null;
3295
3353
  for (const chunk of chunks) {
3296
3354
  const own = this._chunkPlacement(chunk);
3355
+ const ownUndo =
3356
+ hot && own.undo !== null && own.undo !== this._hotUpdateUndo(chunk)
3357
+ ? null
3358
+ : own.undo;
3297
3359
  if (first === undefined) {
3298
3360
  first = own;
3299
- undo = own.undo;
3361
+ undo = ownUndo;
3300
3362
  loaded = own.loaded;
3301
3363
  continue;
3302
3364
  }
3303
- if (undo !== own.undo) undo = null;
3365
+ if (undo !== ownUndo) undo = null;
3304
3366
  if (loaded !== own.loaded) loaded = null;
3305
3367
  }
3306
3368
  if (first === undefined) return NO_PLACEMENT;
@@ -3311,6 +3373,33 @@ class RuntimeTemplate {
3311
3373
  : { undo, loaded };
3312
3374
  }
3313
3375
 
3376
+ /**
3377
+ * Static literal specifier (already quoted) for the `new URL(<here>, import.meta.url)`
3378
+ * a worker or worklet entry chunk bakes to, or `null` to keep the runtime form. The
3379
+ * gate and the build are asked together so every call site agrees on both — the
3380
+ * `new Worker(...)` emit, and the resource hint `ResourceHintPlugin` spells for it.
3381
+ * @param {string | undefined} overridePublicPath a `publicPath` set on the worker itself
3382
+ * @param {Chunk} chunk the worker's entry chunk
3383
+ * @param {Module} module the module the reference is emitted into
3384
+ * @param {ChunkGraph} chunkGraph the chunk graph
3385
+ * @returns {string | null} a quoted literal, or `null` to fall back
3386
+ */
3387
+ getAnalyzableWorkerUrl(overridePublicPath, chunk, module, chunkGraph) {
3388
+ // Without an id nothing can name the chunk in a stand-in.
3389
+ if (
3390
+ chunk.id === null ||
3391
+ !this.supportsAnalyzable("url", chunkGraph, module)
3392
+ ) {
3393
+ return null;
3394
+ }
3395
+ return this._getAnalyzableChunkSpecifier(
3396
+ overridePublicPath,
3397
+ chunk,
3398
+ module,
3399
+ chunkGraph
3400
+ );
3401
+ }
3402
+
3314
3403
  /**
3315
3404
  * Static literal specifier (already quoted) for a `new URL(<here>, import.meta.url)`
3316
3405
  * or `import(<here>)` pointing at `chunk`'s JS file, or `null` when it can't be known
@@ -3320,12 +3409,11 @@ class RuntimeTemplate {
3320
3409
  * @param {Module | undefined} consumingModule the module the reference is emitted
3321
3410
  * into, or `undefined` when `consumingChunks` names where it goes instead
3322
3411
  * @param {ChunkGraph} chunkGraph the chunk graph
3323
- * @param {RuntimeRequirements=} runtimeRequirements set when the caller wraps the result in
3324
- * `new URL(...)` and so accepts a runtime public path prefix around the literal filename
3325
3412
  * @param {Chunk[]=} consumingChunks the chunks the reference is written into, where
3326
3413
  * they are known without a module to read them from
3327
3414
  * @param {string=} sourceType which half of the chunk is named — its javascript by
3328
3415
  * default, or its stylesheet
3416
+ * @param {boolean=} hot whether a hot update can re-run what is written
3329
3417
  * @returns {string | null} a JS string literal or expression, or `null` to fall back to the runtime form
3330
3418
  */
3331
3419
  _getAnalyzableChunkSpecifier(
@@ -3333,120 +3421,113 @@ class RuntimeTemplate {
3333
3421
  chunk,
3334
3422
  consumingModule,
3335
3423
  chunkGraph,
3336
- runtimeRequirements,
3337
3424
  consumingChunks,
3338
- sourceType = JAVASCRIPT_TYPE
3425
+ sourceType = JAVASCRIPT_TYPE,
3426
+ hot = isHotModule(consumingModule)
3339
3427
  ) {
3340
3428
  const { compilation } = this;
3341
3429
  const { outputOptions } = compilation;
3342
3430
  const naming = CHUNK_ASSET_NAMING.get(sourceType);
3343
3431
  // Nothing here names this type's asset, so nothing can spell where it will be.
3344
3432
  if (naming === undefined) return null;
3345
- const template = this._resolveChunkFilenameTemplate(
3346
- naming.template(chunk, outputOptions),
3347
- chunk,
3348
- sourceType
3349
- );
3433
+ const template = this._chunkAssetTemplate(chunk, sourceType);
3350
3434
  if (template === null) return null;
3351
3435
  // A hashed name is settled long after this code is generated, so a stand-in is
3352
3436
  // emitted and filled in once the hash exists.
3353
3437
  const deferred = template === undefined || HASH_IN_FILENAME.test(template);
3354
3438
  // An id names the chunk in the stand-in, and one nothing could resolve would
3355
- // reach the bundle verbatim. The whole name is re-resolved later rather than
3356
- // carrying a stand-in of its own, so any hash spelling is fine here.
3357
- // One of the two always names where the reference goes.
3439
+ // reach the bundle verbatim. The whole name is re-resolved later, so any hash
3440
+ // spelling is fine here; one of the two always names the destination.
3358
3441
  const chunks =
3359
3442
  consumingChunks ||
3360
3443
  this._moduleChunks(/** @type {Module} */ (consumingModule), chunkGraph);
3361
- // Baking makes the consuming chunk's content depend on this one's hash. A pair that
3362
- // reaches back has no fixed point if both bake, so only the lower id's name does.
3363
- const cycles =
3444
+ // An id names the chunk in the stand-in; `blockPromise` drops an id-less chunk
3445
+ // before asking, so the guard only keeps `null` out of a name.
3446
+ if (chunk.id === null) return null;
3447
+ // A runtime module is generated in its chunk's round of `createHash`, where a
3448
+ // hashed name settled earlier exists: spelled, so a hot update carries the new one.
3449
+ const settled =
3364
3450
  deferred &&
3365
- this._reachesAny(chunk, chunks) &&
3366
- !this._namesBeforeAll(chunk, chunks);
3367
- // An id names the chunk in the stand-in. No reference reaches here without one —
3368
- // `blockPromise` drops an id-less chunk before asking, and one the whole build
3369
- // keeps has no name to be emitted under — so the guard carries no reason of its
3370
- // own; it only keeps `null` out of a name.
3371
- const canReserve =
3372
- chunk.id !== null && !cycles && this._canDeferAnalyzableName(chunks);
3373
- // A content-named consumer bakes anyway when this chunk settles first: the fold
3374
- // puts the name into its hash. Only what `_foldsWholeName` covers, though.
3451
+ template !== undefined &&
3452
+ this._hashedNamePresent(chunk, template, sourceType) &&
3453
+ this._namesBeforeAll(chunk, chunks);
3454
+ const canReserve = this._canDeferAnalyzableName(chunks);
3455
+ // A content-named consumer folds this name into its hash when it settles first;
3456
+ // what the fold cannot cover is repaired instead.
3375
3457
  const canFold = !canReserve && this._namesBeforeAll(chunk, chunks);
3376
- /**
3377
- * @returns {null} always, having recorded why no stand-in may be reserved
3378
- */
3379
- const cannotReserve = () => {
3380
- this._analyzableBailout(
3381
- consumingModule,
3382
- cycles
3383
- ? "this chunk and the one it references name each other, so neither hash could settle"
3384
- : DEFER_BAILOUT
3385
- );
3386
- return null;
3387
- };
3388
- if (deferred && !canReserve && !canFold) return cannotReserve();
3389
- const filename = deferred
3390
- ? ""
3391
- : compilation.getPath(/** @type {string} */ (template), {
3392
- chunk,
3393
- // Matches what names the asset, or a placeholder resolved here would
3394
- // not be the one on disk.
3395
- runtime: chunk.runtime,
3396
- contentHashType: sourceType
3397
- });
3458
+ const filename =
3459
+ deferred && !settled
3460
+ ? ""
3461
+ : compilation.getPath(/** @type {string} */ (template), {
3462
+ chunk,
3463
+ // Matches what names the asset, or a placeholder resolved here would
3464
+ // not be the one on disk.
3465
+ runtime: chunk.runtime,
3466
+ contentHashType: sourceType
3467
+ });
3398
3468
  /**
3399
3469
  * @param {SpecifierPart[]} prefix what goes in front of the chunk's filename
3400
- * @returns {string | null} the specifier already quoted, or `null` when the
3401
- * recipe needs a stand-in that cannot be reserved
3470
+ * @returns {string} the specifier already quoted
3402
3471
  */
3403
3472
  const specifier = (prefix) => {
3404
- if (!deferred) {
3473
+ if (!deferred || settled) {
3405
3474
  const text = literalText(prefix);
3406
3475
  if (text !== null) return toJsStringLiteral(text + filename);
3407
3476
  }
3408
- if (!canReserve && !(canFold && this._foldsWholeName(prefix, chunks))) {
3409
- return cannotReserve();
3410
- }
3477
+ const repair =
3478
+ !canReserve && !(canFold && this._foldsWholeName(prefix, chunks));
3411
3479
  return toJsStringLiteral(
3412
- this._reserveAnalyzableSpecifier([
3413
- ...prefix,
3414
- [naming.standIn, /** @type {ChunkId} */ (chunk.id)]
3415
- ])
3480
+ this._reserveAnalyzableSpecifier(
3481
+ this._markedForRepair(repair, [
3482
+ ...prefix,
3483
+ // A settled name is carried as text: the fill would spell the same,
3484
+ // but only text of its own moves the runtime module's hash.
3485
+ settled
3486
+ ? ["literal", filename]
3487
+ : [naming.standIn, /** @type {ChunkId} */ (chunk.id)]
3488
+ ])
3489
+ )
3416
3490
  );
3417
3491
  };
3418
3492
  if (overridePublicPath) {
3419
- const resolved = this._resolvePublicPathPrefix(
3420
- overridePublicPath,
3421
- consumingModule,
3422
- chunks
3423
- );
3493
+ const resolved = this._resolvePublicPathPrefix(overridePublicPath);
3424
3494
  return resolved === null ? null : specifier(resolved);
3425
3495
  }
3426
3496
  const { publicPath } = outputOptions;
3427
3497
  if (publicPath === "auto") {
3428
- const { undo } = this._placementOf(chunks);
3498
+ const { undo } = this._placementOf(chunks, hot);
3429
3499
  if (undo !== null) return specifier([["literal", undo]]);
3430
3500
  // Different depths — no one `../` path is right for every asset the reference
3431
3501
  // lands in, so the deferred pass builds each asset's own.
3432
- const perAsset = specifier([["undo", ""]]);
3433
- if (perAsset !== null) return perAsset;
3434
- // Only a stand-in that could not be reserved reaches here, and `specifier`
3435
- // has already recorded why.
3436
- if (deferred || !runtimeRequirements) return null;
3437
- // No bundler follows a concatenation, but a `new URL(...)` caller still sheds
3438
- // the `.u(id)` lookup this way; `import()` needs a static specifier.
3439
- runtimeRequirements.add(RuntimeGlobals.publicPath);
3440
- return `${RuntimeGlobals.publicPath} + ${toJsStringLiteral(filename)}`;
3502
+ return specifier([["undo", ""]]);
3441
3503
  }
3442
3504
  const prefix = this._analyzablePathPrefix(
3443
3505
  consumingModule,
3444
3506
  chunkGraph,
3445
- chunks
3507
+ chunks,
3508
+ undefined,
3509
+ hot
3446
3510
  );
3447
3511
  return prefix === null ? null : specifier(prefix);
3448
3512
  }
3449
3513
 
3514
+ /**
3515
+ * The template this chunk's asset of `sourceType` is named by: `undefined` where a
3516
+ * function reads a hash, `null` where nothing here names that type or it answers nothing.
3517
+ * @param {Chunk} chunk the chunk
3518
+ * @param {string} sourceType which asset of the chunk
3519
+ * @returns {string | undefined | null} the template
3520
+ */
3521
+ _chunkAssetTemplate(chunk, sourceType) {
3522
+ const naming = CHUNK_ASSET_NAMING.get(sourceType);
3523
+ if (naming === undefined) return null;
3524
+ return this._resolveChunkFilenameTemplate(
3525
+ naming.template(chunk, this.outputOptions),
3526
+ chunk,
3527
+ sourceType
3528
+ );
3529
+ }
3530
+
3450
3531
  /**
3451
3532
  * The parts that go in front of a name so a literal read from the chunk holding it
3452
3533
  * reaches what the runtime would. A public path needing no base is the whole answer.
@@ -3460,18 +3541,24 @@ class RuntimeTemplate {
3460
3541
  * @param {ChunkGraph} chunkGraph the chunk graph
3461
3542
  * @param {Iterable<Chunk>} chunks the chunks a stand-in would be written into
3462
3543
  * @param {string=} relativeBase an entry `baseUri` read against the output root
3544
+ * @param {boolean=} hot whether a hot update can re-run what is written
3463
3545
  * @returns {SpecifierPart[] | null} the parts, or `null` having recorded why not
3464
3546
  */
3465
- _analyzablePathPrefix(module, chunkGraph, chunks, relativeBase) {
3547
+ _analyzablePathPrefix(
3548
+ module,
3549
+ chunkGraph,
3550
+ chunks,
3551
+ relativeBase,
3552
+ hot = isHotModule(module)
3553
+ ) {
3466
3554
  const publicPath = /** @type {PublicPath} */ (
3467
3555
  this.outputOptions.publicPath
3468
3556
  );
3469
3557
  const shape = this._publicPathShape();
3470
3558
  if (shape === undefined) return null;
3471
- const resolve = () =>
3472
- this._resolvePublicPathPrefix(publicPath, module, chunks);
3559
+ const resolve = () => this._resolvePublicPathPrefix(publicPath);
3473
3560
  if (isBaseIndependent(shape)) return resolve();
3474
- const { undo, loaded } = this._placementOf(chunks);
3561
+ const { undo, loaded } = this._placementOf(chunks, hot);
3475
3562
  // What a chunk the loader fetched climbs back out of. One of no depth leaves
3476
3563
  // nothing to climb, and then both answers put the same text in front.
3477
3564
  const served = this._publicPathClimb();
@@ -3483,24 +3570,17 @@ class RuntimeTemplate {
3483
3570
  // Each knows its own answer, so the fill asks per asset — where each has one,
3484
3571
  // and where nothing else goes between the two shapes.
3485
3572
  if (relativeBase !== undefined || !this._eachChunkServedOneWay(chunks)) {
3486
- this._analyzableBailout(module, SERVED_BAILOUT);
3487
- return null;
3573
+ return this._analyzableBailout(module, SERVED_BAILOUT, null);
3488
3574
  }
3489
3575
  return [...head, ["unserved", ""]];
3490
3576
  }
3491
3577
  if (climb !== "" && relativeBase === undefined) return head;
3492
3578
  const resolved = resolve();
3493
3579
  if (resolved === null) return null;
3494
- return [
3495
- ...head,
3496
- .../** @type {SpecifierPart[]} */ (
3497
- climb === "" ? [] : [["literal", climb]]
3498
- ),
3499
- .../** @type {SpecifierPart[]} */ (
3500
- relativeBase === undefined ? [] : [["literal", relativeBase]]
3501
- ),
3502
- ...rootedParts(resolved)
3503
- ];
3580
+ if (climb !== "") head.push(["literal", climb]);
3581
+ if (relativeBase !== undefined) head.push(["literal", relativeBase]);
3582
+ head.push(...rootedParts(resolved));
3583
+ return head;
3504
3584
  }
3505
3585
 
3506
3586
  /**
@@ -3512,12 +3592,9 @@ class RuntimeTemplate {
3512
3592
  * the deferred pass whole instead. A function is called for its value rather than
3513
3593
  * read as a template, so one whose answer moves with the hash is called again there.
3514
3594
  * @param {PublicPath} publicPath the configured public path
3515
- * @param {Module=} module the module the reference is emitted into, to record why
3516
- * a name no stand-in may be reserved for kept the runtime form
3517
- * @param {Iterable<Chunk>=} chunks the chunks a stand-in would be written into
3518
3595
  * @returns {SpecifierPart[] | null} the parts, or `null` when it cannot be known
3519
3596
  */
3520
- _resolvePublicPathPrefix(publicPath, module, chunks) {
3597
+ _resolvePublicPathPrefix(publicPath) {
3521
3598
  if (typeof publicPath === "function") {
3522
3599
  // One that answers nothing to a probe cannot be placed, absolute or not.
3523
3600
  if (this._publicPathShape() === undefined) return null;
@@ -3528,28 +3605,18 @@ class RuntimeTemplate {
3528
3605
  if (resolved !== null) return [["literal", resolved]];
3529
3606
  // Called again by the fill against the compilation hash, so what it answers
3530
3607
  // there is that hash by another name.
3531
- return this._canDeferOrBakeFullHash(true, chunks, module)
3532
- ? [["publicPath", ""]]
3533
- : null;
3608
+ return [["publicPath", ""]];
3534
3609
  }
3535
3610
  if (!publicPath.includes("[")) return [["literal", publicPath]];
3536
3611
  // A re-encoded digest cannot be spelled by a stand-in, so it stays a template.
3537
3612
  if (getTemplatedPathPlugin().usesFullHashDigest(publicPath)) {
3538
- return this._canDeferOrBakeFullHash(true, chunks, module)
3539
- ? [["template", publicPath]]
3540
- : null;
3613
+ return [["template", publicPath]];
3541
3614
  }
3542
3615
  const literal = this.compilation.getPath(
3543
3616
  publicPath,
3544
3617
  this._deferredFullHashPathData()
3545
3618
  );
3546
- return this._canDeferOrBakeFullHash(
3547
- this._hasReservedFullHash(literal),
3548
- chunks,
3549
- module
3550
- )
3551
- ? [["literal", literal]]
3552
- : null;
3619
+ return [["literal", literal]];
3553
3620
  }
3554
3621
 
3555
3622
  /**
@@ -3626,11 +3693,6 @@ class RuntimeTemplate {
3626
3693
  ) {
3627
3694
  const { publicPath } = this.outputOptions;
3628
3695
  const chunks = this._moduleChunks(module, chunkGraph);
3629
- /**
3630
- * @param {SpecifierPart[]} parts the whole specifier
3631
- * @returns {string | null} it already quoted, or `null` when no stand-in may be reserved
3632
- */
3633
- const specifier = (parts) => this._specifierOf(parts, chunks, module);
3634
3696
  if (bakePublicPath && publicPath !== "auto") {
3635
3697
  const prefix = this._analyzablePathPrefix(
3636
3698
  module,
@@ -3638,30 +3700,28 @@ class RuntimeTemplate {
3638
3700
  chunks,
3639
3701
  relativeBase
3640
3702
  );
3641
- return prefix === null ? null : specifier([...prefix, ...file]);
3703
+ if (prefix === null) return null;
3704
+ return this._specifierOf([...prefix, ...file], chunks);
3642
3705
  }
3643
3706
  // No public path to place it against, so the `../` path back to the output root
3644
3707
  // is the whole prefix. Different depths — no one is right for every asset the
3645
3708
  // reference is emitted into, so the deferred pass builds each asset's own.
3646
3709
  const { undo } = this._modulePlacement(module, chunkGraph);
3647
- return specifier([
3648
- undo === null ? ["undo", ""] : ["literal", undo],
3649
- ...(relativeBase === undefined
3650
- ? []
3651
- : /** @type {SpecifierPart[]} */ ([["literal", relativeBase]])),
3652
- ...file
3653
- ]);
3710
+ /** @type {SpecifierPart[]} */
3711
+ const parts = [undo === null ? ["undo", ""] : ["literal", undo]];
3712
+ if (relativeBase !== undefined) parts.push(["literal", relativeBase]);
3713
+ parts.push(...file);
3714
+ return this._specifierOf(parts, chunks);
3654
3715
  }
3655
3716
 
3656
3717
  /**
3657
3718
  * Static `new URL(<file>, import.meta.url)` for every stylesheet a runtime can load,
3658
- * keyed by chunk id, or `null` when any of them has to keep the runtime
3719
+ * keyed by chunk id, or `null` when they have to keep the runtime
3659
3720
  * `publicPath + getChunkCssFilename(id)` form. Both the runtime module reading them
3660
3721
  * and the plugin declaring what it needs ask this, so the two always agree. The
3661
3722
  * runtime hands an absolute url string to `link.href`, and so does this — the browser
3662
3723
  * resolves the element against the document, which is not where the chunk sits, and
3663
- * the loader reads the url as text either way. Nothing is written out for a runtime
3664
- * that also carries the hot handler; see below.
3724
+ * the loader reads the url as text either way.
3665
3725
  * @param {Chunk} runtimeChunk the chunk holding the stylesheet loader
3666
3726
  * @param {ChunkGraph} chunkGraph the chunk graph
3667
3727
  * @param {ReadOnlyRuntimeRequirements} runtimeRequirements what that chunk needs
@@ -3698,7 +3758,7 @@ class RuntimeTemplate {
3698
3758
  /**
3699
3759
  * Static `new URL(<file>, import.meta.url).href` for every javascript chunk a
3700
3760
  * runtime can hint at with `<link rel="prefetch"/"modulepreload">`, keyed by chunk
3701
- * id, or `null` when any of them has to keep the runtime
3761
+ * id, or `null` when they have to keep the runtime
3702
3762
  * `publicPath + getChunkScriptFilename(id)` form. Both the runtime module reading
3703
3763
  * them and the plugin declaring what it needs ask this, so the two always agree.
3704
3764
  * @param {Chunk} runtimeChunk the chunk holding the hint handlers
@@ -3722,11 +3782,9 @@ class RuntimeTemplate {
3722
3782
  );
3723
3783
  /** @type {Set<ChunkId>} */
3724
3784
  const hinted = new Set();
3725
- for (const order of Object.keys(orders)) {
3726
- for (const from of Object.keys(orders[order])) {
3727
- for (const id of orders[order][/** @type {ChunkId} */ (from)]) {
3728
- hinted.add(id);
3729
- }
3785
+ for (const byParent of Object.values(orders)) {
3786
+ for (const ids of Object.values(byParent)) {
3787
+ for (const id of ids) hinted.add(id);
3730
3788
  }
3731
3789
  }
3732
3790
  /** @type {Chunk[]} */
@@ -3744,6 +3802,146 @@ class RuntimeTemplate {
3744
3802
  );
3745
3803
  }
3746
3804
 
3805
+ /**
3806
+ * Why no chunk import of this build can be a native `import()`, read from the
3807
+ * output configuration alone, or `null` when nothing in it rules one out.
3808
+ * @returns {string | null} the reason
3809
+ */
3810
+ _chunkImportOutputReason() {
3811
+ const { chunkFormat, importFunctionName } = this.outputOptions;
3812
+ if (chunkFormat !== "module") {
3813
+ return `output.chunkFormat is ${JSON.stringify(chunkFormat)}, so chunks are not read through a native import()`;
3814
+ }
3815
+ if (importFunctionName !== "import") {
3816
+ return `output.importFunctionName is ${JSON.stringify(importFunctionName)}, so the call site is not a native import()`;
3817
+ }
3818
+ return null;
3819
+ }
3820
+
3821
+ /**
3822
+ * Records that this runtime reads its chunks some way other than a native
3823
+ * `import()`, so no literal can name what it loads. Recorded on the entries that
3824
+ * chose it, which is where the setting behind it lives.
3825
+ * @param {Chunk} chunk the runtime chunk
3826
+ * @param {ChunkGraph} chunkGraph the chunk graph
3827
+ * @returns {void}
3828
+ */
3829
+ reportChunkImportBailout(chunk, chunkGraph) {
3830
+ if (!this.outputOptions.module) return;
3831
+ const entryOptions = chunk.getEntryOptions();
3832
+ const chunkLoading =
3833
+ entryOptions && entryOptions.chunkLoading !== undefined
3834
+ ? entryOptions.chunkLoading
3835
+ : this.outputOptions.chunkLoading;
3836
+ if (chunkLoading === "import") return;
3837
+ const subject = entryOptions && entryOptions.worker ? "worker" : "runtime";
3838
+ const reason = `this ${subject} loads its chunks with ${JSON.stringify(chunkLoading)}, not "import"`;
3839
+ for (const module of chunkGraph.getChunkEntryModulesIterable(chunk)) {
3840
+ this._analyzableBailout(module, reason, false);
3841
+ }
3842
+ }
3843
+
3844
+ /**
3845
+ * The chunks this loader can be handed an id for: the ones held by a group outside
3846
+ * the initial wave, which is what an `import()` or a context map turns into an id.
3847
+ * @param {Chunk} runtimeChunk the chunk holding the chunk loader
3848
+ * @returns {Set<Chunk>} the chunks reachable from here through a non-initial group
3849
+ */
3850
+ _loadableChunks(runtimeChunk) {
3851
+ const queue = new Set(runtimeChunk.groupsIterable);
3852
+ /** @type {Set<Chunk>} */
3853
+ const chunks = new Set();
3854
+ for (const group of queue) {
3855
+ if (!group.isInitial()) {
3856
+ for (const chunk of group.chunks) chunks.add(chunk);
3857
+ }
3858
+ for (const child of group.childrenIterable) queue.add(child);
3859
+ }
3860
+ return chunks;
3861
+ }
3862
+
3863
+ /**
3864
+ * Whether the loader still needs to build a url from a chunk id, which is the case
3865
+ * for a chunk it can be asked for that `analyzableChunkImports` leaves unnamed: one
3866
+ * carrying a runtime of its own, or one a second entrypoint has at startup.
3867
+ * @param {Chunk} runtimeChunk the chunk holding the chunk loader
3868
+ * @param {ChunkGraph} chunkGraph the chunk graph
3869
+ * @returns {boolean} true when an id the map does not hold can reach the loader
3870
+ */
3871
+ chunkImportsNeedRuntimeUrl(runtimeChunk, chunkGraph) {
3872
+ const installed = new Set(runtimeChunk.getAllInitialChunks());
3873
+ for (const chunk of this._loadableChunks(runtimeChunk)) {
3874
+ if (chunk === runtimeChunk) continue;
3875
+ if (!chunk.hasRuntime() && !installed.has(chunk)) continue;
3876
+ if (JavascriptModulesPlugin.chunkHasJs(chunk, chunkGraph)) return true;
3877
+ }
3878
+ return false;
3879
+ }
3880
+
3881
+ /**
3882
+ * A literal `import()` specifier for every javascript chunk a runtime loads on
3883
+ * demand, keyed by chunk id, or `null` when one of them cannot be named here. The
3884
+ * map is written into the runtime chunk rather than at each import site, so a
3885
+ * hashed chunk name reaches no module that imports it.
3886
+ * @param {Chunk} runtimeChunk the chunk holding the chunk loader
3887
+ * @param {ChunkGraph} chunkGraph the chunk graph
3888
+ * @param {ReadOnlyRuntimeRequirements} runtimeRequirements what that chunk needs
3889
+ * @param {Module=} consumingModule the runtime module, where one exists to report against
3890
+ * @returns {Map<ChunkId, string> | null} the specifiers, or `null` to keep the runtime form
3891
+ */
3892
+ analyzableChunkImports(
3893
+ runtimeChunk,
3894
+ chunkGraph,
3895
+ runtimeRequirements,
3896
+ consumingModule
3897
+ ) {
3898
+ // The runtime names the scope `__webpack_public_path__` belongs to, so the
3899
+ // answer holds whether or not a runtime module is there to ask about.
3900
+ if (
3901
+ !this.supportsAnalyzable(
3902
+ "import",
3903
+ chunkGraph,
3904
+ consumingModule,
3905
+ runtimeChunk.runtime
3906
+ )
3907
+ ) {
3908
+ return null;
3909
+ }
3910
+ // An initial chunk and one carrying a runtime are both reached by a static
3911
+ // import, so naming either here would only make two hashes chase each other.
3912
+ const installed = new Set(runtimeChunk.getAllInitialChunks());
3913
+ /** @type {Chunk[]} */
3914
+ const chunks = [];
3915
+ for (const chunk of runtimeChunk.getAllReferencedChunks()) {
3916
+ if (
3917
+ chunk === runtimeChunk ||
3918
+ chunk.hasRuntime() ||
3919
+ installed.has(chunk) ||
3920
+ !JavascriptModulesPlugin.chunkHasJs(chunk, chunkGraph)
3921
+ ) {
3922
+ continue;
3923
+ }
3924
+ chunks.push(chunk);
3925
+ }
3926
+ const specifiers = this._analyzableChunkSpecifiers(
3927
+ chunks,
3928
+ chunkGraph,
3929
+ consumingModule,
3930
+ [runtimeChunk],
3931
+ JAVASCRIPT_TYPE,
3932
+ runtimeRequirements
3933
+ );
3934
+ if (specifiers === null) return null;
3935
+ for (const [id, specifier] of specifiers) {
3936
+ // A bare specifier is a package name, so spell it relative the way the
3937
+ // runtime form's `.p + <file>` would have resolved it.
3938
+ if (!RESOLVABLE_SPECIFIER_REGEXP.test(specifier.slice(1))) {
3939
+ specifiers.set(id, `"./${specifier.slice(1)}`);
3940
+ }
3941
+ }
3942
+ return specifiers;
3943
+ }
3944
+
3747
3945
  /**
3748
3946
  * The urls of one asset of each of `chunks`, written out so they can be read by
3749
3947
  * chunk id, or `null` as soon as one of them cannot be named here.
@@ -3763,16 +3961,51 @@ class RuntimeTemplate {
3763
3961
  sourceType,
3764
3962
  runtimeRequirements
3765
3963
  ) {
3766
- // A map written now cannot answer for whatever id an update names, so it yields
3767
- // to HMR — analyzable output is what ships.
3768
- if (
3769
- runtimeRequirements.has(RuntimeGlobals.hmrDownloadUpdateHandlers) ||
3770
- !this.supportsAnalyzable("url-runtime", chunkGraph, consumingModule)
3771
- ) {
3964
+ if (!this.supportsAnalyzable("url-runtime", chunkGraph, consumingModule)) {
3772
3965
  return null;
3773
3966
  }
3967
+ const urls = this._analyzableChunkSpecifiers(
3968
+ chunks,
3969
+ chunkGraph,
3970
+ consumingModule,
3971
+ consumingChunks,
3972
+ sourceType,
3973
+ runtimeRequirements
3974
+ );
3975
+ if (urls === null) return null;
3976
+ for (const [id, specifier] of urls) {
3977
+ urls.set(id, `${this.importMetaUrl(specifier)}.href`);
3978
+ }
3979
+ return urls;
3980
+ }
3981
+
3982
+ /**
3983
+ * The quoted specifier naming one asset of each of `chunks`, keyed by chunk id, or
3984
+ * `null` as soon as one of them cannot be named here. What the name is read against
3985
+ * is the caller's: a url resolves it, an `import()` hands it to the loader.
3986
+ * @param {Iterable<Chunk>} chunks the chunks whose asset is wanted
3987
+ * @param {ChunkGraph} chunkGraph the chunk graph
3988
+ * @param {Module | undefined} consumingModule the runtime module, where one exists to report against
3989
+ * @param {Chunk[]} consumingChunks the chunks the specifiers are written into
3990
+ * @param {string} sourceType which asset of each chunk to name
3991
+ * @param {ReadOnlyRuntimeRequirements} runtimeRequirements what the runtime chunk needs
3992
+ * @returns {Map<ChunkId, string> | null} the specifiers, or `null` to keep the runtime form
3993
+ */
3994
+ _analyzableChunkSpecifiers(
3995
+ chunks,
3996
+ chunkGraph,
3997
+ consumingModule,
3998
+ consumingChunks,
3999
+ sourceType,
4000
+ runtimeRequirements
4001
+ ) {
4002
+ // A hot update re-ships the runtime module only when its own code changes, so
4003
+ // each name has to be text nothing but an id joining or leaving the map can move.
4004
+ const hot = runtimeRequirements.has(
4005
+ RuntimeGlobals.hmrDownloadUpdateHandlers
4006
+ );
3774
4007
  /** @type {Map<ChunkId, string>} */
3775
- const urls = new Map();
4008
+ const specifiers = new Map();
3776
4009
  for (const chunk of chunks) {
3777
4010
  if (chunk.id === null) continue;
3778
4011
  const specifier = this._getAnalyzableChunkSpecifier(
@@ -3780,14 +4013,74 @@ class RuntimeTemplate {
3780
4013
  chunk,
3781
4014
  consumingModule,
3782
4015
  chunkGraph,
3783
- undefined,
3784
4016
  consumingChunks,
3785
- sourceType
4017
+ sourceType,
4018
+ hot
3786
4019
  );
3787
4020
  if (specifier === null) return null;
3788
- urls.set(chunk.id, `${this.importMetaUrl(specifier)}.href`);
4021
+ if (
4022
+ hot &&
4023
+ !this._settledUnderHot(specifier, chunk, sourceType, consumingChunks)
4024
+ ) {
4025
+ return this._analyzableBailout(consumingModule, HOT_NAME_BAILOUT, null);
4026
+ }
4027
+ specifiers.set(chunk.id, specifier);
4028
+ }
4029
+ return specifiers.size > 0 ? specifiers : null;
4030
+ }
4031
+
4032
+ /**
4033
+ * Whether a url a hot update re-ships moves only with the runtime module's own text:
4034
+ * spelled here, or a stand-in of each asset's depth and a hash-free or settled name.
4035
+ * @param {string} specifier the specifier already quoted
4036
+ * @param {Chunk} chunk the chunk it names
4037
+ * @param {string} sourceType which asset of the chunk it names
4038
+ * @param {Chunk[]} consumingChunks the chunks the map is written into
4039
+ * @returns {boolean} true when no update moves what the fill spells
4040
+ */
4041
+ _settledUnderHot(specifier, chunk, sourceType, consumingChunks) {
4042
+ const match = ANALYZABLE_TOKEN_PAYLOAD.exec(specifier);
4043
+ if (match === null) return true;
4044
+ const parts = RuntimeTemplate._readAnalyzableSpecifier(match[1]);
4045
+ if (parts === null) return false;
4046
+ for (const [kind, value] of parts) {
4047
+ if (kind === "undo") continue;
4048
+ if (kind === "literal" && !this._hasReservedFullHash(String(value))) {
4049
+ continue;
4050
+ }
4051
+ if (!CHUNK_SPECIFIER_PART_KINDS.has(kind)) return false;
4052
+ }
4053
+ const template = this._chunkAssetTemplate(chunk, sourceType);
4054
+ return (
4055
+ typeof template === "string" &&
4056
+ (!HASH_IN_FILENAME.test(template) ||
4057
+ this._hashedNameSettledBefore(chunk, template, consumingChunks))
4058
+ );
4059
+ }
4060
+
4061
+ /**
4062
+ * The `../` path from a chunk's hot update file, which re-runs its modules, back to
4063
+ * the output root. Hashes are neutralized; the chunk's own id may add a directory.
4064
+ * @param {Chunk} chunk the chunk being updated
4065
+ * @returns {string} the path back to the output root
4066
+ */
4067
+ _hotUpdateUndo(chunk) {
4068
+ const cached = this._hotUpdateUndoByChunk.get(chunk);
4069
+ if (cached !== undefined) return cached;
4070
+ const { outputOptions } = this;
4071
+ if (this._hotUpdateShapeText === undefined) {
4072
+ this._hotUpdateShapeText = outputOptions.hotUpdateChunkFilename.replace(
4073
+ HASH_IN_FILENAME_GLOBAL,
4074
+ "x"
4075
+ );
3789
4076
  }
3790
- return urls.size > 0 ? urls : null;
4077
+ const undo = getUndoPath(
4078
+ this.compilation.getPath(this._hotUpdateShapeText, { chunk }),
4079
+ /** @type {string} */ (outputOptions.path),
4080
+ true
4081
+ );
4082
+ this._hotUpdateUndoByChunk.set(chunk, undo);
4083
+ return undo;
3791
4084
  }
3792
4085
 
3793
4086
  /**
@@ -3852,20 +4145,20 @@ class RuntimeTemplate {
3852
4145
  try {
3853
4146
  // Everything the naming call is given except the hashes, so only a hash can
3854
4147
  // make the two answers differ.
3855
- const template = filenameTemplate({
3856
- chunk: createHashProbeChunk(chunk, HASH_PROBE, true),
3857
- runtime: chunk.runtime,
4148
+ const template = probeTemplateName(
4149
+ filenameTemplate,
4150
+ chunk,
3858
4151
  contentHashType,
3859
- hash: HASH_PROBE,
3860
- contentHash: HASH_PROBE
3861
- });
3862
- const probe = filenameTemplate({
3863
- chunk: createHashProbeChunk(chunk, HASH_PROBE_ALTERNATE, false),
3864
- runtime: chunk.runtime,
4152
+ HASH_PROBE,
4153
+ true
4154
+ );
4155
+ const probe = probeTemplateName(
4156
+ filenameTemplate,
4157
+ chunk,
3865
4158
  contentHashType,
3866
- hash: HASH_PROBE_ALTERNATE,
3867
- contentHash: HASH_PROBE_ALTERNATE
3868
- });
4159
+ HASH_PROBE_ALTERNATE,
4160
+ false
4161
+ );
3869
4162
  return template === probe ? template : undefined;
3870
4163
  } catch (_error) {
3871
4164
  // Nothing to report: the probe is given strictly more than the naming call,
@@ -3882,21 +4175,16 @@ class RuntimeTemplate {
3882
4175
  * @returns {boolean} true when every runtime reaching it fetches
3883
4176
  */
3884
4177
  _wasmModuleFetches(module, chunkGraph) {
3885
- /** @type {string[]} */
3886
- const keys = [];
4178
+ const { onlyFetching } = this._wasmGroups();
4179
+ let placed = false;
3887
4180
  for (const moduleRuntime of chunkGraph.getModuleRuntimes(module)) {
3888
- forEachRuntime(moduleRuntime, (key) => {
3889
- keys.push(/** @type {string} */ (key));
3890
- });
4181
+ for (const group of this._wasmGroupsOf(moduleRuntime)) {
4182
+ placed = true;
4183
+ if (!onlyFetching.get(group)) return false;
4184
+ }
3891
4185
  }
3892
4186
  // In no runtime at all nothing named a loader, so `output` answers.
3893
- if (keys.length === 0) return this.outputOptions.wasmLoading === "fetch";
3894
- const only = this._wasmGroupOnlyFetches();
3895
- const groups = this._wasmRuntimeGroups();
3896
- for (const key of keys) {
3897
- if (!only.get(this._wasmGroupOf(groups, key))) return false;
3898
- }
3899
- return true;
4187
+ return placed || this.outputOptions.wasmLoading === "fetch";
3900
4188
  }
3901
4189
 
3902
4190
  /**
@@ -3998,7 +4286,7 @@ class RuntimeTemplate {
3998
4286
  * @returns {string} the stand-in to emit
3999
4287
  */
4000
4288
  _reserveAnalyzableSpecifier(parts) {
4001
- return `./@@webpackAnalyzableChunk:${Buffer.from(JSON.stringify(parts))
4289
+ return `${ANALYZABLE_TOKEN_PREFIX}${Buffer.from(JSON.stringify(parts))
4002
4290
  .toString("base64")
4003
4291
  .replace(/\+/g, "-")
4004
4292
  .replace(/\//g, "_")
@@ -4059,20 +4347,34 @@ class RuntimeTemplate {
4059
4347
  compilation.hooks.processAssets.tapPromise(
4060
4348
  {
4061
4349
  name: PASS_NAME,
4062
- // Before source maps are written and before a minifier runs: both read
4063
- // the asset, and a stand-in is a different length than what replaces it.
4064
- // Every hash it reads is already settled — `createHash` runs ahead of
4065
- // every `processAssets` stage — and `RealContentHashPlugin` still repairs
4066
- // the names afterwards.
4350
+ // Before source maps are written and before a minifier runs, since both
4351
+ // read the asset and a stand-in differs in length from what replaces it.
4352
+ // Every hash it reads is settled: `createHash` precedes every stage.
4067
4353
  stage: getCompilation().PROCESS_ASSETS_STAGE_DERIVED
4068
4354
  },
4069
4355
  async () => {
4070
4356
  const { outputOptions } = compilation;
4071
4357
  const fullHash = compilation.hash;
4358
+ // The assets a stand-in landed in whose hashed name has to be brought
4359
+ // back in line, which `RealContentHashPlugin` then does.
4360
+ /** @type {Set<string>} */
4361
+ const repairedAssets = new Set();
4072
4362
  /** @type {Map<string, Chunk> | undefined} */
4073
4363
  let chunksById;
4074
4364
  /** @type {Map<string, Chunk> | undefined} */
4075
4365
  let chunkByAsset;
4366
+ /** @type {string | undefined} */
4367
+ let publicPathText;
4368
+ // One value for the whole pass, however many stand-ins read it.
4369
+ const resolvedPublicPath = () => {
4370
+ if (publicPathText === undefined) {
4371
+ publicPathText = compilation.getPath(
4372
+ outputOptions.publicPath || "",
4373
+ {}
4374
+ );
4375
+ }
4376
+ return publicPathText;
4377
+ };
4076
4378
  /**
4077
4379
  * @param {string} name an emitted asset's name
4078
4380
  * @returns {Chunk | undefined} the chunk it was emitted for
@@ -4111,7 +4413,9 @@ class RuntimeTemplate {
4111
4413
  let base;
4112
4414
  let specifier = "";
4113
4415
  for (const [kind, value] of parts) {
4114
- if (kind === "base") {
4416
+ if (kind === "repair") {
4417
+ repairedAssets.add(assetName);
4418
+ } else if (kind === "base") {
4115
4419
  base = /** @type {string} */ (value);
4116
4420
  } else if (kind === "literal") {
4117
4421
  specifier += value;
@@ -4127,10 +4431,7 @@ class RuntimeTemplate {
4127
4431
  {}
4128
4432
  );
4129
4433
  } else if (kind === "publicPath") {
4130
- specifier += compilation.getPath(
4131
- outputOptions.publicPath || "",
4132
- {}
4133
- );
4434
+ specifier += resolvedPublicPath();
4134
4435
  } else if (kind === "unserved") {
4135
4436
  const holder = chunkOf(assetName);
4136
4437
  if (holder === undefined) return null;
@@ -4141,10 +4442,7 @@ class RuntimeTemplate {
4141
4442
  compilation.runtimeTemplate._chunkPlacement(holder).loaded ===
4142
4443
  false
4143
4444
  ) {
4144
- const path = compilation.getPath(
4145
- outputOptions.publicPath || "",
4146
- {}
4147
- );
4445
+ const path = resolvedPublicPath();
4148
4446
  // The `../` already reached the root this is read from.
4149
4447
  specifier += path.startsWith("./") ? path.slice(2) : path;
4150
4448
  }
@@ -4188,7 +4486,7 @@ class RuntimeTemplate {
4188
4486
  // the chunk loader does. A literal part may still carry a stand-in of its own,
4189
4487
  // so finish those here rather than scanning again.
4190
4488
  return fillFullHash(
4191
- /^(?:\.{0,2}\/|[a-zA-Z][\w+.-]*:)/.test(specifier)
4489
+ RESOLVABLE_SPECIFIER_REGEXP.test(specifier)
4192
4490
  ? specifier
4193
4491
  : `./${specifier}`
4194
4492
  );
@@ -4241,6 +4539,8 @@ class RuntimeTemplate {
4241
4539
 
4242
4540
  tasks.push({ name, source, replacements });
4243
4541
  }
4542
+ compilation.runtimeTemplate._analyzableRepairedAssets =
4543
+ repairedAssets;
4244
4544
  if (tasks.length === 0) return;
4245
4545
 
4246
4546
  await Promise.all(