@depup/webpack 5.110.1-depup.0 → 5.110.3-depup.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/changes.json +3 -3
- package/lib/ChunkGraph.js +20 -18
- package/lib/Compilation.js +1 -1
- package/lib/ConcatenationScope.js +25 -35
- package/lib/DotenvPlugin.js +41 -13
- package/lib/ExportsInfo.js +10 -0
- package/lib/FlagDependencyUsagePlugin.js +8 -2
- package/lib/NormalModule.js +13 -20
- package/lib/RuntimePlugin.js +3 -0
- package/lib/RuntimeTemplate.js +437 -432
- package/lib/async-modules/AsyncModuleHelpers.js +10 -4
- package/lib/config/defaults.js +26 -16
- package/lib/config/normalization.js +18 -8
- package/lib/css/CssLoadingRuntimeModule.js +32 -13
- package/lib/css/CssModulesPlugin.js +9 -17
- package/lib/css/data.js +13 -3
- package/lib/css/syntax.js +78 -10
- package/lib/dependencies/CommonJsRequireDependency.js +18 -4
- package/lib/dependencies/ContextDependencyTemplateAsRequireCall.js +20 -7
- package/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js +10 -4
- package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +2 -0
- package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +143 -17
- package/lib/dependencies/HarmonyImportDependency.js +94 -41
- package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +93 -14
- package/lib/dependencies/HarmonyImportSideEffectDependency.js +114 -0
- package/lib/dependencies/HtmlEntryDependency.js +12 -2
- package/lib/dependencies/ImportContextDependency.js +43 -3
- package/lib/dependencies/ImportDependency.js +8 -29
- package/lib/dependencies/ImportParserPlugin.js +24 -14
- package/lib/dependencies/RequireHeaderDependency.js +20 -21
- package/lib/dependencies/WorkerAndWorkletPlugin.js +1 -0
- package/lib/dependencies/WorkerDependency.js +6 -12
- package/lib/dependencies/WorkletDependency.js +7 -11
- package/lib/dependencies/importOptionsCheck.js +47 -0
- package/lib/errors/DotenvFileError.js +29 -0
- package/lib/errors/ImportedBindingAssignmentError.js +30 -0
- package/lib/esm/ModuleChunkLoadingPlugin.js +6 -9
- package/lib/esm/ModuleChunkLoadingRuntimeModule.js +7 -1
- package/lib/html/HtmlModulesPlugin.js +84 -38
- package/lib/html/syntax.js +417 -169
- package/lib/javascript/JavascriptModulesPlugin.js +5 -10
- package/lib/loaders/LoaderRunner.js +33 -5
- package/lib/node/ReadFileCompileAsyncWasmPlugin.js +8 -15
- package/lib/optimize/ConcatenatedModule.js +46 -7
- package/lib/optimize/ConstExportsPlugin.js +5 -0
- package/lib/optimize/InlineExports.js +121 -0
- package/lib/optimize/RealContentHashPlugin.js +111 -35
- package/lib/prefetch/ResourceHintPlugin.js +12 -13
- package/lib/runtime/AsyncModuleRuntimeModule.js +5 -1
- package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +11 -1
- package/lib/schemes/HttpUriPlugin.js +22 -5
- package/lib/serialization/FileMiddleware.js +15 -2
- package/lib/typescript/TypeScriptPlugin.js +8 -10
- package/lib/util/ArrayQueue.js +3 -3
- package/lib/util/SourceProcessor.js +24 -8
- package/lib/util/URLAbsoluteSpecifier.js +3 -1
- package/lib/util/conventions.js +5 -1
- package/lib/util/createMappings.js +25 -3
- package/lib/util/internalSerializables.js +2 -0
- package/lib/util/removeBOM.js +82 -1
- package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +5 -3
- package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +5 -3
- package/lib/wasm-async/UniversalCompileAsyncWasmPlugin.js +7 -12
- package/lib/web/FetchCompileAsyncWasmPlugin.js +10 -6
- package/lib/web/FetchCompileWasmPlugin.js +4 -2
- package/package.json +7 -7
- package/schemas/WebpackOptions.check.js +1 -1
- package/schemas/WebpackOptions.json +9 -10
- package/types.d.ts +95 -27
|
@@ -50,7 +50,7 @@ const {
|
|
|
50
50
|
const createHash = require("../util/createHash");
|
|
51
51
|
const createHooksRegistry = require("../util/createHooksRegistry");
|
|
52
52
|
const { digestNonNumericOnlyWithFull } = require("../util/nonNumericOnlyHash");
|
|
53
|
-
const
|
|
53
|
+
const { removeBOMFromResult } = require("../util/removeBOM");
|
|
54
54
|
const { intersectRuntime } = require("../util/runtime");
|
|
55
55
|
const { getAllChunks } = require("./ChunkHelpers");
|
|
56
56
|
const JavascriptGenerator = require("./JavascriptGenerator");
|
|
@@ -528,15 +528,8 @@ class JavascriptModulesPlugin {
|
|
|
528
528
|
|
|
529
529
|
NormalModule.getCompilationHooks(compilation).processResult.tap(
|
|
530
530
|
PLUGIN_NAME,
|
|
531
|
-
(result, module) =>
|
|
532
|
-
|
|
533
|
-
const [source, ...rest] = result;
|
|
534
|
-
|
|
535
|
-
return [removeBOM(source), ...rest];
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
return result;
|
|
539
|
-
}
|
|
531
|
+
(result, module) =>
|
|
532
|
+
module.type === type ? removeBOMFromResult(result) : result
|
|
540
533
|
);
|
|
541
534
|
}
|
|
542
535
|
|
|
@@ -1687,6 +1680,8 @@ class JavascriptModulesPlugin {
|
|
|
1687
1680
|
continue;
|
|
1688
1681
|
}
|
|
1689
1682
|
}
|
|
1683
|
+
// An async entry is not awaited here: that needs top-level `await`,
|
|
1684
|
+
// gated on an option held back from the patch release (revert #21881).
|
|
1690
1685
|
let i = jsEntries.length;
|
|
1691
1686
|
for (const [entryModule, entrypoint] of jsEntries) {
|
|
1692
1687
|
const chunks =
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const { readFile } = require("fs");
|
|
9
9
|
const { parseResource } = require("../util/identifier");
|
|
10
|
+
const { adjustSourceMapForRemovedBOM } = require("../util/removeBOM");
|
|
10
11
|
const loadLoader = require("./loadLoader");
|
|
11
12
|
|
|
12
13
|
// Set on a loader context to have each loader's own run measured. Absent
|
|
@@ -59,6 +60,8 @@ const HASH_ESCAPE_REGEXP = /#/g;
|
|
|
59
60
|
const UTF8_BOM_0 = 0xef;
|
|
60
61
|
const UTF8_BOM_1 = 0xbb;
|
|
61
62
|
const UTF8_BOM_2 = 0xbf;
|
|
63
|
+
// U+FEFF, the BOM once decoded
|
|
64
|
+
const BOM_CHAR_CODE = 0xfeff;
|
|
62
65
|
|
|
63
66
|
/**
|
|
64
67
|
* @param {Buffer} buf buffer
|
|
@@ -76,6 +79,14 @@ function utf8BufferToString(buf) {
|
|
|
76
79
|
return buf.toString("utf8");
|
|
77
80
|
}
|
|
78
81
|
|
|
82
|
+
/**
|
|
83
|
+
* @param {string} str string
|
|
84
|
+
* @returns {string} string, with a leading BOM removed
|
|
85
|
+
*/
|
|
86
|
+
function stringWithoutBOM(str) {
|
|
87
|
+
return str.charCodeAt(0) === BOM_CHAR_CODE ? str.slice(1) : str;
|
|
88
|
+
}
|
|
89
|
+
|
|
79
90
|
/**
|
|
80
91
|
* Escape `#` with a preceding `\0` byte; short-circuits when there is no `#`.
|
|
81
92
|
* @param {string} str input string
|
|
@@ -292,9 +303,19 @@ function runSyncOrAsync(fn, context, args, callback) {
|
|
|
292
303
|
/**
|
|
293
304
|
* @param {EXPECTED_ANY[]} args arguments
|
|
294
305
|
* @param {boolean | null=} raw whether the loader wants a Buffer
|
|
306
|
+
* @param {boolean=} fromLoader whether args came from a loader rather than the resource
|
|
295
307
|
* @returns {void}
|
|
296
308
|
*/
|
|
297
|
-
function convertArgs(args, raw) {
|
|
309
|
+
function convertArgs(args, raw, fromLoader) {
|
|
310
|
+
// A BOM only means anything at the start of a file, so one a loader produced
|
|
311
|
+
// is content the next loader must not see. The resource keeps its own.
|
|
312
|
+
if (fromLoader && typeof args[0] === "string") {
|
|
313
|
+
const withoutBOM = stringWithoutBOM(args[0]);
|
|
314
|
+
if (withoutBOM !== args[0]) {
|
|
315
|
+
args[0] = withoutBOM;
|
|
316
|
+
if (args[1]) args[1] = adjustSourceMapForRemovedBOM(args[1]);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
298
319
|
if (!raw && Buffer.isBuffer(args[0])) {
|
|
299
320
|
args[0] = utf8BufferToString(args[0]);
|
|
300
321
|
} else if (raw && typeof args[0] === "string") {
|
|
@@ -307,9 +328,16 @@ function convertArgs(args, raw) {
|
|
|
307
328
|
* @param {LoaderContext} loaderContext the loader context
|
|
308
329
|
* @param {EXPECTED_ANY[]} args arguments
|
|
309
330
|
* @param {(err: Error | null, args?: EXPECTED_ANY[]) => void} callback callback
|
|
331
|
+
* @param {boolean=} fromLoader whether args came from a loader rather than the resource
|
|
310
332
|
* @returns {void}
|
|
311
333
|
*/
|
|
312
|
-
function iterateNormalLoaders(
|
|
334
|
+
function iterateNormalLoaders(
|
|
335
|
+
options,
|
|
336
|
+
loaderContext,
|
|
337
|
+
args,
|
|
338
|
+
callback,
|
|
339
|
+
fromLoader
|
|
340
|
+
) {
|
|
313
341
|
while (loaderContext.loaderIndex >= 0) {
|
|
314
342
|
const currentLoaderObject =
|
|
315
343
|
loaderContext.loaders[loaderContext.loaderIndex];
|
|
@@ -327,11 +355,11 @@ function iterateNormalLoaders(options, loaderContext, args, callback) {
|
|
|
327
355
|
continue;
|
|
328
356
|
}
|
|
329
357
|
|
|
330
|
-
convertArgs(args, currentLoaderObject.raw);
|
|
358
|
+
convertArgs(args, currentLoaderObject.raw, fromLoader);
|
|
331
359
|
|
|
332
360
|
return runSyncOrAsync(fn, loaderContext, args, (err, ...nextArgs) => {
|
|
333
361
|
if (err) return callback(err);
|
|
334
|
-
iterateNormalLoaders(options, loaderContext, nextArgs, callback);
|
|
362
|
+
iterateNormalLoaders(options, loaderContext, nextArgs, callback, true);
|
|
335
363
|
});
|
|
336
364
|
}
|
|
337
365
|
|
|
@@ -410,7 +438,7 @@ function iteratePitchingLoaders(options, loaderContext, callback) {
|
|
|
410
438
|
}
|
|
411
439
|
if (hasArg) {
|
|
412
440
|
loaderContext.loaderIndex--;
|
|
413
|
-
iterateNormalLoaders(options, loaderContext, args, callback);
|
|
441
|
+
iterateNormalLoaders(options, loaderContext, args, callback, true);
|
|
414
442
|
} else {
|
|
415
443
|
iteratePitchingLoaders(options, loaderContext, callback);
|
|
416
444
|
}
|
|
@@ -62,27 +62,20 @@ class ReadFileCompileAsyncWasmPlugin {
|
|
|
62
62
|
// settled while the plugin is being applied.
|
|
63
63
|
/**
|
|
64
64
|
* @param {string} path rendered path to the wasm file
|
|
65
|
-
* @param {
|
|
65
|
+
* @param {boolean} analyzable whether the call site bakes a complete URL
|
|
66
66
|
* @returns {string} the argument to hand the reader
|
|
67
67
|
*/
|
|
68
|
-
const wasmUrlArg = (path,
|
|
69
|
-
compilation.runtimeTemplate.
|
|
70
|
-
"wasm",
|
|
71
|
-
undefined,
|
|
72
|
-
undefined,
|
|
73
|
-
runtime
|
|
74
|
-
)
|
|
75
|
-
? path
|
|
76
|
-
: compilation.runtimeTemplate.importMetaUrl(path);
|
|
68
|
+
const wasmUrlArg = (path, analyzable) =>
|
|
69
|
+
analyzable ? path : compilation.runtimeTemplate.importMetaUrl(path);
|
|
77
70
|
/**
|
|
78
|
-
* @type {(path: string, runtime: RuntimeSpec) => string} callback to generate code to load the wasm file
|
|
71
|
+
* @type {(path: string, runtime: RuntimeSpec, analyzable: boolean) => string} callback to generate code to load the wasm file
|
|
79
72
|
*/
|
|
80
73
|
const generateLoadBinaryCode = this._import
|
|
81
|
-
? (path, runtime) =>
|
|
74
|
+
? (path, runtime, analyzable) =>
|
|
82
75
|
Template.asString([
|
|
83
76
|
"Promise.all([import('fs'), import('url')]).then(([{ readFile }, { URL }]) => new Promise((resolve, reject) => {",
|
|
84
77
|
Template.indent([
|
|
85
|
-
`readFile(${wasmUrlArg(path,
|
|
78
|
+
`readFile(${wasmUrlArg(path, analyzable)}, (err, buffer) => {`,
|
|
86
79
|
Template.indent([
|
|
87
80
|
"if (err) return reject(err);",
|
|
88
81
|
"",
|
|
@@ -154,7 +147,7 @@ class ReadFileCompileAsyncWasmPlugin {
|
|
|
154
147
|
if (
|
|
155
148
|
!compilation.runtimeTemplate.supportsAnalyzable(
|
|
156
149
|
"wasm",
|
|
157
|
-
|
|
150
|
+
chunkGraph,
|
|
158
151
|
undefined,
|
|
159
152
|
chunk.runtime
|
|
160
153
|
) &&
|
|
@@ -196,7 +189,7 @@ class ReadFileCompileAsyncWasmPlugin {
|
|
|
196
189
|
if (
|
|
197
190
|
!compilation.runtimeTemplate.supportsAnalyzable(
|
|
198
191
|
"wasm",
|
|
199
|
-
|
|
192
|
+
chunkGraph,
|
|
200
193
|
undefined,
|
|
201
194
|
chunk.runtime
|
|
202
195
|
) &&
|
|
@@ -181,6 +181,7 @@ const findIdentifiers = (source) => {
|
|
|
181
181
|
* @property {string=} comment
|
|
182
182
|
* @property {ExportName} ids
|
|
183
183
|
* @property {ExportName} exportName
|
|
184
|
+
* @property {boolean=} accessorCall the raw name is the wrapper accessor call itself, so it binds looser than the identifier it replaces
|
|
184
185
|
*/
|
|
185
186
|
|
|
186
187
|
/**
|
|
@@ -494,11 +495,14 @@ const getFinalBinding = (
|
|
|
494
495
|
/** @type {NonNullable<ConcatenatedModuleInfo["namespaceObjectName"]>} */
|
|
495
496
|
(info.namespaceObjectName),
|
|
496
497
|
ids: /** @type {ExportName} */ (usedName),
|
|
497
|
-
exportName
|
|
498
|
+
exportName,
|
|
499
|
+
accessorCall: info.wrapped
|
|
498
500
|
};
|
|
499
501
|
}
|
|
500
502
|
|
|
501
|
-
if (
|
|
503
|
+
if (moduleExportsAccess) {
|
|
504
|
+
// Skip ESM interop when require() targets a module outside the concatenation
|
|
505
|
+
} else if (exportName.length === 0) {
|
|
502
506
|
switch (exportsType) {
|
|
503
507
|
case "default-only":
|
|
504
508
|
if (deferred) info.deferredNamespaceObjectUsed = true;
|
|
@@ -509,7 +513,8 @@ const getFinalBinding = (
|
|
|
509
513
|
? /** @type {string} */ (info.deferredNamespaceObjectName)
|
|
510
514
|
: getInteropExpr(info, info.interopNamespaceObject2Name),
|
|
511
515
|
ids: exportName,
|
|
512
|
-
exportName
|
|
516
|
+
exportName,
|
|
517
|
+
accessorCall: !deferred && info.wrapped
|
|
513
518
|
};
|
|
514
519
|
case "default-with-named":
|
|
515
520
|
if (deferred) info.deferredNamespaceObjectUsed = true;
|
|
@@ -520,7 +525,8 @@ const getFinalBinding = (
|
|
|
520
525
|
? /** @type {string} */ (info.deferredNamespaceObjectName)
|
|
521
526
|
: getInteropExpr(info, info.interopNamespaceObjectName),
|
|
522
527
|
ids: exportName,
|
|
523
|
-
exportName
|
|
528
|
+
exportName,
|
|
529
|
+
accessorCall: !deferred && info.wrapped
|
|
524
530
|
};
|
|
525
531
|
case "namespace":
|
|
526
532
|
case "dynamic":
|
|
@@ -611,7 +617,8 @@ const getFinalBinding = (
|
|
|
611
617
|
info,
|
|
612
618
|
rawName: getExternalModuleExpr(info),
|
|
613
619
|
ids: exportName,
|
|
614
|
-
exportName
|
|
620
|
+
exportName,
|
|
621
|
+
accessorCall: info.wrapped
|
|
615
622
|
};
|
|
616
623
|
}
|
|
617
624
|
info.interopDefaultAccessUsed = true;
|
|
@@ -657,7 +664,8 @@ const getFinalBinding = (
|
|
|
657
664
|
/** @type {NonNullable<ConcatenatedModuleInfo["namespaceObjectName"]>} */
|
|
658
665
|
(info.namespaceObjectName),
|
|
659
666
|
ids: exportName,
|
|
660
|
-
exportName
|
|
667
|
+
exportName,
|
|
668
|
+
accessorCall: info.wrapped
|
|
661
669
|
};
|
|
662
670
|
case "external":
|
|
663
671
|
if (deferred) {
|
|
@@ -673,7 +681,8 @@ const getFinalBinding = (
|
|
|
673
681
|
info,
|
|
674
682
|
rawName: getExternalModuleExpr(info),
|
|
675
683
|
ids: exportName,
|
|
676
|
-
exportName
|
|
684
|
+
exportName,
|
|
685
|
+
accessorCall: info.wrapped
|
|
677
686
|
};
|
|
678
687
|
}
|
|
679
688
|
}
|
|
@@ -938,9 +947,13 @@ const getFinalName = (
|
|
|
938
947
|
let reference;
|
|
939
948
|
/** @type {boolean} */
|
|
940
949
|
let isPropertyAccess;
|
|
950
|
+
// the raw name is the wrapper accessor call itself, so the reference binds
|
|
951
|
+
// looser than the identifier it stands in for
|
|
952
|
+
let accessorCall = false;
|
|
941
953
|
if ("rawName" in binding) {
|
|
942
954
|
reference = `${binding.rawName}${comment || ""}${propertyAccess(ids)}`;
|
|
943
955
|
isPropertyAccess = ids.length > 0;
|
|
956
|
+
accessorCall = binding.accessorCall === true;
|
|
944
957
|
} else {
|
|
945
958
|
const { info, name: exportId } = binding;
|
|
946
959
|
const name = info.internalNames.get(exportId);
|
|
@@ -970,6 +983,10 @@ const getFinalName = (
|
|
|
970
983
|
} else if (binding.info.wrapped) {
|
|
971
984
|
return asiSafe === false ? `;(${reference})` : `(${reference})`;
|
|
972
985
|
}
|
|
986
|
+
} else if (accessorCall && !asCall) {
|
|
987
|
+
// without parentheses `new X()` would construct the accessor rather
|
|
988
|
+
// than what it returns
|
|
989
|
+
return asiSafe === false ? `;(${reference})` : `(${reference})`;
|
|
973
990
|
}
|
|
974
991
|
|
|
975
992
|
return reference;
|
|
@@ -1415,10 +1432,18 @@ class ConcatenatedModule extends Module {
|
|
|
1415
1432
|
runtimeCondition,
|
|
1416
1433
|
runtime
|
|
1417
1434
|
);
|
|
1435
|
+
const wasDeferredOnly = !entry.nonDeferAccess;
|
|
1418
1436
|
entry.nonDeferAccess = mergeNonDeferAccess(
|
|
1419
1437
|
entry.nonDeferAccess,
|
|
1420
1438
|
nonDeferAccess
|
|
1421
1439
|
);
|
|
1440
|
+
// A deferred import does not evaluate, so the first eager reference
|
|
1441
|
+
// fixes the position; insertion order is emit order.
|
|
1442
|
+
if (wasDeferredOnly && nonDeferAccess) {
|
|
1443
|
+
entry.connection = connection;
|
|
1444
|
+
referencesMap.delete(module);
|
|
1445
|
+
referencesMap.set(module, entry);
|
|
1446
|
+
}
|
|
1422
1447
|
}
|
|
1423
1448
|
if (targets !== undefined) importConnections.set(module, targets);
|
|
1424
1449
|
return referencesMap.values();
|
|
@@ -1841,6 +1866,13 @@ class ConcatenatedModule extends Module {
|
|
|
1841
1866
|
for (const reference of ConcatenationScope.findModuleReferences(
|
|
1842
1867
|
/** @type {Source} */ (info.internalSource)
|
|
1843
1868
|
)) {
|
|
1869
|
+
if (reference.leaked) {
|
|
1870
|
+
throw new Error(
|
|
1871
|
+
`Unsubstituted module reference "${reference.name}" in ${info.module.readableIdentifier(
|
|
1872
|
+
requestShortener
|
|
1873
|
+
)}`
|
|
1874
|
+
);
|
|
1875
|
+
}
|
|
1844
1876
|
if (visitedReferences.has(reference.name)) continue;
|
|
1845
1877
|
visitedReferences.add(reference.name);
|
|
1846
1878
|
const match = ConcatenationScope.matchModuleReference(
|
|
@@ -1934,6 +1966,13 @@ class ConcatenatedModule extends Module {
|
|
|
1934
1966
|
ignoredScopes
|
|
1935
1967
|
);
|
|
1936
1968
|
} else {
|
|
1969
|
+
if (ConcatenationScope.isLeakedModuleReference(name)) {
|
|
1970
|
+
throw new Error(
|
|
1971
|
+
`Unsubstituted module reference "${name}" in ${info.module.readableIdentifier(
|
|
1972
|
+
requestShortener
|
|
1973
|
+
)}`
|
|
1974
|
+
);
|
|
1975
|
+
}
|
|
1937
1976
|
allUsedNames.add(name);
|
|
1938
1977
|
freeNames.add(name);
|
|
1939
1978
|
}
|
|
@@ -15,6 +15,7 @@ const { VariableInfoFlags } = require("../javascript/JavascriptParser");
|
|
|
15
15
|
const {
|
|
16
16
|
InlinedUsedName,
|
|
17
17
|
enableInlineExports,
|
|
18
|
+
findModulesInCycles,
|
|
18
19
|
toInlinedValue
|
|
19
20
|
} = require("./InlineExports");
|
|
20
21
|
|
|
@@ -162,11 +163,15 @@ class ConstExportsPlugin {
|
|
|
162
163
|
|
|
163
164
|
const moduleGraph = compilation.moduleGraph;
|
|
164
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);
|
|
165
169
|
/** @type {Set<ExportsInfo>} */
|
|
166
170
|
const visited = new Set();
|
|
167
171
|
/** @type {ExportsInfo[]} */
|
|
168
172
|
let queue = [];
|
|
169
173
|
for (const module of modules) {
|
|
174
|
+
if (inCycle.has(module)) continue;
|
|
170
175
|
queue.push(moduleGraph.getExportsInfo(module));
|
|
171
176
|
}
|
|
172
177
|
while (queue.length > 0) {
|
|
@@ -172,10 +172,131 @@ const isExportInlined = (moduleGraph, module, ids, runtime) => {
|
|
|
172
172
|
return moduleGraph.getExportsInfo(module).hasInlinedUsedName(ids, runtime);
|
|
173
173
|
};
|
|
174
174
|
|
|
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
|
+
|
|
175
295
|
module.exports.InlinedUsedName = InlinedUsedName;
|
|
176
296
|
module.exports.InlinedValue = InlinedValue;
|
|
177
297
|
module.exports.MAX_INLINE_BYTES = MAX_INLINE_BYTES;
|
|
178
298
|
module.exports.enableInlineExports = enableInlineExports;
|
|
299
|
+
module.exports.findModulesInCycles = findModulesInCycles;
|
|
179
300
|
module.exports.isExportInlined = isExportInlined;
|
|
180
301
|
module.exports.isInlineEnabled = isInlineEnabled;
|
|
181
302
|
module.exports.isInlineExportsEnabled = isInlineExportsEnabled;
|
|
@@ -345,47 +345,68 @@ ${referencingAssets
|
|
|
345
345
|
}
|
|
346
346
|
return hashes;
|
|
347
347
|
};
|
|
348
|
+
// Tarjan: hashes naming each other come out as one group where a chain
|
|
349
|
+
// walk would never end, and groups arrive dependencies-first as needed.
|
|
350
|
+
/** @type {string[][]} */
|
|
351
|
+
const groupsInOrder = [];
|
|
352
|
+
/** @type {Map<string, number>} */
|
|
353
|
+
const indexOf = new Map();
|
|
354
|
+
/** @type {Map<string, number>} */
|
|
355
|
+
const lowOf = new Map();
|
|
356
|
+
/** @type {Set<string>} */
|
|
357
|
+
const onStack = new Set();
|
|
358
|
+
/** @type {string[]} */
|
|
359
|
+
const stack = [];
|
|
348
360
|
/**
|
|
349
|
-
*
|
|
361
|
+
* Visits one hash and everything its assets reference.
|
|
350
362
|
* @param {string} hash the hash
|
|
351
|
-
* @returns {
|
|
363
|
+
* @returns {void}
|
|
352
364
|
*/
|
|
353
|
-
const
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
)
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
for (const hash of hashToAssets.keys()) {
|
|
363
|
-
/**
|
|
364
|
-
* Processes the provided hash.
|
|
365
|
-
* @param {string} hash the hash
|
|
366
|
-
* @param {Set<string>} stack stack of hashes
|
|
367
|
-
*/
|
|
368
|
-
const add = (hash, stack) => {
|
|
369
|
-
const deps = getDependencies(hash);
|
|
370
|
-
if (!deps) return;
|
|
371
|
-
stack.add(hash);
|
|
365
|
+
const strongConnect = (hash) => {
|
|
366
|
+
const index = indexOf.size;
|
|
367
|
+
indexOf.set(hash, index);
|
|
368
|
+
lowOf.set(hash, index);
|
|
369
|
+
stack.push(hash);
|
|
370
|
+
onStack.add(hash);
|
|
371
|
+
// An unowned reference reports the caching problem and stays a leaf.
|
|
372
|
+
const deps = getDependencies(hash);
|
|
373
|
+
if (deps) {
|
|
372
374
|
for (const dep of deps) {
|
|
373
|
-
if (
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
375
|
+
if (!indexOf.has(dep)) {
|
|
376
|
+
strongConnect(dep);
|
|
377
|
+
lowOf.set(
|
|
378
|
+
hash,
|
|
379
|
+
Math.min(
|
|
380
|
+
/** @type {number} */ (lowOf.get(hash)),
|
|
381
|
+
/** @type {number} */ (lowOf.get(dep))
|
|
382
|
+
)
|
|
383
|
+
);
|
|
384
|
+
} else if (onStack.has(dep)) {
|
|
385
|
+
lowOf.set(
|
|
386
|
+
hash,
|
|
387
|
+
Math.min(
|
|
388
|
+
/** @type {number} */ (lowOf.get(hash)),
|
|
389
|
+
/** @type {number} */ (indexOf.get(dep))
|
|
390
|
+
)
|
|
380
391
|
);
|
|
381
392
|
}
|
|
382
|
-
add(dep, stack);
|
|
383
393
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
394
|
+
}
|
|
395
|
+
if (lowOf.get(hash) === indexOf.get(hash)) {
|
|
396
|
+
/** @type {string[]} */
|
|
397
|
+
const group = [];
|
|
398
|
+
/** @type {string} */
|
|
399
|
+
let member;
|
|
400
|
+
do {
|
|
401
|
+
member = /** @type {string} */ (stack.pop());
|
|
402
|
+
onStack.delete(member);
|
|
403
|
+
if (hashToAssets.has(member)) group.push(member);
|
|
404
|
+
} while (member !== hash);
|
|
405
|
+
if (group.length > 0) groupsInOrder.push(group);
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
for (const hash of hashToAssets.keys()) {
|
|
409
|
+
if (!indexOf.has(hash)) strongConnect(hash);
|
|
389
410
|
}
|
|
390
411
|
/** @type {Map<string, string>} */
|
|
391
412
|
const hashToNewHash = new Map();
|
|
@@ -474,7 +495,62 @@ ${referencingAssets
|
|
|
474
495
|
};
|
|
475
496
|
/** @type {Comparator<AssetInfoForRealContentHash>} */
|
|
476
497
|
const comparator = compareSelect((a) => a.name, compareStrings);
|
|
477
|
-
for (const
|
|
498
|
+
for (const group of groupsInOrder) {
|
|
499
|
+
// A cycle is assigned as one group: members are told apart by stable
|
|
500
|
+
// markers, so the group hashes to a fixed point instead of chasing one.
|
|
501
|
+
if (group.length > 1) {
|
|
502
|
+
const members = group.sort();
|
|
503
|
+
/** @type {Map<string, string>} */
|
|
504
|
+
const markerOf = new Map(
|
|
505
|
+
members.map((hash, i) => [hash, `|webpack/scc/${i}|`])
|
|
506
|
+
);
|
|
507
|
+
for (let i = 0; i < members.length; i++) {
|
|
508
|
+
const oldHash = members[i];
|
|
509
|
+
const memberAssets =
|
|
510
|
+
/** @type {AssetInfoForRealContentHash[]} */
|
|
511
|
+
(hashToAssets.get(oldHash));
|
|
512
|
+
memberAssets.sort(comparator);
|
|
513
|
+
const hash = createHash(this._hashFunction);
|
|
514
|
+
if (compilation.outputOptions.hashSalt) {
|
|
515
|
+
hash.update(compilation.outputOptions.hashSalt);
|
|
516
|
+
}
|
|
517
|
+
// Two members can normalize to the same text; the position keeps
|
|
518
|
+
// their hashes apart, and sorting keeps the position stable.
|
|
519
|
+
hash.update(`${i}`);
|
|
520
|
+
for (const asset of memberAssets) {
|
|
521
|
+
if (Buffer.isBuffer(asset.content)) {
|
|
522
|
+
hash.update(asset.content);
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
const withoutOwn =
|
|
526
|
+
/** @type {Hashes} */
|
|
527
|
+
(asset.ownHashes).has(oldHash);
|
|
528
|
+
hash.update(
|
|
529
|
+
asset.content.replace(hashRegExp, (matched) => {
|
|
530
|
+
// Its own occurrences vanish, exactly as a lone hash's do.
|
|
531
|
+
if (
|
|
532
|
+
withoutOwn &&
|
|
533
|
+
/** @type {Hashes} */ (asset.ownHashes).has(matched)
|
|
534
|
+
) {
|
|
535
|
+
return "";
|
|
536
|
+
}
|
|
537
|
+
const marker = markerOf.get(matched);
|
|
538
|
+
if (marker !== undefined) return marker;
|
|
539
|
+
const mapped = hashToNewHash.get(matched);
|
|
540
|
+
return mapped === undefined ? matched : mapped;
|
|
541
|
+
})
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
const digest = hash.digest(
|
|
545
|
+
/** @type {HashDigest} */ (
|
|
546
|
+
hashToDigest.get(oldHash) || this._hashDigest
|
|
547
|
+
)
|
|
548
|
+
);
|
|
549
|
+
hashToNewHash.set(oldHash, digest.slice(0, oldHash.length));
|
|
550
|
+
}
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
const oldHash = group[0];
|
|
478
554
|
const assets =
|
|
479
555
|
/** @type {AssetInfoForRealContentHash[]} */
|
|
480
556
|
(hashToAssets.get(oldHash));
|
|
@@ -300,7 +300,12 @@ const collectEntrypointHints = (compilation, entryName, hints) => {
|
|
|
300
300
|
const out = [];
|
|
301
301
|
/** @type {Set<string>} */
|
|
302
302
|
const seenKeys = new Set();
|
|
303
|
-
|
|
303
|
+
/**
|
|
304
|
+
* Appends a hint, dropping one already emitted for the same rel + href.
|
|
305
|
+
* @param {EntrypointHint} h hint to append
|
|
306
|
+
* @returns {void}
|
|
307
|
+
*/
|
|
308
|
+
const push = (h) => {
|
|
304
309
|
const key = `${h.rel}\0${h.href}`;
|
|
305
310
|
if (seenKeys.has(key)) return;
|
|
306
311
|
seenKeys.add(key);
|
|
@@ -481,19 +486,13 @@ const collectStartupAssetHintLines = (
|
|
|
481
486
|
seenWorkerChunks.add(workerChunk.id);
|
|
482
487
|
// The same specifier the `new Worker(...)` call site bakes, asked of the
|
|
483
488
|
// same module, so the two agree on whether a literal is spellable.
|
|
484
|
-
const specifier = runtimeTemplate.
|
|
485
|
-
|
|
489
|
+
const specifier = runtimeTemplate.getAnalyzableWorkerUrl(
|
|
490
|
+
dep.options.publicPath,
|
|
491
|
+
workerChunk,
|
|
492
|
+
module,
|
|
486
493
|
chunkGraph,
|
|
487
|
-
|
|
488
|
-
)
|
|
489
|
-
? runtimeTemplate._getAnalyzableChunkSpecifier(
|
|
490
|
-
dep.options.publicPath,
|
|
491
|
-
workerChunk,
|
|
492
|
-
module,
|
|
493
|
-
chunkGraph,
|
|
494
|
-
set
|
|
495
|
-
)
|
|
496
|
-
: null;
|
|
494
|
+
set
|
|
495
|
+
);
|
|
497
496
|
// Without one the call site keeps the runtime form, where it wraps its own
|
|
498
497
|
// href — firing the `<link>` here too would emit the call twice.
|
|
499
498
|
if (specifier === null) continue;
|