webpack 5.110.1 → 5.110.2
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 +10 -17
- package/lib/ExportsInfo.js +10 -0
- package/lib/RuntimeTemplate.js +423 -430
- 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/HarmonyEvaluatedImportSpecifierDependency.js +10 -4
- package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +2 -0
- package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +48 -3
- package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +83 -14
- package/lib/dependencies/HarmonyImportSideEffectDependency.js +114 -0
- package/lib/dependencies/WorkerAndWorkletPlugin.js +1 -0
- package/lib/dependencies/WorkerDependency.js +6 -12
- package/lib/dependencies/WorkletDependency.js +7 -11
- package/lib/esm/ModuleChunkLoadingPlugin.js +6 -9
- package/lib/esm/ModuleChunkLoadingRuntimeModule.js +7 -1
- package/lib/html/HtmlModulesPlugin.js +55 -16
- package/lib/html/syntax.js +113 -26
- package/lib/javascript/JavascriptModulesPlugin.js +3 -10
- package/lib/loaders/LoaderRunner.js +33 -5
- package/lib/node/ReadFileCompileAsyncWasmPlugin.js +8 -15
- package/lib/optimize/ConcatenatedModule.js +8 -0
- 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 +6 -12
- package/lib/typescript/TypeScriptPlugin.js +8 -10
- package/lib/util/SourceProcessor.js +24 -8
- package/lib/util/createMappings.js +25 -3
- 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 +1 -1
- package/types.d.ts +69 -10
|
@@ -43,6 +43,7 @@ const { ImportPhaseUtils, createGetImportPhase } = require("./ImportPhase");
|
|
|
43
43
|
* } from "../../declarations/WebpackOptions"
|
|
44
44
|
*/
|
|
45
45
|
/** @import Module from "../Module" */
|
|
46
|
+
/** @import { JavascriptModuleBuildInfo } from "../javascript/JavascriptModule" */
|
|
46
47
|
/**
|
|
47
48
|
* @import JavascriptParser, {
|
|
48
49
|
* ImportAttributes,
|
|
@@ -71,10 +72,24 @@ const EMPTY_ID_RANGES = [];
|
|
|
71
72
|
* @property {boolean} await
|
|
72
73
|
* @property {ImportAttributes=} attributes
|
|
73
74
|
* @property {ImportPhaseType} phase
|
|
75
|
+
* @property {boolean} used whether the binding is referenced anywhere
|
|
76
|
+
* @property {HarmonyImportSideEffectDependency | undefined} dependency the statement's own dependency
|
|
74
77
|
*/
|
|
75
78
|
|
|
76
79
|
const PLUGIN_NAME = "HarmonyImportDependencyParserPlugin";
|
|
77
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Records that an import binding is referenced, so it is not reported as an
|
|
83
|
+
* unused specifier naming a missing export.
|
|
84
|
+
* @param {HarmonySettings | undefined} settings settings of the referenced tag
|
|
85
|
+
* @returns {boolean} true when the reference was a harmony import specifier
|
|
86
|
+
*/
|
|
87
|
+
const markUsed = (settings) => {
|
|
88
|
+
if (!settings) return false;
|
|
89
|
+
settings.used = true;
|
|
90
|
+
return true;
|
|
91
|
+
};
|
|
92
|
+
|
|
78
93
|
/**
|
|
79
94
|
* Gets in operator harmony import info.
|
|
80
95
|
* @param {JavascriptParser} parser the parser
|
|
@@ -107,6 +122,7 @@ const getInOperatorHarmonyImportInfo = (parser, left, right) => {
|
|
|
107
122
|
if (!settings) {
|
|
108
123
|
return;
|
|
109
124
|
}
|
|
125
|
+
settings.used = true;
|
|
110
126
|
|
|
111
127
|
return {
|
|
112
128
|
leftPart,
|
|
@@ -125,7 +141,10 @@ const getInOperatorHarmonyImportInfo = (parser, left, right) => {
|
|
|
125
141
|
const findImportSpecifier = (parser, node) => {
|
|
126
142
|
switch (node.type) {
|
|
127
143
|
case "Identifier":
|
|
128
|
-
return
|
|
144
|
+
return markUsed(
|
|
145
|
+
/** @type {HarmonySettings | undefined} */
|
|
146
|
+
(parser.getTagData(node.name, harmonySpecifierTag))
|
|
147
|
+
);
|
|
129
148
|
case "UnaryExpression":
|
|
130
149
|
return findImportSpecifier(
|
|
131
150
|
parser,
|
|
@@ -213,6 +232,35 @@ class HarmonyImportDependencyParserPlugin {
|
|
|
213
232
|
this.options.sourceImport
|
|
214
233
|
);
|
|
215
234
|
|
|
235
|
+
// Parsers are reused across modules, so both are reset per program.
|
|
236
|
+
/** @type {HarmonySettings[]} */
|
|
237
|
+
const declaredSpecifiers = [];
|
|
238
|
+
/** @type {HarmonyImportSideEffectDependency | undefined} */
|
|
239
|
+
let lastSideEffectDependency;
|
|
240
|
+
|
|
241
|
+
parser.hooks.program.tap(PLUGIN_NAME, () => {
|
|
242
|
+
declaredSpecifiers.length = 0;
|
|
243
|
+
lastSideEffectDependency = undefined;
|
|
244
|
+
});
|
|
245
|
+
parser.hooks.finish.tap(PLUGIN_NAME, () => {
|
|
246
|
+
for (const settings of declaredSpecifiers) {
|
|
247
|
+
const dependency = settings.dependency;
|
|
248
|
+
if (dependency === undefined || settings.used) continue;
|
|
249
|
+
if (dependency.unusedSpecifiers === undefined) {
|
|
250
|
+
dependency.unusedSpecifiers = {
|
|
251
|
+
exportPresenceMode: this.exportPresenceMode,
|
|
252
|
+
specifiers: []
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
dependency.unusedSpecifiers.specifiers.push([
|
|
256
|
+
settings.ids,
|
|
257
|
+
settings.name
|
|
258
|
+
]);
|
|
259
|
+
}
|
|
260
|
+
declaredSpecifiers.length = 0;
|
|
261
|
+
lastSideEffectDependency = undefined;
|
|
262
|
+
});
|
|
263
|
+
|
|
216
264
|
/**
|
|
217
265
|
* Gets non optional member chain.
|
|
218
266
|
* @param {MemberExpression} node member expression
|
|
@@ -228,7 +276,10 @@ class HarmonyImportDependencyParserPlugin {
|
|
|
228
276
|
const expr = /** @type {Identifier} */ (expression);
|
|
229
277
|
if (
|
|
230
278
|
parser.isVariableDefined(expr.name) ||
|
|
231
|
-
|
|
279
|
+
markUsed(
|
|
280
|
+
/** @type {HarmonySettings | undefined} */
|
|
281
|
+
(parser.getTagData(expr.name, harmonySpecifierTag))
|
|
282
|
+
)
|
|
232
283
|
) {
|
|
233
284
|
return true;
|
|
234
285
|
}
|
|
@@ -267,6 +318,7 @@ class HarmonyImportDependencyParserPlugin {
|
|
|
267
318
|
);
|
|
268
319
|
sideEffectDep.loc = parser.getLocation(statement);
|
|
269
320
|
parser.state.module.addDependency(sideEffectDep);
|
|
321
|
+
lastSideEffectDependency = sideEffectDep;
|
|
270
322
|
return true;
|
|
271
323
|
});
|
|
272
324
|
parser.hooks.importSpecifier.tap(
|
|
@@ -274,18 +326,20 @@ class HarmonyImportDependencyParserPlugin {
|
|
|
274
326
|
(statement, source, id, name) => {
|
|
275
327
|
const ids = id === null ? [] : [id];
|
|
276
328
|
const phase = getImportPhase(parser, statement);
|
|
277
|
-
|
|
329
|
+
const settings = /** @type {HarmonySettings} */ ({
|
|
278
330
|
name,
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
331
|
+
source,
|
|
332
|
+
ids,
|
|
333
|
+
sourceOrder: parser.state.lastHarmonyImportOrder,
|
|
334
|
+
attributes: getImportAttributes(statement),
|
|
335
|
+
phase,
|
|
336
|
+
used: false,
|
|
337
|
+
dependency: lastSideEffectDependency
|
|
338
|
+
});
|
|
339
|
+
parser.tagVariable(name, harmonySpecifierTag, settings);
|
|
340
|
+
// The statement's dependency carries them, so a never-referenced
|
|
341
|
+
// one still resolves; a namespace import names no export.
|
|
342
|
+
if (ids.length !== 0) declaredSpecifiers.push(settings);
|
|
289
343
|
return true;
|
|
290
344
|
}
|
|
291
345
|
);
|
|
@@ -328,16 +382,28 @@ class HarmonyImportDependencyParserPlugin {
|
|
|
328
382
|
nameInfo &&
|
|
329
383
|
nameInfo.rootInfo instanceof VariableInfo &&
|
|
330
384
|
nameInfo.rootInfo.name &&
|
|
331
|
-
|
|
385
|
+
markUsed(
|
|
386
|
+
/** @type {HarmonySettings | undefined} */
|
|
387
|
+
(parser.getTagData(nameInfo.rootInfo.name, harmonySpecifierTag))
|
|
388
|
+
)
|
|
332
389
|
) {
|
|
333
390
|
return true;
|
|
334
391
|
}
|
|
335
392
|
}
|
|
336
393
|
);
|
|
394
|
+
// Concatenation would turn the binding into a plain local, losing the
|
|
395
|
+
// getter-only property whose write throws.
|
|
396
|
+
parser.hooks.assign.for(harmonySpecifierTag).tap(PLUGIN_NAME, () => {
|
|
397
|
+
markUsed(/** @type {HarmonySettings} */ (parser.currentTagData));
|
|
398
|
+
/** @type {JavascriptModuleBuildInfo} */
|
|
399
|
+
(parser.state.module.buildInfo).moduleConcatenationBailout =
|
|
400
|
+
"assignment to an imported binding";
|
|
401
|
+
});
|
|
337
402
|
parser.hooks.expression
|
|
338
403
|
.for(harmonySpecifierTag)
|
|
339
404
|
.tap(PLUGIN_NAME, (expr) => {
|
|
340
405
|
const settings = /** @type {HarmonySettings} */ (parser.currentTagData);
|
|
406
|
+
settings.used = true;
|
|
341
407
|
|
|
342
408
|
const dep = new HarmonyImportSpecifierDependency(
|
|
343
409
|
settings.source,
|
|
@@ -376,6 +442,7 @@ class HarmonyImportDependencyParserPlugin {
|
|
|
376
442
|
const settings =
|
|
377
443
|
/** @type {HarmonySettings} */
|
|
378
444
|
(parser.currentTagData);
|
|
445
|
+
settings.used = true;
|
|
379
446
|
const nonOptionalMembers = getNonOptionalPart(
|
|
380
447
|
members,
|
|
381
448
|
membersOptionals
|
|
@@ -431,6 +498,7 @@ class HarmonyImportDependencyParserPlugin {
|
|
|
431
498
|
const settings = /** @type {HarmonySettings} */ (
|
|
432
499
|
parser.currentTagData
|
|
433
500
|
);
|
|
501
|
+
settings.used = true;
|
|
434
502
|
const nonOptionalMembers = getNonOptionalPart(
|
|
435
503
|
members,
|
|
436
504
|
membersOptionals
|
|
@@ -491,6 +559,7 @@ class HarmonyImportDependencyParserPlugin {
|
|
|
491
559
|
.for(harmonySpecifierTag)
|
|
492
560
|
.tap(PLUGIN_NAME, (expression, members) => {
|
|
493
561
|
const settings = /** @type {HarmonySettings} */ (parser.currentTagData);
|
|
562
|
+
settings.used = true;
|
|
494
563
|
if (!ImportPhaseUtils.isDefer(settings.phase)) return;
|
|
495
564
|
if (expression.operator !== "=") return;
|
|
496
565
|
if (members.length !== 1) return;
|
|
@@ -13,6 +13,8 @@ const makeSerializable = require("../util/makeSerializable");
|
|
|
13
13
|
const HarmonyImportDependency = require("./HarmonyImportDependency");
|
|
14
14
|
const { ImportPhaseUtils } = require("./ImportPhase");
|
|
15
15
|
|
|
16
|
+
const { ExportPresenceModes } = HarmonyImportDependency;
|
|
17
|
+
|
|
16
18
|
/** @import { ReplaceSource } from "webpack-sources" */
|
|
17
19
|
/** @import { GetConditionFn, LazyUntil } from "../Dependency" */
|
|
18
20
|
/** @import { DependencyTemplateContext } from "../DependencyTemplate" */
|
|
@@ -20,6 +22,19 @@ const { ImportPhaseUtils } = require("./ImportPhase");
|
|
|
20
22
|
/** @import { ConnectionState } from "../ModuleGraphConnection" */
|
|
21
23
|
/** @import { ImportAttributes } from "../javascript/JavascriptParser" */
|
|
22
24
|
/** @import { ImportPhaseType } from "./ImportPhase" */
|
|
25
|
+
/** @import { Ids, ExportPresenceMode } from "./HarmonyImportDependency" */
|
|
26
|
+
/** @import { JavascriptModuleBuildMeta } from "../javascript/JavascriptModule" */
|
|
27
|
+
/** @import WebpackError from "../errors/WebpackError" */
|
|
28
|
+
/** @import { ObjectSerializerContext, ObjectDeserializerContext } from "../serialization/ObjectMiddleware" */
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Specifiers the module declares but never reads, with the presence level to
|
|
32
|
+
* report them at. Allocated only for a module that has one, so the common
|
|
33
|
+
* import pays a single empty slot.
|
|
34
|
+
* @typedef {object} UnusedSpecifiers
|
|
35
|
+
* @property {ExportPresenceMode} exportPresenceMode
|
|
36
|
+
* @property {[Ids, string][]} specifiers
|
|
37
|
+
*/
|
|
23
38
|
|
|
24
39
|
class HarmonyImportSideEffectDependency extends HarmonyImportDependency {
|
|
25
40
|
/**
|
|
@@ -31,12 +46,111 @@ class HarmonyImportSideEffectDependency extends HarmonyImportDependency {
|
|
|
31
46
|
*/
|
|
32
47
|
constructor(request, sourceOrder, phase, attributes) {
|
|
33
48
|
super(request, sourceOrder, phase, attributes);
|
|
49
|
+
/** @type {UnusedSpecifiers | undefined} */
|
|
50
|
+
this.unusedSpecifiers = undefined;
|
|
34
51
|
}
|
|
35
52
|
|
|
36
53
|
get type() {
|
|
37
54
|
return "harmony side effect evaluation";
|
|
38
55
|
}
|
|
39
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Returns warnings.
|
|
59
|
+
* @param {ModuleGraph} moduleGraph module graph
|
|
60
|
+
* @returns {WebpackError[] | null | undefined} warnings
|
|
61
|
+
*/
|
|
62
|
+
getWarnings(moduleGraph) {
|
|
63
|
+
if (
|
|
64
|
+
this._getEffectiveExportPresenceLevel(moduleGraph) !==
|
|
65
|
+
ExportPresenceModes.WARN
|
|
66
|
+
) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
return this._getUnusedSpecifierErrors(moduleGraph);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get effective export presence level.
|
|
74
|
+
* @param {ModuleGraph} moduleGraph module graph
|
|
75
|
+
* @returns {ExportPresenceMode} effective mode
|
|
76
|
+
*/
|
|
77
|
+
_getEffectiveExportPresenceLevel(moduleGraph) {
|
|
78
|
+
if (this.unusedSpecifiers === undefined) return ExportPresenceModes.NONE;
|
|
79
|
+
const mode = this.unusedSpecifiers.exportPresenceMode;
|
|
80
|
+
if (mode !== ExportPresenceModes.AUTO) return mode;
|
|
81
|
+
const buildMeta =
|
|
82
|
+
/** @type {JavascriptModuleBuildMeta} */
|
|
83
|
+
(
|
|
84
|
+
/** @type {Module} */
|
|
85
|
+
(moduleGraph.getParentModule(this)).buildMeta
|
|
86
|
+
);
|
|
87
|
+
return buildMeta.strictHarmonyModule
|
|
88
|
+
? ExportPresenceModes.ERROR
|
|
89
|
+
: ExportPresenceModes.WARN;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Returns errors.
|
|
94
|
+
* @param {ModuleGraph} moduleGraph module graph
|
|
95
|
+
* @returns {WebpackError[] | null | undefined} errors
|
|
96
|
+
*/
|
|
97
|
+
getErrors(moduleGraph) {
|
|
98
|
+
if (
|
|
99
|
+
this._getEffectiveExportPresenceLevel(moduleGraph) !==
|
|
100
|
+
ExportPresenceModes.ERROR
|
|
101
|
+
) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
return this._getUnusedSpecifierErrors(moduleGraph);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Linking errors for specifiers the module declares but never references.
|
|
109
|
+
* The spec resolves every import entry, so a name that does not exist is a
|
|
110
|
+
* link error whether or not the binding is read.
|
|
111
|
+
* @param {ModuleGraph} moduleGraph module graph
|
|
112
|
+
* @returns {WebpackError[] | undefined} errors
|
|
113
|
+
*/
|
|
114
|
+
_getUnusedSpecifierErrors(moduleGraph) {
|
|
115
|
+
// Both callers resolve the presence level first, which is NONE without
|
|
116
|
+
// specifiers, so this only runs when there are some.
|
|
117
|
+
const { specifiers } = /** @type {UnusedSpecifiers} */ (
|
|
118
|
+
this.unusedSpecifiers
|
|
119
|
+
);
|
|
120
|
+
/** @type {WebpackError[] | undefined} */
|
|
121
|
+
let errors;
|
|
122
|
+
for (const [ids, name] of specifiers) {
|
|
123
|
+
const linkingErrors = this.getLinkingErrors(
|
|
124
|
+
moduleGraph,
|
|
125
|
+
ids,
|
|
126
|
+
`(imported as '${name}')`
|
|
127
|
+
);
|
|
128
|
+
if (linkingErrors) {
|
|
129
|
+
if (errors === undefined) errors = linkingErrors;
|
|
130
|
+
else errors.push(...linkingErrors);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return errors;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Serializes this instance into the provided serializer context.
|
|
138
|
+
* @param {ObjectSerializerContext} context context
|
|
139
|
+
*/
|
|
140
|
+
serialize(context) {
|
|
141
|
+
context.write(this.unusedSpecifiers);
|
|
142
|
+
super.serialize(context);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Restores this instance from the provided deserializer context.
|
|
147
|
+
* @param {ObjectDeserializerContext} context context
|
|
148
|
+
*/
|
|
149
|
+
deserialize(context) {
|
|
150
|
+
this.unusedSpecifiers = context.read();
|
|
151
|
+
super.deserialize(context);
|
|
152
|
+
}
|
|
153
|
+
|
|
40
154
|
/**
|
|
41
155
|
* Returns the export name this dependency requests from its target module (lazy barrel optimization).
|
|
42
156
|
* @returns {string | true | null} export name, true for all exports, null for none
|
|
@@ -136,19 +136,13 @@ WorkerDependency.Template = class WorkerDependencyTemplate extends (
|
|
|
136
136
|
// and webpack itself can statically follow the worker. A resource hint doesn't
|
|
137
137
|
// block it: the `<link>` is emitted at chunk startup instead of wrapping the href,
|
|
138
138
|
// which would turn the specifier into a non-analyzable expression.
|
|
139
|
-
const analyzableSpecifier = runtimeTemplate.
|
|
140
|
-
|
|
139
|
+
const analyzableSpecifier = runtimeTemplate.getAnalyzableWorkerUrl(
|
|
140
|
+
dep.options.publicPath,
|
|
141
|
+
chunk,
|
|
142
|
+
templateContext.module,
|
|
141
143
|
chunkGraph,
|
|
142
|
-
|
|
143
|
-
)
|
|
144
|
-
? runtimeTemplate._getAnalyzableChunkSpecifier(
|
|
145
|
-
dep.options.publicPath,
|
|
146
|
-
chunk,
|
|
147
|
-
templateContext.module,
|
|
148
|
-
chunkGraph,
|
|
149
|
-
runtimeRequirements
|
|
150
|
-
)
|
|
151
|
-
: null;
|
|
144
|
+
runtimeRequirements
|
|
145
|
+
);
|
|
152
146
|
|
|
153
147
|
if (analyzableSpecifier !== null) {
|
|
154
148
|
// No `<link>` helper is asked for here: this form emits no call, and
|
|
@@ -140,17 +140,13 @@ WorkletDependency.Template = class WorkletDependencyTemplate extends (
|
|
|
140
140
|
// import.meta.url)` form (literal specifier, no runtime helpers) so other bundlers
|
|
141
141
|
// and webpack itself can statically follow the worklet. The worklet chunk links its
|
|
142
142
|
// splits via native `import`, so only the entry chunk is referenced.
|
|
143
|
-
const analyzableSpecifier =
|
|
144
|
-
|
|
145
|
-
entryChunk
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
chunkGraph,
|
|
151
|
-
runtimeRequirements
|
|
152
|
-
)
|
|
153
|
-
: null;
|
|
143
|
+
const analyzableSpecifier = runtimeTemplate.getAnalyzableWorkerUrl(
|
|
144
|
+
dep.options.publicPath,
|
|
145
|
+
entryChunk,
|
|
146
|
+
module,
|
|
147
|
+
chunkGraph,
|
|
148
|
+
runtimeRequirements
|
|
149
|
+
);
|
|
154
150
|
if (analyzableSpecifier !== null) {
|
|
155
151
|
source.replace(
|
|
156
152
|
dep.callRange[0],
|
|
@@ -120,15 +120,12 @@ class ModuleChunkLoadingPlugin {
|
|
|
120
120
|
if (!isEnabledForChunk(chunk)) return;
|
|
121
121
|
// A baked url carries where the chunk is, so neither is read for it.
|
|
122
122
|
// Asked as the runtime module asks, so the two always agree.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
) {
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
123
|
+
const baked = compilation.runtimeTemplate.analyzableChunkScriptUrls(
|
|
124
|
+
chunk,
|
|
125
|
+
chunkGraph,
|
|
126
|
+
set
|
|
127
|
+
);
|
|
128
|
+
if (baked !== null && baked.complete) return;
|
|
132
129
|
set.add(RuntimeGlobals.publicPath);
|
|
133
130
|
set.add(RuntimeGlobals.getChunkScriptFilename);
|
|
134
131
|
});
|
|
@@ -129,7 +129,7 @@ class ModuleChunkLoadingRuntimeModule extends RuntimeModule {
|
|
|
129
129
|
chunk.hasChildByOrder(chunkGraph, "preload", true, chunkHasJs);
|
|
130
130
|
// Under module output each hinted chunk is a known file, so the urls are written
|
|
131
131
|
// out and read by id rather than built from the chunk id at runtime.
|
|
132
|
-
const
|
|
132
|
+
const bakedChunkUrls =
|
|
133
133
|
withPrefetch || withPreload
|
|
134
134
|
? runtimeTemplate.analyzableChunkScriptUrls(
|
|
135
135
|
chunk,
|
|
@@ -138,6 +138,12 @@ class ModuleChunkLoadingRuntimeModule extends RuntimeModule {
|
|
|
138
138
|
this
|
|
139
139
|
)
|
|
140
140
|
: null;
|
|
141
|
+
// A hint is best-effort, so an id an incomplete map lacks is not worth a per-id
|
|
142
|
+
// fallback — only a complete map replaces the runtime name lookup.
|
|
143
|
+
const chunkUrls =
|
|
144
|
+
bakedChunkUrls !== null && bakedChunkUrls.complete
|
|
145
|
+
? bakedChunkUrls.urls
|
|
146
|
+
: null;
|
|
141
147
|
const conditionMap = chunkGraph.getChunkConditionMap(chunk, chunkHasJs);
|
|
142
148
|
const hasJsMatcher = compileBooleanMatcher(conditionMap);
|
|
143
149
|
const initialChunkIds = getInitialChunkIds(chunk, chunkGraph, chunkHasJs);
|
|
@@ -31,7 +31,7 @@ const preloadModuleType = require("../util/preloadModuleType");
|
|
|
31
31
|
const {
|
|
32
32
|
PUBLIC_PATH_AUTO: autoPlaceholder
|
|
33
33
|
} = require("../util/publicPathPlaceholder");
|
|
34
|
-
const
|
|
34
|
+
const { removeBOMFromResult } = require("../util/removeBOM");
|
|
35
35
|
const HtmlModule = require("./HtmlModule");
|
|
36
36
|
|
|
37
37
|
const getMimeTypes = memoize(() => require("../util/mimeTypes"));
|
|
@@ -222,6 +222,9 @@ const isIntegrityEnabled = (integrity) =>
|
|
|
222
222
|
// `\.html`/`\.css` request matchers for the synthetic `output.html` wrapper.
|
|
223
223
|
const HTML_REQUEST_RE = /\.html(\?|$)/i;
|
|
224
224
|
const CSS_REQUEST_RE = /\.css(\?|$)/i;
|
|
225
|
+
// The wrapper's own resource — the media type must end here, so that a
|
|
226
|
+
// `data:text/htmlx,` entry (JavaScript to `DataUriPlugin`) is not one.
|
|
227
|
+
const HTML_DATA_URL_RE = /^data:text\/html[,;]/i;
|
|
225
228
|
|
|
226
229
|
/**
|
|
227
230
|
* Requests an `output.html` page must load for an entry: its `dependOn`
|
|
@@ -714,28 +717,64 @@ class HtmlModulesPlugin {
|
|
|
714
717
|
// An inlined chunk file is dead weight once nothing else references
|
|
715
718
|
// it by URL (another page's tag, or the runtime's async chunk map);
|
|
716
719
|
// a content-hashed filename makes a substring hit a real reference.
|
|
720
|
+
/** @type {Set<string>} */
|
|
721
|
+
const deletionCandidates = new Set(inlinedFiles);
|
|
722
|
+
// A wrapper entry's chunk only rebuilds the page's own markup; the
|
|
723
|
+
// page loads the `<script src>` entries the parser split out of it.
|
|
724
|
+
for (const entrypoint of compilation.entrypoints.values()) {
|
|
725
|
+
const chunk = entrypoint.getEntrypointChunk();
|
|
726
|
+
if (!chunk) continue;
|
|
727
|
+
let wrapperOnly = false;
|
|
728
|
+
for (const entryModule of compilation.chunkGraph.getChunkEntryModulesIterable(
|
|
729
|
+
chunk
|
|
730
|
+
)) {
|
|
731
|
+
const { resource } = /** @type {import("../NormalModule")} */ (
|
|
732
|
+
entryModule
|
|
733
|
+
);
|
|
734
|
+
wrapperOnly =
|
|
735
|
+
typeof resource === "string" &&
|
|
736
|
+
HTML_DATA_URL_RE.test(resource);
|
|
737
|
+
if (!wrapperOnly) break;
|
|
738
|
+
}
|
|
739
|
+
if (!wrapperOnly) continue;
|
|
740
|
+
for (const file of chunk.files) deletionCandidates.add(file);
|
|
741
|
+
}
|
|
717
742
|
// Materialize each other asset's source once (it can be expensive to
|
|
718
|
-
// render) and test all
|
|
719
|
-
// re-materializing every asset per
|
|
720
|
-
if (
|
|
743
|
+
// render) and test all candidates against it, rather than
|
|
744
|
+
// re-materializing every asset per candidate.
|
|
745
|
+
if (deletionCandidates.size > 0) {
|
|
721
746
|
/** @type {Set<string>} */
|
|
722
747
|
const stillReferenced = new Set();
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
}
|
|
748
|
+
/** @type {string[]} */
|
|
749
|
+
const pending = [];
|
|
750
|
+
/**
|
|
751
|
+
* @param {string} name asset whose source may name a candidate
|
|
752
|
+
*/
|
|
753
|
+
const markReferenced = (name) => {
|
|
730
754
|
const source = compilation.assets[name].source();
|
|
731
|
-
if (typeof source !== "string")
|
|
732
|
-
for (const file of
|
|
755
|
+
if (typeof source !== "string") return;
|
|
756
|
+
for (const file of deletionCandidates) {
|
|
733
757
|
if (!stillReferenced.has(file) && source.includes(file)) {
|
|
734
758
|
stillReferenced.add(file);
|
|
759
|
+
pending.push(file);
|
|
735
760
|
}
|
|
736
761
|
}
|
|
762
|
+
};
|
|
763
|
+
for (const name of Object.keys(compilation.assets)) {
|
|
764
|
+
if (
|
|
765
|
+
deletionCandidates.has(name) ||
|
|
766
|
+
stillReferenced.size === deletionCandidates.size
|
|
767
|
+
) {
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
markReferenced(name);
|
|
771
|
+
}
|
|
772
|
+
// A kept candidate can reference another through its async chunk
|
|
773
|
+
// map, so follow each until nothing new is kept.
|
|
774
|
+
while (pending.length > 0) {
|
|
775
|
+
markReferenced(/** @type {string} */ (pending.pop()));
|
|
737
776
|
}
|
|
738
|
-
for (const file of
|
|
777
|
+
for (const file of deletionCandidates) {
|
|
739
778
|
if (!stillReferenced.has(file)) compilation.deleteAsset(file);
|
|
740
779
|
}
|
|
741
780
|
}
|
|
@@ -896,14 +935,14 @@ class HtmlModulesPlugin {
|
|
|
896
935
|
PLUGIN_NAME,
|
|
897
936
|
(result, module) => {
|
|
898
937
|
if (module.type === HTML_MODULE_TYPE) {
|
|
899
|
-
const [source, ...rest] = result;
|
|
938
|
+
const [source, ...rest] = removeBOMFromResult(result);
|
|
900
939
|
// `applyTemplate` is a no-op unless `module.parser.html.template`
|
|
901
940
|
// is set. Running it here (where the returned source becomes the
|
|
902
941
|
// module's stored source) keeps the parser's dependency offsets
|
|
903
942
|
// and the generator's render base in sync.
|
|
904
943
|
const parser = /** @type {HtmlParser} */ (module.parser);
|
|
905
944
|
|
|
906
|
-
return [parser.applyTemplate(
|
|
945
|
+
return [parser.applyTemplate(source, module), ...rest];
|
|
907
946
|
}
|
|
908
947
|
|
|
909
948
|
return result;
|