webpack 5.110.2 → 5.111.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/README.md +5 -2
  2. package/lib/APIPlugin.js +23 -7
  3. package/lib/AutomaticPrefetchPlugin.js +1 -1
  4. package/lib/Cache.js +8 -3
  5. package/lib/CacheFacade.js +1 -1
  6. package/lib/ChunkGraph.js +20 -18
  7. package/lib/CircularModulesPlugin.js +157 -55
  8. package/lib/CleanPlugin.js +2 -2
  9. package/lib/Compilation.js +321 -303
  10. package/lib/Compiler.js +209 -46
  11. package/lib/ConcatenationScope.js +25 -35
  12. package/lib/ContextModule.js +17 -81
  13. package/lib/ContextModuleFactory.js +1 -1
  14. package/lib/CopyPlugin.js +1054 -0
  15. package/lib/DefinePlugin.js +24 -8
  16. package/lib/DotenvPlugin.js +41 -13
  17. package/lib/ExternalModule.js +41 -14
  18. package/lib/FileSystemInfo.js +17 -14
  19. package/lib/FlagDependencyExportsPlugin.js +45 -21
  20. package/lib/FlagDependencyUsagePlugin.js +8 -2
  21. package/lib/HotModuleReplacementPlugin.js +53 -7
  22. package/lib/JavascriptMetaInfoPlugin.js +5 -3
  23. package/lib/MainTemplate.js +5 -1
  24. package/lib/Module.js +16 -7
  25. package/lib/MultiCompiler.js +1 -1
  26. package/lib/MultiWatching.js +1 -1
  27. package/lib/NormalModule.js +58 -32
  28. package/lib/NormalModuleFactory.js +1 -1
  29. package/lib/RuntimeGlobals.js +21 -7
  30. package/lib/RuntimeModule.js +28 -12
  31. package/lib/RuntimePlugin.js +33 -3
  32. package/lib/RuntimeTemplate.js +794 -477
  33. package/lib/SourceMapDevToolPlugin.js +1 -1
  34. package/lib/Watching.js +9 -0
  35. package/lib/WebpackOptionsApply.js +84 -91
  36. package/lib/asset/AssetGenerator.js +25 -8
  37. package/lib/asset/AssetModule.js +3 -0
  38. package/lib/asset/WebManifestParser.js +7 -5
  39. package/lib/async-modules/AsyncModuleHelpers.js +10 -4
  40. package/lib/cache/AddBuildDependenciesPlugin.js +33 -4
  41. package/lib/cache/IdleFileCachePlugin.js +2 -2
  42. package/lib/cache/PackFileCacheStrategy.js +14 -1
  43. package/lib/config/browserslistTargetHandler.js +19 -0
  44. package/lib/config/defaults.js +110 -64
  45. package/lib/config/normalization.js +100 -20
  46. package/lib/config/target.js +10 -0
  47. package/lib/container/ContainerReferencePlugin.js +6 -0
  48. package/lib/container/RemoteRuntimeModule.js +9 -0
  49. package/lib/container/declaredRemotes.js +39 -0
  50. package/lib/css/CssGenerator.js +8 -0
  51. package/lib/css/CssInjectStyleRuntimeModule.js +9 -0
  52. package/lib/css/CssLoadingRuntimeModule.js +94 -36
  53. package/lib/css/CssModule.js +1 -1
  54. package/lib/css/CssModulesPlugin.js +7 -6
  55. package/lib/css/CssParser.js +144 -11
  56. package/lib/css/CssServerStylesRuntimeModule.js +9 -0
  57. package/lib/css/cssMinify.js +11 -3
  58. package/lib/css/data.js +2814 -678
  59. package/lib/css/syntax.js +4082 -248
  60. package/lib/debug/ProfilingPlugin.js +4 -0
  61. package/lib/dependencies/AMDDefineDependencyParserPlugin.js +6 -3
  62. package/lib/dependencies/AMDRuntimeModules.js +18 -0
  63. package/lib/dependencies/CommonJsExportRequireDependency.js +3 -1
  64. package/lib/dependencies/CommonJsExportsParserPlugin.js +12 -13
  65. package/lib/dependencies/CommonJsImportsParserPlugin.js +1 -0
  66. package/lib/dependencies/CommonJsPlugin.js +19 -3
  67. package/lib/dependencies/CommonJsRequireDependency.js +32 -9
  68. package/lib/dependencies/ContextDependencyTemplateAsRequireCall.js +20 -7
  69. package/lib/dependencies/CssIcssExportDependency.js +7 -1
  70. package/lib/dependencies/HarmonyCompatibilityDependency.js +58 -4
  71. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +8 -3
  72. package/lib/dependencies/HarmonyExportExpressionDependency.js +44 -29
  73. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +171 -32
  74. package/lib/dependencies/HarmonyExportInitFragment.js +99 -19
  75. package/lib/dependencies/HarmonyExportSpecifierDependency.js +18 -30
  76. package/lib/dependencies/HarmonyImportBareSideEffectDependency.js +59 -0
  77. package/lib/dependencies/HarmonyImportDependency.js +99 -41
  78. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +20 -3
  79. package/lib/dependencies/HarmonyImportSpecifierDependency.js +24 -1
  80. package/lib/dependencies/HarmonyModulesPlugin.js +20 -0
  81. package/lib/dependencies/HtmlEntryDependency.js +62 -2
  82. package/lib/dependencies/ImportContextDependency.js +43 -3
  83. package/lib/dependencies/ImportDependency.js +8 -29
  84. package/lib/dependencies/ImportMetaMainDependency.js +104 -0
  85. package/lib/dependencies/ImportMetaPlugin.js +59 -15
  86. package/lib/dependencies/ImportParserPlugin.js +24 -14
  87. package/lib/dependencies/RequireHeaderDependency.js +20 -21
  88. package/lib/dependencies/SystemRuntimeModule.js +9 -0
  89. package/lib/dependencies/WorkerDependency.js +1 -2
  90. package/lib/dependencies/WorkletDependency.js +1 -2
  91. package/lib/dependencies/importOptionsCheck.js +47 -0
  92. package/lib/dll/DelegatedModule.js +18 -1
  93. package/lib/dll/DllReferencePlugin.js +11 -8
  94. package/lib/dll/LibManifestPlugin.js +1 -1
  95. package/lib/errors/AnalyzableBailoutsWarning.js +41 -0
  96. package/lib/errors/DotenvFileError.js +29 -0
  97. package/lib/errors/DuplicateModulesWarning.js +12 -7
  98. package/lib/errors/EmptyCopyPatternWarning.js +26 -0
  99. package/lib/errors/ExternalStarReexportError.js +44 -0
  100. package/lib/errors/ImportedBindingAssignmentError.js +30 -0
  101. package/lib/errors/MissingSourceMapsWarning.js +36 -0
  102. package/lib/errors/ModuleParseError.js +2 -1
  103. package/lib/errors/UnusedAssetsWarning.js +36 -0
  104. package/lib/errors/UnusedFederationWarning.js +35 -0
  105. package/lib/errors/UnusedModulesWarning.js +38 -0
  106. package/lib/esm/ExportWebpackRequireRuntimeModule.js +9 -0
  107. package/lib/esm/ModuleChunkFormatPlugin.js +28 -3
  108. package/lib/esm/ModuleChunkLoadingPlugin.js +30 -11
  109. package/lib/esm/ModuleChunkLoadingRuntimeModule.js +92 -60
  110. package/lib/hmr/HotModuleReplacement.runtime.js +8 -2
  111. package/lib/hmr/HotModuleReplacementRuntimeModule.js +9 -0
  112. package/lib/html/HtmlGenerator.js +11 -5
  113. package/lib/html/HtmlModulesPlugin.js +360 -84
  114. package/lib/html/HtmlParser.js +13 -6
  115. package/lib/html/builtinEmbeddedRenderer.js +107 -0
  116. package/lib/html/data.js +121 -7
  117. package/lib/html/htmlMinify.js +84 -113
  118. package/lib/html/syntax.js +1067 -569
  119. package/lib/ids/HashedModuleIdsPlugin.js +2 -1
  120. package/lib/index.js +3 -0
  121. package/lib/javascript/JavascriptModule.js +1 -0
  122. package/lib/javascript/JavascriptModulesPlugin.js +52 -22
  123. package/lib/javascript/JavascriptParser.js +1255 -728
  124. package/lib/javascript/StartupHelpers.js +27 -3
  125. package/lib/javascript/data.js +172 -0
  126. package/lib/javascript/grammar.js +5546 -0
  127. package/lib/javascript/parser.js +1494 -0
  128. package/lib/javascript/regexp.js +1762 -0
  129. package/lib/javascript/regexpData.js +51 -0
  130. package/lib/javascript/syntax.js +8696 -5225
  131. package/lib/library/ModuleLibraryPlugin.js +116 -5
  132. package/lib/node/ReadFileChunkLoadingRuntimeModule.js +22 -0
  133. package/lib/node/RequireChunkLoadingRuntimeModule.js +22 -0
  134. package/lib/optimize/ConcatenatedModule.js +133 -25
  135. package/lib/optimize/ConstExportsPlugin.js +50 -39
  136. package/lib/optimize/InlineExports.js +20 -123
  137. package/lib/optimize/MangleExportsPlugin.js +10 -0
  138. package/lib/optimize/ModuleConcatenationPlugin.js +24 -1
  139. package/lib/optimize/RealContentHashPlugin.js +68 -9
  140. package/lib/optimize/SideEffectsFlagPlugin.js +17 -9
  141. package/lib/performance/AnalyzableBailoutsPlugin.js +115 -0
  142. package/lib/performance/CacheEffectivenessPlugin.js +4 -3
  143. package/lib/performance/DuplicateModulesPlugin.js +57 -13
  144. package/lib/performance/EvalUsagePlugin.js +5 -2
  145. package/lib/performance/MissingSourceMapsPlugin.js +115 -0
  146. package/lib/performance/PureAnnotationsPlugin.js +6 -3
  147. package/lib/performance/TopLevelThisPlugin.js +5 -4
  148. package/lib/performance/UnusedAssetsPlugin.js +109 -0
  149. package/lib/performance/UnusedFederationPlugin.js +147 -0
  150. package/lib/performance/UnusedModulesPlugin.js +141 -0
  151. package/lib/performance/UnusedReexportsPlugin.js +1 -55
  152. package/lib/performance/isOnlyReexported.js +65 -0
  153. package/lib/prefetch/ChunkPrefetchFunctionRuntimeModule.js +25 -5
  154. package/lib/prefetch/ChunkPrefetchPreloadPlugin.js +2 -0
  155. package/lib/prefetch/ChunkPrefetchStartupRuntimeModule.js +9 -0
  156. package/lib/prefetch/ChunkPrefetchTriggerRuntimeModule.js +9 -0
  157. package/lib/prefetch/ChunkPreloadTriggerRuntimeModule.js +9 -0
  158. package/lib/prefetch/ResourceHintPlugin.js +7 -3
  159. package/lib/prefetch/ResourceHintRuntimeModule.js +9 -0
  160. package/lib/prefetch/StartupAssetHintRuntimeModule.js +9 -0
  161. package/lib/rules/RuleSetCompiler.js +211 -4
  162. package/lib/runtime/AsyncModuleRuntimeModule.js +105 -67
  163. package/lib/runtime/AutoPublicPathRuntimeModule.js +9 -0
  164. package/lib/runtime/BaseUriRuntimeModule.js +9 -0
  165. package/lib/runtime/ChunkNameRuntimeModule.js +9 -0
  166. package/lib/runtime/EnsureChunkRuntimeModule.js +35 -16
  167. package/lib/runtime/GetChunkFilenameRuntimeModule.js +9 -0
  168. package/lib/runtime/GetFullHashRuntimeModule.js +9 -0
  169. package/lib/runtime/GetMainFilenameRuntimeModule.js +9 -0
  170. package/lib/runtime/GetWorkletBootstrapRuntimeModule.js +9 -0
  171. package/lib/runtime/GlobalRuntimeModule.js +9 -0
  172. package/lib/runtime/HasOwnPropertyRuntimeModule.js +9 -0
  173. package/lib/runtime/HelperRuntimeModule.js +10 -0
  174. package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +338 -217
  175. package/lib/runtime/NonceRuntimeModule.js +9 -0
  176. package/lib/runtime/OnChunksLoadedRuntimeModule.js +67 -20
  177. package/lib/runtime/PublicPathRuntimeModule.js +9 -0
  178. package/lib/runtime/RuntimeIdRuntimeModule.js +9 -0
  179. package/lib/runtime/SpecNamespaceObjectRuntimeModule.js +90 -0
  180. package/lib/runtime/StartupChunkDependenciesRuntimeModule.js +9 -0
  181. package/lib/runtime/StartupEntrypointRuntimeModule.js +9 -0
  182. package/lib/runtime/SystemContextRuntimeModule.js +9 -0
  183. package/lib/runtime/ToBinaryRuntimeModule.js +9 -0
  184. package/lib/schemes/HttpUriPlugin.js +22 -5
  185. package/lib/serialization/FileMiddleware.js +15 -2
  186. package/lib/serialization/ObjectMiddleware.js +138 -106
  187. package/lib/sharing/ConsumeSharedPlugin.js +12 -0
  188. package/lib/sharing/ConsumeSharedRuntimeModule.js +11 -0
  189. package/lib/sharing/ShareRuntimeModule.js +9 -0
  190. package/lib/sharing/declaredShared.js +47 -0
  191. package/lib/stats/DefaultStatsFactoryPlugin.js +3 -1
  192. package/lib/stats/DefaultStatsPrinterPlugin.js +3 -0
  193. package/lib/util/ArrayQueue.js +3 -3
  194. package/lib/util/SourceProcessor.js +76 -69
  195. package/lib/util/URLAbsoluteSpecifier.js +3 -1
  196. package/lib/util/async.js +292 -0
  197. package/lib/util/buildDiagnostics.js +69 -0
  198. package/lib/util/conventions.js +5 -1
  199. package/lib/util/fs.js +27 -1
  200. package/lib/util/handlerKeys.js +40 -0
  201. package/lib/util/identifier.js +20 -0
  202. package/lib/util/internalSerializables.js +6 -0
  203. package/lib/util/registerExternalSerializer.js +6 -45
  204. package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +9 -0
  205. package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +9 -0
  206. package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +9 -0
  207. package/lib/web/JsonpChunkLoadingRuntimeModule.js +36 -0
  208. package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +19 -0
  209. package/package.json +22 -16
  210. package/schemas/WebpackOptions.check.js +1 -1
  211. package/schemas/WebpackOptions.json +388 -76
  212. package/schemas/plugins/LoaderOptionsPlugin.check.js +1 -1
  213. package/schemas/plugins/ManifestPlugin.check.js +1 -1
  214. package/schemas/plugins/SourceMapDevToolPlugin.check.js +1 -1
  215. package/schemas/plugins/asset/AssetGeneratorOptions.check.js +1 -1
  216. package/schemas/plugins/asset/AssetResourceGeneratorOptions.check.js +1 -1
  217. package/schemas/plugins/container/ContainerPlugin.check.js +1 -1
  218. package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
  219. package/schemas/plugins/debug/ProfilingPlugin.check.js +1 -1
  220. package/schemas/plugins/dll/DllReferencePlugin.check.js +1 -1
  221. package/schemas/plugins/ids/HashedModuleIdsPlugin.check.js +1 -1
  222. package/schemas/plugins/schemes/HttpUriPlugin.check.js +1 -1
  223. package/types.d.ts +1440 -331
  224. package/lib/dependencies/ExportBindingInitFragment.js +0 -191
  225. package/lib/errors/EntrypointOverlapWarning.js +0 -45
  226. package/lib/performance/EntrypointOverlapPlugin.js +0 -122
