webpack 5.110.2 → 5.110.3

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 (42) hide show
  1. package/README.md +0 -2
  2. package/lib/ChunkGraph.js +20 -18
  3. package/lib/Compilation.js +1 -1
  4. package/lib/ConcatenationScope.js +25 -35
  5. package/lib/DotenvPlugin.js +41 -13
  6. package/lib/FlagDependencyUsagePlugin.js +8 -2
  7. package/lib/NormalModule.js +13 -20
  8. package/lib/RuntimePlugin.js +3 -0
  9. package/lib/RuntimeTemplate.js +14 -2
  10. package/lib/async-modules/AsyncModuleHelpers.js +10 -4
  11. package/lib/config/defaults.js +26 -16
  12. package/lib/config/normalization.js +18 -8
  13. package/lib/dependencies/CommonJsRequireDependency.js +18 -4
  14. package/lib/dependencies/ContextDependencyTemplateAsRequireCall.js +20 -7
  15. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +100 -19
  16. package/lib/dependencies/HarmonyImportDependency.js +94 -41
  17. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +12 -2
  18. package/lib/dependencies/HtmlEntryDependency.js +12 -2
  19. package/lib/dependencies/ImportContextDependency.js +43 -3
  20. package/lib/dependencies/ImportDependency.js +8 -29
  21. package/lib/dependencies/ImportParserPlugin.js +24 -14
  22. package/lib/dependencies/RequireHeaderDependency.js +20 -21
  23. package/lib/dependencies/importOptionsCheck.js +47 -0
  24. package/lib/errors/DotenvFileError.js +29 -0
  25. package/lib/errors/ImportedBindingAssignmentError.js +30 -0
  26. package/lib/html/HtmlModulesPlugin.js +29 -22
  27. package/lib/html/syntax.js +304 -143
  28. package/lib/javascript/JavascriptModulesPlugin.js +2 -0
  29. package/lib/optimize/ConcatenatedModule.js +38 -7
  30. package/lib/prefetch/ResourceHintPlugin.js +6 -1
  31. package/lib/runtime/AsyncModuleRuntimeModule.js +5 -1
  32. package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +11 -1
  33. package/lib/schemes/HttpUriPlugin.js +22 -5
  34. package/lib/serialization/FileMiddleware.js +15 -2
  35. package/lib/util/ArrayQueue.js +3 -3
  36. package/lib/util/URLAbsoluteSpecifier.js +3 -1
  37. package/lib/util/conventions.js +5 -1
  38. package/lib/util/internalSerializables.js +2 -0
  39. package/package.json +1 -1
  40. package/schemas/WebpackOptions.check.js +1 -1
  41. package/schemas/WebpackOptions.json +9 -10
  42. package/types.d.ts +26 -17
package/README.md CHANGED
@@ -301,8 +301,6 @@ For information about the governance of the webpack project, see [GOVERNANCE.md]
301
301
  **Aviv Keller** <<me@aviv.sh>> (he/him)