@@ -5,6 +5,7 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ const CircularModulesPlugin = require("../CircularModulesPlugin");
8
9
  const { UsageState } = require("../ExportsInfo");
9
10
  const {
10
11
  JAVASCRIPT_MODULE_TYPE_AUTO,
@@ -15,10 +16,11 @@ const { VariableInfoFlags } = require("../javascript/JavascriptParser");
15
16
  const {
16
17
  InlinedUsedName,
17
18
  enableInlineExports,
18
- findModulesInCycles,
19
19
  toInlinedValue
20
20
  } = require("./InlineExports");
21
21
 
22
+ const { CircularKind } = CircularModulesPlugin;
23
+
22
24
  /** @import { InlinedValue } from "./InlineExports" */
23
25
  /** @import Compiler from "../Compiler" */
24
26
  /** @import ExportsInfo from "../ExportsInfo" */
@@ -162,54 +164,63 @@ class ConstExportsPlugin {
162
164
  enableInlineExports(compilation.moduleGraph);
163
165
 
164
166
  const moduleGraph = compilation.moduleGraph;
165
- compilation.hooks.optimizeDependencies.tap(PLUGIN_NAME, (modules) => {
166
- // A cycle peer may read an export during its temporal dead zone,
167
- // where the binding must throw and a literal would answer.
168
- const inCycle = findModulesInCycles(moduleGraph, modules);
169
- /** @type {Set<ExportsInfo>} */
170
- const visited = new Set();
171
- /** @type {ExportsInfo[]} */
172
- let queue = [];
173
- for (const module of modules) {
174
- if (inCycle.has(module)) continue;
175
- queue.push(moduleGraph.getExportsInfo(module));
176
- }
177
- while (queue.length > 0) {
178
- const items = queue;
179
- queue = [];
180
- for (const exportsInfo of items) {
181
- if (visited.has(exportsInfo)) continue;
182
- visited.add(exportsInfo);
183
- // Other-export usage means we can't safely inline (some non-statically-known consumer)
167
+ // After CircularModulesPlugin (STAGE_DETECT): needs Binding kind flags.
168
+ compilation.hooks.optimizeDependencies.tap(
169
+ {
170
+ name: PLUGIN_NAME,
171
+ stage: CircularModulesPlugin.STAGE_DETECT + 1
172
+ },
173
+ (modules) => {
174
+ /** @type {Set<ExportsInfo>} */
175
+ const visited = new Set();
176
+ /** @type {ExportsInfo[]} */
177
+ let queue = [];
178
+ for (const module of modules) {
179
+ // Binding-observable cycle peers must keep TDZ live bindings.
184
180
  if (
185
- exportsInfo.otherExportsInfo.getUsed(undefined) !==
186
- UsageState.Unused
181
+ CircularModulesPlugin.isCircular(module, CircularKind.Binding)
187
182
  ) {
188
183
  continue;
189
184
  }
190
- for (const exportInfo of exportsInfo.ownedExports) {
191
- const inlined = exportInfo.canInline();
192
- const doInline =
193
- !exportInfo.hasUsedName() &&
194
- inlined !== undefined &&
195
- exportInfo.provided === true;
196
- if (doInline) {
197
- exportInfo.setUsedName(new InlinedUsedName(inlined));
198
- exportsInfo.markInlinedExports();
185
+ queue.push(moduleGraph.getExportsInfo(module));
186
+ }
187
+ while (queue.length > 0) {
188
+ const items = queue;
189
+ queue = [];
190
+ for (const exportsInfo of items) {
191
+ if (visited.has(exportsInfo)) continue;
192
+ visited.add(exportsInfo);
193
+ // Other-export usage means we can't safely inline (some non-statically-known consumer)
194
+ if (
195
+ exportsInfo.otherExportsInfo.getUsed(undefined) !==
196
+ UsageState.Unused
197
+ ) {
198
+ continue;
199
199
  }
200
- if (exportInfo.exportsInfoOwned && exportInfo.exportsInfo) {
201
- const used = exportInfo.getUsed(undefined);
202
- if (
203
- used === UsageState.OnlyPropertiesUsed ||
204
- used === UsageState.Unused
205
- ) {
206
- queue.push(exportInfo.exportsInfo);
200
+ for (const exportInfo of exportsInfo.ownedExports) {
201
+ const inlined = exportInfo.canInline();
202
+ const doInline =
203
+ !exportInfo.hasUsedName() &&
204
+ inlined !== undefined &&
205
+ exportInfo.provided === true;
206
+ if (doInline) {
207
+ exportInfo.setUsedName(new InlinedUsedName(inlined));
208
+ exportsInfo.markInlinedExports();
209
+ }
210
+ if (exportInfo.exportsInfoOwned && exportInfo.exportsInfo) {
211
+ const used = exportInfo.getUsed(undefined);
212
+ if (
213
+ used === UsageState.OnlyPropertiesUsed ||
214
+ used === UsageState.Unused
215
+ ) {
216
+ queue.push(exportInfo.exportsInfo);
217
+ }
207
218
  }
208
219
  }
209
220
  }
210
221
  }
211
222
  }
212
- });
223
+ );
213
224
  }
214
225
  );
215
226
  }
@@ -17,6 +17,7 @@ const { propertyAccess } = require("../util/property");
17
17
  /** @import ModuleGraph from "../ModuleGraph" */
18
18
  /** @import Module from "../Module" */
19
19
  /** @import { RuntimeSpec } from "../util/runtime" */
20
+ /** @import Hash from "../util/Hash" */
20
21
 
21
22
  const MAX_INLINE_BYTES = 6;
22
23
 
@@ -60,6 +61,14 @@ class InlinedValue {
60
61
  return `(${comment}${this.renderLiteral()})`;
61
62
  }
62
63
 
64
+ /**
65
+ * @param {InlinedValue} other other inlined value
66
+ * @returns {boolean} true when kind and value match
67
+ */
68
+ equals(other) {
69
+ return this.kind === other.kind && this.value === other.value;
70
+ }
71
+
63
72
  /**
64
73
  * @param {ObjectSerializerContext} context context
65
74
  */
@@ -96,12 +105,21 @@ class InlinedUsedName {
96
105
  }
97
106
 
98
107
  /**
99
- * @param {string} comment leading comment text
108
+ * @param {string=} comment leading comment text
100
109
  * @returns {string} rendered literal with property access suffix
101
110
  */
102
- render(comment) {
111
+ render(comment = "") {
103
112
  return this.value.render(comment) + propertyAccess(this.suffix);
104
113
  }
114
+
115
+ /**
116
+ * Updates the hash with the data contributed by this instance.
117
+ * @param {Hash} hash the hash used to track dependencies
118
+ * @returns {void}
119
+ */
120
+ updateHash(hash) {
121
+ hash.update(this.render());
122
+ }
105
123
  }
106
124
 
107
125
  /**
@@ -172,131 +190,10 @@ const isExportInlined = (moduleGraph, module, ids, runtime) => {
172
190
  return moduleGraph.getExportsInfo(module).hasInlinedUsedName(ids, runtime);
173
191
  };
174
192
 
175
- /**
176
- * Collects, by an iterative Tarjan pass, the modules a cycle peer may read in
177
- * their dead zone. A cycle of bare side-effect imports still inlines.
178
- * @param {ModuleGraph} moduleGraph module graph
179
- * @param {Iterable<Module>} modules all modules of the compilation
180
- * @returns {Set<Module>} modules whose exports must not be inlined
181
- */
182
- const findModulesInCycles = (moduleGraph, modules) => {
183
- const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency");
184
- const HarmonyImportSideEffectDependency = require("../dependencies/HarmonyImportSideEffectDependency");
185
-
186
- /** @type {Set<Module>} */
187
- const inCycle = new Set();
188
- /** @type {[Module, Module][]} */
189
- const bindingReads = [];
190
- /** @type {Map<Module, number>} */
191
- const componentOf = new Map();
192
- let nextComponent = 0;
193
- /** @type {Map<Module, number>} */
194
- const index = new Map();
195
- /** @type {Map<Module, number>} */
196
- const low = new Map();
197
- /** @type {Set<Module>} */
198
- const onStack = new Set();
199
- /** @type {Module[]} */
200
- const componentStack = [];
201
- let counter = 0;
202
- /**
203
- * @param {Module} module the module
204
- * @returns {Module[]} modules it imports, self included when self-importing
205
- */
206
- const targetsOf = (module) => {
207
- const connections = moduleGraph.getOutgoingConnectionsByModule(module);
208
- if (!connections) return [];
209
- /** @type {Module[]} */
210
- const targets = [];
211
- for (const [target, moduleConnections] of connections) {
212
- if (
213
- !target ||
214
- !moduleConnections.some(
215
- (c) => c.dependency instanceof HarmonyImportDependency
216
- )
217
- ) {
218
- continue;
219
- }
220
- // Anything but a bare side-effect import may observe the binding.
221
- if (
222
- moduleConnections.some(
223
- (c) =>
224
- c.dependency instanceof HarmonyImportDependency &&
225
- !(c.dependency instanceof HarmonyImportSideEffectDependency)
226
- )
227
- ) {
228
- bindingReads.push([module, target]);
229
- }
230
- targets.push(target);
231
- }
232
- return targets;
233
- };
234
- /** @type {{ node: Module, targets: Module[], i: number }[]} */
235
- const work = [];
236
- /**
237
- * @param {Module} node node to open
238
- */
239
- const open = (node) => {
240
- index.set(node, counter);
241
- low.set(node, counter);
242
- counter++;
243
- componentStack.push(node);
244
- onStack.add(node);
245
- work.push({ node, targets: targetsOf(node), i: 0 });
246
- };
247
- for (const seed of modules) {
248
- if (index.has(seed)) continue;
249
- open(seed);
250
- while (work.length > 0) {
251
- const frame = work[work.length - 1];
252
- if (frame.i < frame.targets.length) {
253
- const next = frame.targets[frame.i++];
254
- if (!index.has(next)) {
255
- open(next);
256
- } else if (onStack.has(next)) {
257
- const nodeLow = /** @type {number} */ (low.get(frame.node));
258
- const nextIndex = /** @type {number} */ (index.get(next));
259
- if (nextIndex < nodeLow) low.set(frame.node, nextIndex);
260
- }
261
- continue;
262
- }
263
- work.pop();
264
- const node = frame.node;
265
- const nodeLow = /** @type {number} */ (low.get(node));
266
- if (work.length > 0) {
267
- const parent = work[work.length - 1].node;
268
- if (nodeLow < /** @type {number} */ (low.get(parent))) {
269
- low.set(parent, nodeLow);
270
- }
271
- }
272
- if (nodeLow === index.get(node)) {
273
- const id = nextComponent++;
274
- let member;
275
- do {
276
- member = /** @type {Module} */ (componentStack.pop());
277
- onStack.delete(member);
278
- componentOf.set(member, id);
279
- } while (member !== node);
280
- }
281
- }
282
- }
283
- // Reader and target evaluate together exactly when they share a component.
284
- for (const [origin, target] of bindingReads) {
285
- if (
286
- origin === target ||
287
- componentOf.get(origin) === componentOf.get(target)
288
- ) {
289
- inCycle.add(target);
290
- }
291
- }
292
- return inCycle;
293
- };
294
-
295
193
  module.exports.InlinedUsedName = InlinedUsedName;
296
194
  module.exports.InlinedValue = InlinedValue;
297
195
  module.exports.MAX_INLINE_BYTES = MAX_INLINE_BYTES;
298
196
  module.exports.enableInlineExports = enableInlineExports;
299
- module.exports.findModulesInCycles = findModulesInCycles;
300
197
  module.exports.isExportInlined = isExportInlined;
301
198
  module.exports.isInlineEnabled = isInlineEnabled;
302
199
  module.exports.isInlineExportsEnabled = isInlineExportsEnabled;
@@ -15,6 +15,7 @@ const { assignDeterministicIds } = require("../ids/IdHelpers");
15
15
  const { compareSelect, compareStringsNumeric } = require("../util/comparators");
16
16
 
17
17
  /** @import Compiler from "../Compiler" */
18
+ /** @import { BuildInfo } from "../Module" */
18
19
  /** @import ExportsInfo, { ExportInfo } from "../ExportsInfo" */
19
20
  /** @import { UsedNames } from "../util/concatenate" */
20
21
 
@@ -184,6 +185,15 @@ class MangleExportsPlugin {
184
185
  );
185
186
  }
186
187
  for (const module of modules) {
188
+ // A spec namespace reports the names the module exports, so a
189
+ // module handing one out cannot have them renamed.
190
+ if (
191
+ /** @type {BuildInfo} */
192
+ (module.buildInfo).specNamespaceObject
193
+ ) {
194
+ continue;
195
+ }
196
+
187
197
  const isNamespace =
188
198
  module.buildMeta && module.buildMeta.exportsType === "namespace";
189
199
  const exportsInfo = moduleGraph.getExportsInfo(module);
@@ -5,7 +5,6 @@
5
5
 
6
6
  "use strict";
7
7
 
8
- const asyncLib = require("neo-async");
9
8
  const ChunkGraph = require("../ChunkGraph");
10
9
  const Dependency = require("../Dependency");
11
10
  const Module = require("../Module");
@@ -14,6 +13,7 @@ const { JAVASCRIPT_TYPE } = require("../ModuleSourceTypeConstants");
14
13
  const { STAGE_DEFAULT } = require("../OptimizationStages");
15
14
  const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency");
16
15
  const { ImportPhaseUtils } = require("../dependencies/ImportPhase");
16
+ const asyncLib = require("../util/async");
17
17
  const { compareModulesByIdentifier } = require("../util/comparators");
18
18
  const { isCommonJsConcatenationEnabled } = require("../util/concatenate");
19
19
  const {
@@ -28,6 +28,7 @@ const ConcatenatedModule = require("./ConcatenatedModule");
28
28
  /** @import Chunk from "../Chunk" */
29
29
  /** @import Compilation from "../Compilation" */
30
30
  /** @import Compiler from "../Compiler" */
31
+ /** @import { JavascriptModuleBuildInfo } from "../javascript/JavascriptModule" */
31
32
  /** @import { BuildInfo, BuildMeta } from "../Module" */
32
33
  /** @import RequestShortener from "../RequestShortener" */
33
34
  /** @import { RuntimeSpec } from "../util/runtime" */
@@ -235,6 +236,19 @@ class ModuleConcatenationPlugin {
235
236
  continue;
236
237
  }
237
238
 
239
+ // Concatenation materializes the namespace inline, which would
240
+ // silently drop the spec exotic object the module asked for.
241
+ if (
242
+ /** @type {BuildInfo} */
243
+ (module.buildInfo).specNamespaceObject
244
+ ) {
245
+ setBailoutReason(
246
+ module,
247
+ "Module hands out a spec namespace object"
248
+ );
249
+ continue;
250
+ }
251
+
238
252
  // Module must be in any chunk (we don't want to do useless work)
239
253
  if (chunkGraph.getNumberOfModuleChunks(module) === 0) {
240
254
  setBailoutReason(module, "Module is not in any chunk");
@@ -302,6 +316,15 @@ class ModuleConcatenationPlugin {
302
316
  canBeInner = false;
303
317
  }
304
318
 
319
+ // The root stands in for the whole concatenation, so an
320
+ // expression only an inner module can answer statically rules it out
321
+ if (
322
+ /** @type {JavascriptModuleBuildInfo} */
323
+ (module.buildInfo).moduleConcatenationRootBailout
324
+ ) {
325
+ canBeRoot = false;
326
+ }
327
+
305
328
  if (deferEnabled && moduleGraph.isDeferred(module)) {
306
329
  setInnerBailoutReason(module, "Module is deferred");
307
330
  canBeInner = false;
@@ -187,6 +187,7 @@ const createCompilationHooks = () => ({
187
187
  * @typedef {object} RealContentHashPluginOptions
188
188
  * @property {HashFunction} hashFunction the hash function to use
189
189
  * @property {HashDigest} hashDigest the hash digest to use
190
+ * @property {boolean=} onDemand run only where analyzable output marked a name for repair
190
191
  */
191
192
 
192
193
  const PLUGIN_NAME = "RealContentHashPlugin";
@@ -196,11 +197,13 @@ class RealContentHashPlugin {
196
197
  * Creates an instance of RealContentHashPlugin.
197
198
  * @param {RealContentHashPluginOptions} options options
198
199
  */
199
- constructor({ hashFunction, hashDigest }) {
200
+ constructor({ hashFunction, hashDigest, onDemand }) {
200
201
  /** @type {HashFunction} */
201
202
  this._hashFunction = hashFunction;
202
203
  /** @type {HashDigest} */
203
204
  this._hashDigest = hashDigest;
205
+ /** @type {boolean} */
206
+ this._onDemand = Boolean(onDemand);
204
207
  }
205
208
 
206
209
  /**
@@ -223,6 +226,14 @@ class RealContentHashPlugin {
223
226
  stage: Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_HASH
224
227
  },
225
228
  async () => {
229
+ // Set by the deferred fill where a baked name would leave a hashed name
230
+ // stale; only then is `[chunkhash]` repaired rather than left as named.
231
+ const repairing = compilation.runtimeTemplate.needsAnalyzableRepair();
232
+ if (this._onDemand && !repairing) return;
233
+ // On demand, only a name the fill left stale moves, and whatever names it.
234
+ const repairedAssets = this._onDemand
235
+ ? compilation.runtimeTemplate.analyzableRepairedAssets()
236
+ : undefined;
226
237
  const assets = compilation.getAssets();
227
238
  /** @type {AssetInfoForRealContentHash[]} */
228
239
  const assetsWithInfo = [];
@@ -238,6 +249,7 @@ class RealContentHashPlugin {
238
249
  /** @type {Hashes} */
239
250
  const hashes = new Set();
240
251
  addToList(info.contenthash, hashes);
252
+ if (repairing) addToList(info.chunkhash, hashes);
241
253
  if (info.contenthashDigest) {
242
254
  for (const hash of Object.keys(info.contenthashDigest)) {
243
255
  hashToDigest.set(hash, info.contenthashDigest[hash]);
@@ -495,7 +507,44 @@ ${referencingAssets
495
507
  };
496
508
  /** @type {Comparator<AssetInfoForRealContentHash>} */
497
509
  const comparator = compareSelect((a) => a.name, compareStrings);
510
+ /**
511
+ * Whether a group's names went stale: the fill rewrote an asset of theirs, or
512
+ * one names a hash that already moved (groups arrive dependencies first).
513
+ * @param {string[]} group the hashes assigned together
514
+ * @returns {boolean} true when they have to be recomputed
515
+ */
516
+ const staleOnDemand = (group) => {
517
+ const own = new Set(group);
518
+ for (const hash of group) {
519
+ for (const asset of /** @type {AssetInfoForRealContentHash[]} */ (
520
+ hashToAssets.get(hash)
521
+ )) {
522
+ if (
523
+ /** @type {ReadonlySet<string>} */ (repairedAssets).has(
524
+ asset.name
525
+ )
526
+ ) {
527
+ return true;
528
+ }
529
+ for (const referenced of /** @type {Hashes} */ (
530
+ asset.referencedHashes
531
+ )) {
532
+ if (
533
+ !own.has(referenced) &&
534
+ hashToNewHash.get(referenced) !== referenced
535
+ ) {
536
+ return true;
537
+ }
538
+ }
539
+ }
540
+ }
541
+ return false;
542
+ };
498
543
  for (const group of groupsInOrder) {
544
+ if (repairedAssets !== undefined && !staleOnDemand(group)) {
545
+ for (const hash of group) hashToNewHash.set(hash, hash);
546
+ continue;
547
+ }
499
548
  // A cycle is assigned as one group: members are told apart by stable
500
549
  // markers, so the group hashes to a fixed point instead of chasing one.
501
550
  if (group.length > 1) {
@@ -608,15 +657,25 @@ ${referencingAssets
608
657
  (hash) => /** @type {string} */ (hashToNewHash.get(hash))
609
658
  );
610
659
 
660
+ /**
661
+ * @param {string | string[]} hash what the asset was named with
662
+ * @returns {string | string[]} the same, repaired
663
+ */
664
+ const repaired = (hash) =>
665
+ Array.isArray(hash)
666
+ ? hash.map(
667
+ (hash) => /** @type {string} */ (hashToNewHash.get(hash))
668
+ )
669
+ : /** @type {string} */ (hashToNewHash.get(hash));
670
+ /** @type {AssetInfo} */
611
671
  const infoUpdate = {};
612
- const hash =
613
- /** @type {Exclude<AssetInfo["contenthash"], undefined>} */
614
- (asset.info.contenthash);
615
- infoUpdate.contenthash = Array.isArray(hash)
616
- ? hash.map(
617
- (hash) => /** @type {string} */ (hashToNewHash.get(hash))
618
- )
619
- : /** @type {string} */ (hashToNewHash.get(hash));
672
+ if (asset.info.contenthash) {
673
+ infoUpdate.contenthash = repaired(asset.info.contenthash);
674
+ }
675
+ // Tracked only while repairing, so only then does it move with the name.
676
+ if (repairing && asset.info.chunkhash) {
677
+ infoUpdate.chunkhash = repaired(asset.info.chunkhash);
678
+ }
620
679
 
621
680
  if (asset.newSource !== undefined) {
622
681
  compilation.updateAsset(
@@ -15,6 +15,10 @@ const HarmonyExportImportedSpecifierDependency = require("../dependencies/Harmon
15
15
  const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency");
16
16
  const HarmonyImportSpecifierDependency = require("../dependencies/HarmonyImportSpecifierDependency");
17
17
  const { ImportPhaseUtils } = require("../dependencies/ImportPhase");
18
+ const {
19
+ getBuildDiagnostics,
20
+ recordOptimizationBailout
21
+ } = require("../util/buildDiagnostics");
18
22
  const formatLocation = require("../util/formatLocation");
19
23
  const { dirname, join, readJson, relative } = require("../util/fs");
20
24
  const { getGlobToRegExpSource } = require("../util/globUtils");
@@ -35,7 +39,7 @@ const { CompilerHintNotationRegExp } = require("../util/magicComment");
35
39
  * } from "estree"
36
40
  */
37
41
  /** @import Compiler from "../Compiler" */
38
- /** @import Module, { BuildMeta } from "../Module" */
42
+ /** @import Module, { BuildMeta, BuildInfo } from "../Module" */
39
43
  /**
40
44
  * @import {
41
45
  * JavascriptModuleBuildInfo
@@ -546,14 +550,18 @@ class SideEffectsFlagPlugin {
546
550
  } else {
547
551
  const type = sideEffectsStatement.type;
548
552
  const loc = parser.getLocation(sideEffectsStatement);
549
- moduleGraph
550
- .getOptimizationBailout(parser.state.module)
551
- .push(
552
- () =>
553
- `Statement (${type}) with side effects in source code at ${formatLocation(
554
- loc
555
- )}`
556
- );
553
+ const at = formatLocation(loc);
554
+
555
+ const buildInfo = /** @type {BuildInfo} */ (
556
+ parser.state.module.buildInfo
557
+ );
558
+ recordOptimizationBailout(
559
+ buildInfo,
560
+ `Statement (${type}) with side effects in source code at ${at}`
561
+ );
562
+ // `performance.unusedModules` names the statement, so it is kept typed too.
563
+ getBuildDiagnostics(buildInfo).sideEffectStatement =
564
+ `${type} at ${at}`;
557
565
  }
558
566
  });
559
567
  };
@@ -0,0 +1,115 @@
1
+ /*
2
+ MIT License http://www.opensource.org/licenses/mit-license.php
3
+ Author Alexander Akait @alexander-akait
4
+ */
5
+
6
+ "use strict";
7
+
8
+ const AnalyzableBailoutsWarning = require("../errors/AnalyzableBailoutsWarning");
9
+
10
+ /** @import { PerformanceOptions } from "../../declarations/WebpackOptions" */
11
+ /** @import Compiler from "../Compiler" */
12
+ /** @import Module from "../Module" */
13
+ /** @import { BailoutDetails } from "../errors/AnalyzableBailoutsWarning" */
14
+
15
+ const PLUGIN_NAME = "AnalyzableBailoutsPlugin";
16
+
17
+ // Enough to point at the offenders without listing every module of a build.
18
+ const MAX_REPORTED_MODULES = 5;
19
+
20
+ class AnalyzableBailoutsPlugin {
21
+ /**
22
+ * Creates an instance of AnalyzableBailoutsPlugin.
23
+ * @param {PerformanceOptions} options the plugin options
24
+ */
25
+ constructor(options) {
26
+ /** @type {PerformanceOptions["hints"]} */
27
+ this.hints = options.hints;
28
+ }
29
+
30
+ /**
31
+ * Applies the plugin by registering its hooks on the compiler.
32
+ * @param {Compiler} compiler the compiler instance
33
+ * @returns {void}
34
+ */
35
+ apply(compiler) {
36
+ const hints = this.hints;
37
+
38
+ if (!hints) return;
39
+
40
+ compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
41
+ // `afterSeal` is past the hash, which folds every message into it — and
42
+ // past the runtime modules, which record their reasons while being hashed.
43
+ compilation.hooks.afterSeal.tap(PLUGIN_NAME, () => {
44
+ const { chunkGraph, runtimeTemplate, requestShortener } = compilation;
45
+
46
+ // Nothing is recorded outside ESM output, so there is nothing to walk.
47
+ if (!runtimeTemplate.isModule()) return;
48
+
49
+ /** @type {Map<string, Set<string>>} */
50
+ const modulesByReason = new Map();
51
+
52
+ /**
53
+ * Files the reasons recorded on a module under its printed name.
54
+ * @param {Module} module the module, a runtime module included
55
+ * @returns {void}
56
+ */
57
+ const collect = (module) => {
58
+ const reasons = runtimeTemplate.analyzableBailoutsOf(module);
59
+ if (reasons.length === 0) return;
60
+ const name = module.readableIdentifier(requestShortener);
61
+ for (const reason of reasons) {
62
+ let modules = modulesByReason.get(reason);
63
+ if (modules === undefined) {
64
+ modules = new Set();
65
+ modulesByReason.set(reason, modules);
66
+ }
67
+ modules.add(name);
68
+ }
69
+ };
70
+
71
+ for (const module of compilation.modules) collect(module);
72
+ for (const chunk of compilation.chunks) {
73
+ for (const module of chunkGraph.getChunkRuntimeModulesIterable(
74
+ chunk
75
+ )) {
76
+ collect(module);
77
+ }
78
+ }
79
+
80
+ if (modulesByReason.size === 0) return;
81
+
82
+ /** @type {BailoutDetails[]} */
83
+ const bailouts = [];
84
+ let references = 0;
85
+
86
+ for (const [reason, modules] of modulesByReason) {
87
+ const names = [...modules].sort();
88
+ references += names.length;
89
+ bailouts.push({
90
+ reason,
91
+ modules: names.slice(0, MAX_REPORTED_MODULES),
92
+ count: names.length
93
+ });
94
+ }
95
+
96
+ // The reason holding the most modules leads; ties read in one order.
97
+ bailouts.sort(
98
+ (a, b) => b.count - a.count || (a.reason < b.reason ? -1 : 1)
99
+ );
100
+
101
+ const warning = new AnalyzableBailoutsWarning(bailouts, references);
102
+
103
+ if (hints === "error") {
104
+ compilation.errors.push(warning);
105
+ } else if (hints === "stats") {
106
+ compilation.hints.push(warning);
107
+ } else {
108
+ compilation.warnings.push(warning);
109
+ }
110
+ });
111
+ });
112
+ }
113
+ }
114
+
115
+ module.exports = AnalyzableBailoutsPlugin;
@@ -65,10 +65,11 @@ class CacheEffectivenessPlugin {
65
65
 
66
66
  uncacheable++;
67
67
 
68
+ const notCacheableReasons =
69
+ buildInfo.diagnostics && buildInfo.diagnostics.notCacheableReasons;
68
70
  const reasons =
69
- buildInfo.notCacheableReasons &&
70
- buildInfo.notCacheableReasons.length > 0
71
- ? buildInfo.notCacheableReasons
71
+ notCacheableReasons && notCacheableReasons.length > 0
72
+ ? notCacheableReasons
72
73
  : [UNSPECIFIED_REASON];
73
74
 
74
75
  for (const reason of reasons) {