302
302
  - [evenstensberg](https://github.com/evenstensberg) -
303
303
  **Even Stensberg** <<evenstensberg@gmail.com>> (he/him)
304
- - [ovflowd](https://github.com/ovflowd) -
305
- **Claudio Wunder** <<cwunder@gnome.org>> (he/they)
306
304
  - [thelarkinn](https://github.com/thelarkinn) -
307
305
  **Sean Larkin** <<selarkin@microsoft.com>> (he/him)
308
306
 
package/lib/ChunkGraph.js CHANGED
@@ -1380,27 +1380,29 @@ class ChunkGraph {
1380
1380
 
1381
1381
  for (const chunkGroup of chunk.groupsIterable) {
1382
1382
  if (chunkGroup instanceof Entrypoint) {
1383
+ if (entrypoints.has(chunkGroup)) continue;
1384
+ entrypoints.add(chunkGroup);
1385
+ // `entrypoints` doubles as the visited set: a diamond of `dependOn`
1386
+ // edges would otherwise re-traverse the shared entrypoint per path
1383
1387
  const queue = [chunkGroup];
1384
- while (queue.length > 0) {
1385
- const current = queue.shift();
1386
- if (current) {
1387
- entrypoints.add(current);
1388
-
1389
- let hasChildrenEntrypoint = false;
1390
- for (const child of current.childrenIterable) {
1391
- if (child instanceof Entrypoint && child.dependOn(current)) {
1392
- hasChildrenEntrypoint = true;
1393
- queue.push(/** @type {Entrypoint} */ (child));
1388
+ for (let i = 0; i < queue.length; i++) {
1389
+ const current = queue[i];
1390
+ let hasChildrenEntrypoint = false;
1391
+ for (const child of current.childrenIterable) {
1392
+ if (child instanceof Entrypoint && child.dependOn(current)) {
1393
+ hasChildrenEntrypoint = true;
1394
+ if (!entrypoints.has(child)) {
1395
+ entrypoints.add(child);
1396
+ queue.push(child);
1394
1397
  }
1395
1398
  }
1396
- // entryChunkB: hasChildrenEntrypoint = true
1397
- // entryChunkA: dependOn = entryChunkB
1398
- if (hasChildrenEntrypoint) {
1399
- const entrypointChunk = current.getEntrypointChunk();
1400
- if (entrypointChunk !== chunk && !entrypointChunk.hasRuntime()) {
1401
- // add entryChunkB to set
1402
- set.add(entrypointChunk);
1403
- }
1399
+ }
1400
+ // an entrypoint others depend on has to be loaded before them,
1401
+ // unless it is the runtime chunk itself or carries its own runtime
1402
+ if (hasChildrenEntrypoint) {
1403
+ const entrypointChunk = current.getEntrypointChunk();
1404
+ if (entrypointChunk !== chunk && !entrypointChunk.hasRuntime()) {
1405
+ set.add(entrypointChunk);
1404
1406
  }
1405
1407
  }
1406
1408
  }
@@ -4083,7 +4083,7 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
4083
4083
  const text = /** @type {string} */ (source.source());
4084
4084
  // Everything the hook is handed is in the key: a tap answers per language
4085
4085
  // pair, so identical text offered as two of them is two questions.
4086
- const key = `${info.type}${info.hostType}${text}`;
4086
+ const key = `${info.type}\0${info.hostType}\0${text}`;
4087
4087
  const resolved = this._embeddedSources.get(info.module);
4088
4088
  if (resolved !== undefined) {
4089
4089
  const hit = resolved.get(key);
@@ -19,9 +19,10 @@ const {
19
19
  * ExportName as Ids
20
20
  * } from "./optimize/ConcatenatedModule"
21
21
  */
22
- /** @import { Range } from "./javascript/JavascriptParser" */
23
22
 
24
- /** @typedef {{ start: number, end: number, name: string }} ModuleReferenceMatch */
23
+ /** @typedef {{ start: number, end: number, name: string, leaked: boolean }} ModuleReferenceMatch */
24
+
25
+ const MODULE_REFERENCE_TOKEN = "__WEBPACK_MODULE_REFERENCE__";
25
26
 
26
27
  const MODULE_REFERENCE_REGEXP =
27
28
  /^__WEBPACK_MODULE_REFERENCE__(\d+)_([\da-f]+|ns)(_call)?(_directImport)?(_deferredImport)?(_mangleableNs)?(_moduleExportsAccess)?(?:_asiSafe(\d))?__$/;
@@ -33,6 +34,8 @@ const MODULE_REFERENCE_SCAN_REGEXP = new RegExp(
33
34
  "g"
34
35
  );
35
36
 
37
+ const IDENTIFIER_CHARACTER_REGEXP = /[\w$]/;
38
+
36
39
  /** @type {WeakMap<Source, ModuleReferenceMatch[]>} */
37
40
  const moduleReferencesCache = new WeakMap();
38
41
 
@@ -76,10 +79,6 @@ class ConcatenationScope {
76
79
  this.usedNames = usedNames;
77
80
  /** @type {Map<Module, ModuleInfo>} */
78
81
  this._modulesMap = modulesMap;
79
- // written and read by dependency templates within a single code
80
- // generation pass, never across passes
81
- /** @type {Range[] | undefined} */
82
- this._replacedRequireRanges = undefined;
83
82
  }
84
83
 
85
84
  /**
@@ -121,33 +120,6 @@ class ConcatenationScope {
121
120
  return this._currentModule.wrapped;
122
121
  }
123
122
 
124
- /**
125
- * Records a `require(...)` call that was replaced as a whole by a
126
- * concatenation reference, so nothing else rewrites a range inside it.
127
- * @param {Range} range source range of the replaced call
128
- */
129
- registerReplacedRequire(range) {
130
- if (this._replacedRequireRanges === undefined) {
131
- this._replacedRequireRanges = [];
132
- }
133
- this._replacedRequireRanges.push(range);
134
- }
135
-
136
- /**
137
- * Checks whether an offset sits inside an already-replaced `require(...)` call.
138
- * Containment, not exact match: `new require(...)` is replaced from `new`.
139
- * @param {number} start start offset to test
140
- * @returns {boolean} true, when the offset was replaced already
141
- */
142
- isInsideReplacedRequire(start) {
143
- const ranges = this._replacedRequireRanges;
144
- if (ranges === undefined) return false;
145
- for (const [rangeStart, rangeEnd] of ranges) {
146
- if (start >= rangeStart && start < rangeEnd) return true;
147
- }
148
- return false;
149
- }
150
-
151
123
  /**
152
124
  * Records the symbol that should be used when the current module exports a
153
125
  * named binding.
@@ -263,7 +235,22 @@ class ConcatenationScope {
263
235
  }
264
236
 
265
237
  /**
266
- * Finds all encoded module references in a generated source.
238
+ * Checks whether an identifier buries a reference token without being one,
239
+ * which nothing can resolve: it would reach the output as an undeclared global.
240
+ * @param {string} name the identifier
241
+ * @returns {boolean} true, when a reference token is buried in the identifier
242
+ */
243
+ static isLeakedModuleReference(name) {
244
+ return (
245
+ name.includes(MODULE_REFERENCE_TOKEN) &&
246
+ !MODULE_REFERENCE_REGEXP.test(name)
247
+ );
248
+ }
249
+
250
+ /**
251
+ * Finds all encoded module references in a generated source. A match that
252
+ * continues an identifier is reported as `leaked`: it is a token a wider
253
+ * replacement swallowed, so substituting it would rewrite the middle of a name.
267
254
  * @param {Source} source generated source
268
255
  * @returns {ModuleReferenceMatch[]} reference token positions (end is exclusive, excludes the appended "._")
269
256
  */
@@ -279,7 +266,10 @@ class ConcatenationScope {
279
266
  result.push({
280
267
  start: match.index,
281
268
  end: match.index + match[0].length,
282
- name: match[0]
269
+ name: match[0],
270
+ leaked:
271
+ match.index > 0 &&
272
+ IDENTIFIER_CHARACTER_REGEXP.test(code[match.index - 1])
283
273
  });
284
274
  match = MODULE_REFERENCE_SCAN_REGEXP.exec(code);
285
275
  }
@@ -6,6 +6,7 @@
6
6
  "use strict";
7
7
 
8
8
  const FileSystemInfo = require("./FileSystemInfo");
9
+ const DotenvFileError = require("./errors/DotenvFileError");
9
10
  const { join } = require("./util/fs");
10
11
 
11
12
  /** @import { DotenvPluginOptions } from "../declarations/WebpackOptions" */
@@ -155,8 +156,8 @@ function expandValue(value, processEnv, runningParsed) {
155
156
  const opMatch = expression.match(opRegex);
156
157
  const splitter = opMatch ? opMatch[0] : null;
157
158
 
158
- const r = expression.split(/** @type {string} */ (splitter));
159
- // const r = splitter ? expression.split(splitter) : [expression];
159
+ // `split(null)` splits on the string "null", cutting up names containing it
160
+ const r = splitter ? expression.split(splitter) : [expression];
160
161
 
161
162
  /** @type {string} */
162
163
  let defaultValue;
@@ -235,16 +236,21 @@ function expand(options) {
235
236
  /**
236
237
  * Format environment variables as DefinePlugin definitions
237
238
  * @param {Env} env environment variables
238
- * @returns {Record<string, string>} formatted definitions
239
+ * @returns {Record<string, string | Env>} formatted definitions
239
240
  */
240
241
  const envToDefinitions = (env) => {
241
- const definitions = /** @type {Record<string, string>} */ ({});
242
+ const definitions = /** @type {Record<string, string | Env>} */ ({});
243
+ // one nested definition rather than a `import.meta.env.<key>` per key: a
244
+ // dotted key would otherwise read back as a nested object, not as the key
245
+ const importMetaEnv = /** @type {Env} */ (Object.create(null));
242
246
 
243
247
  for (const [key, value] of Object.entries(env)) {
244
248
  const defValue = JSON.stringify(value);
249
+ // `process.env` stays flat — it must not shadow the real one at runtime
245
250
  definitions[`process.env.${key}`] = defValue;
246
- definitions[`import.meta.env.${key}`] = defValue;
251
+ importMetaEnv[key] = defValue;
247
252
  }
253
+ definitions["import.meta.env"] = importMetaEnv;
248
254
 
249
255
  return definitions;
250
256
  };
@@ -296,6 +302,8 @@ class DotenvPlugin {
296
302
 
297
303
  /** @type {undefined | InstanceType<Snapshot>} */
298
304
  let snapshot;
305
+ /** @type {undefined | DotenvFileError} */
306
+ let loadError;
299
307
 
300
308
  const cache = compiler.getCache(PLUGIN_NAME);
301
309
  const identifier = JSON.stringify(
@@ -304,16 +312,31 @@ class DotenvPlugin {
304
312
  const itemCache = cache.getItemCache(identifier, null);
305
313
 
306
314
  compiler.hooks.beforeCompile.tapPromise(PLUGIN_NAME, async () => {
307
- const { parsed, snapshot: newSnapshot } = dir
308
- ? await this._loadEnv(compiler, itemCache, dir)
309
- : { parsed: {} };
310
- const env = this._getEnv(prefixes, parsed);
311
-
312
- definePlugin.definitions = envToDefinitions(env || {});
313
- snapshot = newSnapshot;
315
+ loadError = undefined;
316
+ try {
317
+ const { parsed, snapshot: newSnapshot } = dir
318
+ ? await this._loadEnv(compiler, itemCache, dir)
319
+ : { parsed: {} };
320
+ const env = this._getEnv(prefixes, parsed);
321
+
322
+ definePlugin.definitions = envToDefinitions(env || {});
323
+ snapshot = newSnapshot;
324
+ } catch (err) {
325
+ // reported on the compilation so watch mode recovers when it is fixed
326
+ if (!(err instanceof DotenvFileError)) throw err;
327
+ loadError = err;
328
+ definePlugin.definitions = envToDefinitions({});
329
+ snapshot = undefined;
330
+ }
314
331
  });
315
332
 
316
333
  compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
334
+ if (loadError) {
335
+ compilation.errors.push(loadError);
336
+ compilation.fileDependencies.add(
337
+ /** @type {string} */ (loadError.file)
338
+ );
339
+ }
317
340
  if (snapshot) {
318
341
  compilation.fileDependencies.addAll(snapshot.getFileIterable());
319
342
  compilation.missingDependencies.addAll(snapshot.getMissingIterable());
@@ -369,7 +392,12 @@ class DotenvPlugin {
369
392
  fileDependencies.push(filePath);
370
393
  return content;
371
394
  },
372
- () => {
395
+ (err) => {
396
+ const code = /** @type {NodeJS.ErrnoException} */ (err).code;
397
+ // only an absent file is normal; anything else is a real fault
398
+ if (code !== "ENOENT" && code !== "ENOTDIR") {
399
+ throw new DotenvFileError(filePath, err);
400
+ }
373
401
  // File doesn't exist, add to missingDependencies (this is normal)
374
402
  missingDependencies.push(filePath);
375
403
  return "";
@@ -345,10 +345,16 @@ class FlagDependencyUsagePlugin {
345
345
  if (oldItem === undefined || Array.isArray(oldItem)) {
346
346
  exportsMap.set(key, item);
347
347
  } else {
348
+ // An unset flag means "allowed", so merge on `!== false`:
349
+ // `undefined && false` reads back as allowed.
348
350
  exportsMap.set(key, {
349
351
  name: item.name,
350
- canMangle: item.canMangle && oldItem.canMangle,
351
- canInline: item.canInline && oldItem.canInline
352
+ canMangle:
353
+ item.canMangle !== false &&
354
+ oldItem.canMangle !== false,
355
+ canInline:
356
+ item.canInline !== false &&
357
+ oldItem.canInline !== false
352
358
  });
353
359
  }
354
360
  }
@@ -109,7 +109,6 @@ const getModuleBuildError = memoize(() => require("./errors/ModuleBuildError"));
109
109
  * BuildMeta,
110
110
  * CodeGenerationContext,
111
111
  * CodeGenerationResult,
112
- * CodeGenerationResultData,
113
112
  * ConcatenationBailoutReasonContext,
114
113
  * KnownBuildInfo,
115
114
  * LibIdentOptions,
@@ -119,7 +118,6 @@ const getModuleBuildError = memoize(() => require("./errors/ModuleBuildError"));
119
118
  * NeedBuildCallback,
120
119
  * BuildCallback,
121
120
  * RuntimeRequirements,
122
- * Sources,
123
121
  * SourceType,
124
122
  * SourceTypes
125
123
  * } from "./Module"
@@ -728,11 +726,6 @@ class NormalModule extends Module {
728
726
  this._sideEffectsStateGraph = undefined;
729
727
  /** @type {ConnectionState | undefined} */
730
728
  this._sideEffectsStateValue = undefined;
731
- /**
732
- * @private
733
- * @type {CodeGenerationResultData}
734
- */
735
- this._codeGeneratorData = new Map();
736
729
  }
737
730
 
738
731
  /**
@@ -1892,10 +1885,19 @@ class NormalModule extends Module {
1892
1885
  runtimeRequirements.add(RuntimeGlobals.thisAsExports);
1893
1886
  }
1894
1887
 
1895
- const getData = () => this._codeGeneratorData;
1888
+ /** @type {CodeGenerationResult} */
1889
+ const resultEntry = {
1890
+ sources: new Map(),
1891
+ runtimeRequirements,
1892
+ data: undefined,
1893
+ hash: undefined
1894
+ };
1895
+
1896
+ const getData = () => {
1897
+ if (resultEntry.data === undefined) resultEntry.data = new Map();
1898
+ return resultEntry.data;
1899
+ };
1896
1900
 
1897
- /** @type {Sources} */
1898
- const sources = new Map();
1899
1901
  for (const type of sourceTypes || chunkGraph.getModuleSourceTypes(this)) {
1900
1902
  // TODO webpack@6 make generateError required
1901
1903
  const generator =
@@ -1930,17 +1932,10 @@ class NormalModule extends Module {
1930
1932
  });
1931
1933
 
1932
1934
  if (source) {
1933
- sources.set(type, new CachedSource(source));
1935
+ resultEntry.sources.set(type, new CachedSource(source));
1934
1936
  }
1935
1937
  }
1936
1938
 
1937
- /** @type {CodeGenerationResult} */
1938
- const resultEntry = {
1939
- sources,
1940
- runtimeRequirements,
1941
- data: this._codeGeneratorData,
1942
- hash: undefined
1943
- };
1944
1939
  return resultEntry;
1945
1940
  }
1946
1941
 
@@ -2116,7 +2111,6 @@ class NormalModule extends Module {
2116
2111
  write(this.error);
2117
2112
  write(this._lastSuccessfulBuildMeta);
2118
2113
  write(this._forceBuild);
2119
- write(this._codeGeneratorData);
2120
2114
  write(this.hot);
2121
2115
  super.serialize(context);
2122
2116
  }
@@ -2160,7 +2154,6 @@ class NormalModule extends Module {
2160
2154
  this.error = read();
2161
2155
  this._lastSuccessfulBuildMeta = read();
2162
2156
  this._forceBuild = read();
2163
- this._codeGeneratorData = read();
2164
2157
  this.hot = read();
2165
2158
  super.deserialize(context);
2166
2159
  }
@@ -189,6 +189,9 @@ const TREE_DEPENDENCIES = {
189
189
  RuntimeGlobals.makeNamespaceObject,
190
190
  RuntimeGlobals.require
191
191
  ],
192
+ [RuntimeGlobals.deferredModuleAsyncTransitiveDependencies]: [
193
+ RuntimeGlobals.asyncModule
194
+ ],
192
195
  [RuntimeGlobals.makeOptimizedDeferredNamespaceObject]: [
193
196
  RuntimeGlobals.require
194
197
  ],
@@ -55,6 +55,7 @@ const {
55
55
  const getCssModulesPlugin = memoize(() => require("./css/CssModulesPlugin"));
56
56
  // `Compilation` requires this module, so its stage constants are read lazily.
57
57
  const getCompilation = memoize(() => require("./Compilation"));
58
+ const getExternalModule = memoize(() => require("./ExternalModule"));
58
59
 
59
60
  /**
60
61
  * @typedef {object} ChunkAssetNaming
@@ -422,6 +423,11 @@ function getGlobalObject(definition) {
422
423
  return `Object(${trimmed})`;
423
424
  }
424
425
 
426
+ // An async external is a promise, not a module record with an evaluating state.
427
+ const isAsyncExternal = (/** @type {Module} */ module) =>
428
+ Boolean(/** @type {BuildMeta} */ (module.buildMeta).async) &&
429
+ module instanceof getExternalModule();
430
+
425
431
  class RuntimeTemplate {
426
432
  /**
427
433
  * Creates an instance of RuntimeTemplate.
@@ -1984,6 +1990,9 @@ class RuntimeTemplate {
1984
1990
  (m) => chunkGraph.getModuleId(m)
1985
1991
  ).filter((id) => id !== null);
1986
1992
  if (asyncDeps.length) {
1993
+ runtimeRequirements.add(
1994
+ RuntimeGlobals.deferredModuleAsyncTransitiveDependencies
1995
+ );
1987
1996
  if (header) {
1988
1997
  appending = `.then(${this.basicFunction(
1989
1998
  "",
@@ -2220,11 +2229,14 @@ class RuntimeTemplate {
2220
2229
  (dependency instanceof HarmonyImportDependency ||
2221
2230
  dependency instanceof ImportDependency) &&
2222
2231
  ImportPhaseUtils.isDefer(dependency.phase) &&
2223
- !(/** @type {BuildMeta} */ (module.buildMeta).async);
2232
+ !isAsyncExternal(module);
2224
2233
 
2225
2234
  if (isModuleDeferred) {
2226
2235
  /** @type {Set<Module>} */
2227
2236
  const outgoingAsyncModules = getOutgoingAsyncModules(moduleGraph, module);
2237
+ // A module deferring itself is already evaluating, so awaiting it here
2238
+ // would deadlock; forcing the namespace throws instead.
2239
+ outgoingAsyncModules.delete(originModule);
2228
2240
 
2229
2241
  importContent = `/* deferred harmony import */ ${optDeclaration}${importVar} = ${getOptimizedDeferredModule(
2230
2242
  moduleId,
@@ -2309,7 +2321,7 @@ class RuntimeTemplate {
2309
2321
  (dependency instanceof HarmonyImportDependency ||
2310
2322
  dependency instanceof ImportDependency) &&
2311
2323
  ImportPhaseUtils.isDefer(dependency.phase) &&
2312
- !(/** @type {BuildMeta} */ (module.buildMeta).async);
2324
+ !isAsyncExternal(module);
2313
2325
 
2314
2326
  if (defaultInterop) {
2315
2327
  // when the defaultInterop is used (when a ESM imports a CJS module),
@@ -32,16 +32,22 @@ const getOutgoingAsyncModules = (moduleGraph, module) => {
32
32
  const outgoingConnectionMap =
33
33
  moduleGraph.getOutgoingConnectionsByModule(module);
34
34
  if (outgoingConnectionMap) {
35
- for (const [module, connections] of outgoingConnectionMap) {
35
+ // Walk the imports in source order: the spec gathers these from
36
+ // `[[RequestedModules]]`, and their evaluation order is observable.
37
+ for (const dependency of module.dependencies) {
38
+ if (!(dependency instanceof HarmonyImportDependency)) continue;
39
+ const referenced = moduleGraph.getModule(dependency);
40
+ if (!referenced || seen.has(referenced)) continue;
41
+ const connections = outgoingConnectionMap.get(referenced);
36
42
  if (
43
+ connections &&
37
44
  connections.some(
38
45
  (c) =>
39
46
  c.dependency instanceof HarmonyImportDependency &&
40
47
  c.isTargetActive(undefined)
41
- ) &&
42
- module
48
+ )
43
49
  ) {
44
- g(module);
50
+ g(referenced);
45
51
  }
46
52
  }
47
53
  }
@@ -179,6 +179,7 @@ const {
179
179
  * portableRecords: NonNullable<Optimization["portableRecords"]>,
180
180
  * realContentHash: NonNullable<Optimization["realContentHash"]>,
181
181
  * minimize: NonNullable<Optimization["minimize"]>,
182
+ * minimizeOptions: NonNullable<Optimization["minimizeOptions"]>,
182
183
  * minimizer: NonNullable<Exclude<Optimization["minimizer"], "...">>,
183
184
  * nodeEnv: NonNullable<Optimization["nodeEnv"]>,
184
185
  * }} OptimizationNormalizedWithDefaults
@@ -1747,6 +1748,8 @@ const applyOutputDefaults = (
1747
1748
  "generator",
1748
1749
  () => tp && optimistic(/** @type {boolean | undefined} */ (tp.generator))
1749
1750
  );
1751
+ // No `topLevelAwait` here: the option is held back from the patch release,
1752
+ // so nothing derives it from `tp` yet. Reapply by reverting #21881.
1750
1753
  F(
1751
1754
  environment,
1752
1755
  "forOf",
@@ -2463,20 +2466,28 @@ const applyOptimizationDefaults = (
2463
2466
  D(optimization, "mangleWasmImports", false);
2464
2467
  D(optimization, "portableRecords", records);
2465
2468
  D(optimization, "realContentHash", production);
2466
- F(optimization, "minimize", () => (production ? {} : false));
2467
- // A plugin can assign the `true` shorthand in `apply()`, which runs after
2468
- // `getNormalizedOptimizationMinimize` and so never gets normalized. The
2469
- // normalized type does not allow it, hence the cast.
2470
- if (/** @type {unknown} */ (optimization.minimize) === true) {
2471
- optimization.minimize = {};
2469
+ // A plugin assigning the object shorthand in `apply()` runs after
2470
+ // normalization: it replaces `minimizeOptions`, keeping `minimize` boolean.
2471
+ const minimizeAssignedInApply = /** @type {unknown} */ (
2472
+ optimization.minimize
2473
+ );
2474
+ if (typeof minimizeAssignedInApply === "object") {
2475
+ optimization.minimizeOptions = /** @type {OptimizationMinimizeOptions} */ (
2476
+ minimizeAssignedInApply
2477
+ );
2478
+ optimization.minimize = true;
2472
2479
  }
2473
- const { minimize } = optimization;
2474
- if (minimize) {
2475
- // Canonicalize the object form: the JS minimizer's defaults become
2476
- // visible in the resolved config rather than hidden in the wiring.
2477
- F(minimize, "javascript", () => ({ compress: { passes: 2 } }));
2478
- F(minimize, "css", () => ({}));
2479
- F(minimize, "html", () => ({}));
2480
+ D(optimization, "minimize", production);
2481
+ F(optimization, "minimizeOptions", () => ({}));
2482
+ const minimizeOptions =
2483
+ /** @type {NonNullable<Optimization["minimizeOptions"]>} */
2484
+ (optimization.minimizeOptions);
2485
+ if (optimization.minimize) {
2486
+ // Canonicalize the per-asset-type options: the JS minimizer's defaults
2487
+ // become visible in the resolved config rather than hidden in the wiring.
2488
+ F(minimizeOptions, "javascript", () => ({ compress: { passes: 2 } }));
2489
+ F(minimizeOptions, "css", () => ({}));
2490
+ F(minimizeOptions, "html", () => ({}));
2480
2491
  }
2481
2492
  A(optimization, "minimizer", () => [
2482
2493
  {
@@ -2501,10 +2512,9 @@ const applyOptimizationDefaults = (
2501
2512
  compiler
2502
2513
  );
2503
2514
 
2504
- // Normalized + defaulted `optimization.minimize` is the per-asset-type
2505
- // object here: `false` disables a type, an object is its options.
2515
+ // Per asset type: `false` disables that type, an object is its options.
2506
2516
  const minimizeByType = /** @type {OptimizationMinimizeOptions} */ (
2507
- compiler.options.optimization.minimize
2517
+ compiler.options.optimization.minimizeOptions
2508
2518
  );
2509
2519
  const terserOptions = minimizeByType.javascript || {
2510
2520
  compress: { passes: 2 }
@@ -331,7 +331,14 @@ const getNormalizedWebpackOptions = (config) => ({
331
331
  cacheGroups: cloneObject(splitChunks.cacheGroups)
332
332
  }
333
333
  ),
334
- minimize: getNormalizedOptimizationMinimize(optimization.minimize),
334
+ // TODO webpack 6: normalize `minimize` to the per-asset-type object and
335
+ // drop `minimizeOptions` — a breaking change for what plugins read back.
336
+ minimize:
337
+ typeof optimization.minimize === "object" ? true : optimization.minimize,
338
+ minimizeOptions: getNormalizedOptimizationMinimizeOptions(
339
+ optimization.minimize,
340
+ optimization.minimizeOptions
341
+ ),
335
342
  minimizer:
336
343
  optimization.minimizer !== undefined
337
344
  ? /** @type {OptimizationNormalized["minimizer"]} */ (
@@ -612,15 +619,18 @@ const getNormalizedEntryStatic = (entry) => {
612
619
  };
613
620
 
614
621
  /**
615
- * Gets normalized optimization minimize: the `true` shorthand becomes an empty
616
- * per-asset-type object, so downstream only sees `false` or the object form.
622
+ * Gets the per-asset-type minimize options. `minimize` itself stays a boolean,
623
+ * and its object form is a shorthand an explicit `minimizeOptions` overrides.
617
624
  * @param {Optimization["minimize"]} minimize minimize option
618
- * @returns {OptimizationNormalized["minimize"]} normalized minimize option
625
+ * @param {Optimization["minimizeOptions"]} minimizeOptions minimizeOptions option
626
+ * @returns {OptimizationNormalized["minimizeOptions"]} normalized minimize options
619
627
  */
620
- const getNormalizedOptimizationMinimize = (minimize) => {
621
- if (minimize === undefined) return;
622
- if (minimize === false) return false;
623
- if (minimize === true) return {};
628
+ const getNormalizedOptimizationMinimizeOptions = (
629
+ minimize,
630
+ minimizeOptions
631
+ ) => {
632
+ if (minimizeOptions !== undefined) return cloneObject(minimizeOptions);
633
+ if (typeof minimize !== "object") return;
624
634
  return cloneObject(minimize);
625
635
  };
626
636
 
@@ -20,6 +20,7 @@ const HarmonyImportGuard = require("./HarmonyImportGuard");
20
20
  const ModuleDependency = require("./ModuleDependency");
21
21
 
22
22
  /** @import { ReplaceSource } from "webpack-sources" */
23
+ /** @import ConcatenationScope from "../ConcatenationScope" */
23
24
  /**
24
25
  * @import {
25
26
  * GetConditionFn,
@@ -30,6 +31,7 @@ const ModuleDependency = require("./ModuleDependency");
30
31
  /** @import { DependencyTemplateContext } from "../DependencyTemplate" */
31
32
  /** @import Module from "../Module" */
32
33
  /** @import ModuleGraph from "../ModuleGraph" */
34
+ /** @import ModuleGraphConnection from "../ModuleGraphConnection" */
33
35
  /** @import { Range } from "../javascript/JavascriptParser" */
34
36
  /** @import { UsedByExports } from "../optimize/InnerGraph" */
35
37
  /** @import { RuntimeSpec } from "../util/runtime" */
@@ -200,6 +202,19 @@ class CommonJsRequireDependency extends ModuleDependency {
200
202
  return concatenateCommonJsModules && this._canConcatenate;
201
203
  }
202
204
 
205
+ /**
206
+ * @param {ModuleGraphConnection} connection the active connection of this dependency
207
+ * @param {ConcatenationScope} concatenationScope the concatenation scope
208
+ * @returns {boolean} true when the call becomes a module reference
209
+ */
210
+ rendersAsConcatenatedReference(connection, concatenationScope) {
211
+ return (
212
+ this._canConcatenate &&
213
+ this.valueRange !== undefined &&
214
+ concatenationScope.isModuleInScope(connection.module)
215
+ );
216
+ }
217
+
203
218
  /**
204
219
  * Serializes this instance into the provided serializer context.
205
220
  * @param {ObjectSerializerContext} context context
@@ -278,9 +293,9 @@ CommonJsRequireDependency.Template = class CommonJsRequireDependencyTemplate ext
278
293
  if (
279
294
  connection &&
280
295
  concatenationScope !== undefined &&
281
- dep.valueRange !== undefined &&
282
- concatenationScope.isModuleInScope(connection.module)
296
+ dep.rendersAsConcatenatedReference(connection, concatenationScope)
283
297
  ) {
298
+ const valueRange = /** @type {Range} */ (dep.valueRange);
284
299
  let content = concatenationScope.createModuleReference(
285
300
  connection.module,
286
301
  {
@@ -300,8 +315,7 @@ CommonJsRequireDependency.Template = class CommonJsRequireDependencyTemplate ext
300
315
  );
301
316
  content = `${RuntimeGlobals.constructRequire}(${content})`;
302
317
  }
303
- source.replace(dep.valueRange[0], dep.valueRange[1] - 1, content);
304
- concatenationScope.registerReplacedRequire(dep.valueRange);
318
+ source.replace(valueRange[0], valueRange[1] - 1, content);
305
319
  return;
306
320
  }
307
321
  const importedModule = /** @type {Module} */ (moduleGraph.getModule(dep));