webpack 5.107.0 → 5.107.1
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/lib/BannerPlugin.js +3 -4
- package/lib/Chunk.js +21 -25
- package/lib/ChunkGroup.js +57 -15
- package/lib/Compilation.js +33 -11
- package/lib/EvalSourceMapDevToolPlugin.js +0 -1
- package/lib/ExportsInfo.js +30 -34
- package/lib/ExternalModule.js +15 -11
- package/lib/ExternalModuleFactoryPlugin.js +2 -1
- package/lib/Module.js +1 -1
- package/lib/ModuleNotFoundError.js +10 -0
- package/lib/ModuleSourceTypeConstants.js +24 -22
- package/lib/NormalModule.js +106 -46
- package/lib/NormalModuleFactory.js +38 -26
- package/lib/RuntimePlugin.js +1 -1
- package/lib/SourceMapDevToolPlugin.js +250 -49
- package/lib/Template.js +1 -1
- package/lib/TemplatedPathPlugin.js +22 -4
- package/lib/asset/AssetBytesGenerator.js +6 -6
- package/lib/asset/AssetGenerator.js +14 -14
- package/lib/asset/AssetModulesPlugin.js +3 -7
- package/lib/asset/AssetSourceGenerator.js +6 -6
- package/lib/css/CssModulesPlugin.js +2 -2
- package/lib/dependencies/CommonJsImportsParserPlugin.js +108 -1
- package/lib/dependencies/CssUrlDependency.js +3 -2
- package/lib/dependencies/HarmonyDetectionParserPlugin.js +21 -1
- package/lib/dependencies/HtmlScriptSrcDependency.js +264 -25
- package/lib/dependencies/HtmlSourceDependency.js +3 -2
- package/lib/html/HtmlModulesPlugin.js +1 -5
- package/lib/html/walkHtmlTokens.js +641 -125
- package/lib/index.js +2 -0
- package/lib/javascript/JavascriptModulesPlugin.js +2 -2
- package/lib/optimize/SideEffectsFlagPlugin.js +1 -2
- package/lib/optimize/SplitChunksPlugin.js +4 -4
- package/lib/runtime/AutoPublicPathRuntimeModule.js +3 -3
- package/lib/runtime/GetChunkFilenameRuntimeModule.js +5 -5
- package/lib/sharing/ConsumeSharedPlugin.js +2 -8
- package/lib/sharing/ProvideSharedPlugin.js +4 -4
- package/lib/wasm-async/AsyncWebAssemblyModulesPlugin.js +1 -2
- package/package.json +3 -3
- package/schemas/WebpackOptions.check.js +1 -1
- package/schemas/WebpackOptions.json +11 -9
- package/schemas/plugins/container/ContainerReferencePlugin.check.js +1 -1
- package/schemas/plugins/container/ContainerReferencePlugin.json +1 -0
- package/schemas/plugins/container/ExternalsType.check.js +1 -1
- package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
- package/schemas/plugins/container/ModuleFederationPlugin.json +1 -0
- package/types.d.ts +355 -144
|
@@ -9,11 +9,11 @@ const { RawSource } = require("webpack-sources");
|
|
|
9
9
|
const ConcatenationScope = require("../ConcatenationScope");
|
|
10
10
|
const Generator = require("../Generator");
|
|
11
11
|
const {
|
|
12
|
+
ASSET_URL_TYPE,
|
|
13
|
+
ASSET_URL_TYPES,
|
|
12
14
|
CSS_TYPE,
|
|
13
|
-
CSS_URL_TYPE,
|
|
14
|
-
CSS_URL_TYPES,
|
|
15
15
|
HTML_TYPE,
|
|
16
|
-
|
|
16
|
+
JAVASCRIPT_AND_ASSET_URL_TYPES,
|
|
17
17
|
JAVASCRIPT_TYPE,
|
|
18
18
|
JAVASCRIPT_TYPES,
|
|
19
19
|
NO_TYPES
|
|
@@ -79,7 +79,7 @@ class AssetSourceGenerator extends Generator {
|
|
|
79
79
|
}
|
|
80
80
|
return new RawSource(sourceContent);
|
|
81
81
|
}
|
|
82
|
-
case
|
|
82
|
+
case ASSET_URL_TYPE: {
|
|
83
83
|
if (!originalSource) {
|
|
84
84
|
return null;
|
|
85
85
|
}
|
|
@@ -150,9 +150,9 @@ class AssetSourceGenerator extends Generator {
|
|
|
150
150
|
sourceTypes.has(JAVASCRIPT_TYPE) &&
|
|
151
151
|
(sourceTypes.has(CSS_TYPE) || sourceTypes.has(HTML_TYPE))
|
|
152
152
|
) {
|
|
153
|
-
return
|
|
153
|
+
return JAVASCRIPT_AND_ASSET_URL_TYPES;
|
|
154
154
|
} else if (sourceTypes.has(CSS_TYPE) || sourceTypes.has(HTML_TYPE)) {
|
|
155
|
-
return
|
|
155
|
+
return ASSET_URL_TYPES;
|
|
156
156
|
}
|
|
157
157
|
return JAVASCRIPT_TYPES;
|
|
158
158
|
}
|
|
@@ -58,7 +58,7 @@ const CssParser = require("./CssParser");
|
|
|
58
58
|
/** @typedef {import("../Module").BuildInfo} BuildInfo */
|
|
59
59
|
/** @typedef {import("../Module").RuntimeRequirements} RuntimeRequirements */
|
|
60
60
|
/** @typedef {import("../Template").RuntimeTemplate} RuntimeTemplate */
|
|
61
|
-
/** @typedef {import("../
|
|
61
|
+
/** @typedef {import("../Chunk").ChunkFilenameTemplate} ChunkFilenameTemplate */
|
|
62
62
|
/** @typedef {import("../util/Hash")} Hash */
|
|
63
63
|
/** @typedef {import("../Module").BuildMeta} BuildMeta */
|
|
64
64
|
|
|
@@ -1152,7 +1152,7 @@ class CssModulesPlugin {
|
|
|
1152
1152
|
* Gets chunk filename template.
|
|
1153
1153
|
* @param {Chunk} chunk chunk
|
|
1154
1154
|
* @param {OutputOptions} outputOptions output options
|
|
1155
|
-
* @returns {
|
|
1155
|
+
* @returns {ChunkFilenameTemplate} used filename template
|
|
1156
1156
|
*/
|
|
1157
1157
|
static getChunkFilenameTemplate(chunk, outputOptions) {
|
|
1158
1158
|
if (chunk.cssFilenameTemplate) {
|
|
@@ -48,6 +48,22 @@ const RequireResolveHeaderDependency = require("./RequireResolveHeaderDependency
|
|
|
48
48
|
* @property {string} context
|
|
49
49
|
*/
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Per-`const NAME = require(LITERAL)` binding state used to forward
|
|
53
|
+
* member-access references on `NAME` to the `CommonJsRequireDependency`
|
|
54
|
+
* created for the `require()` call.
|
|
55
|
+
* @typedef {object} RequireBindingData
|
|
56
|
+
* @property {RawReferencedExports} referencedExports mutable list shared with the dependency; pushed to as `NAME.x.y` accesses are walked
|
|
57
|
+
* @property {InstanceType<typeof import("./CommonJsRequireDependency")> | null} dep dependency for the `require()` call (assigned during walk)
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
/** @type {WeakMap<CallExpression, RequireBindingData>} */
|
|
61
|
+
const requireBindingData = new WeakMap();
|
|
62
|
+
|
|
63
|
+
const REQUIRE_BINDING_TAG = Symbol(
|
|
64
|
+
"CommonJsImportsParserPlugin require binding"
|
|
65
|
+
);
|
|
66
|
+
|
|
51
67
|
const PLUGIN_NAME = "CommonJsImportsParserPlugin";
|
|
52
68
|
|
|
53
69
|
/**
|
|
@@ -152,10 +168,18 @@ const createRequireCallHandler = (parser, options, getContext) => {
|
|
|
152
168
|
*/
|
|
153
169
|
const processRequireItem = (expr, param) => {
|
|
154
170
|
if (param.isString()) {
|
|
155
|
-
|
|
171
|
+
let referencedExports = getRequireReferencedExportsFromDestructuring(
|
|
156
172
|
parser,
|
|
157
173
|
expr
|
|
158
174
|
);
|
|
175
|
+
const binding = requireBindingData.get(
|
|
176
|
+
/** @type {CallExpression} */ (expr)
|
|
177
|
+
);
|
|
178
|
+
if (binding && !referencedExports) {
|
|
179
|
+
// `const NAME = require(LITERAL)` — let later member-access walks
|
|
180
|
+
// on `NAME` populate the dependency's referenced exports.
|
|
181
|
+
referencedExports = binding.referencedExports;
|
|
182
|
+
}
|
|
159
183
|
const dep = new CommonJsRequireDependency(
|
|
160
184
|
/** @type {string} */ (param.string),
|
|
161
185
|
/** @type {Range} */ (param.range),
|
|
@@ -163,6 +187,7 @@ const createRequireCallHandler = (parser, options, getContext) => {
|
|
|
163
187
|
referencedExports,
|
|
164
188
|
/** @type {Range} */ (expr.range)
|
|
165
189
|
);
|
|
190
|
+
if (binding) binding.dep = dep;
|
|
166
191
|
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
|
167
192
|
dep.optional = Boolean(parser.scope.inTry);
|
|
168
193
|
parser.state.current.addDependency(dep);
|
|
@@ -662,6 +687,88 @@ class CommonJsImportsParserPlugin {
|
|
|
662
687
|
.tap(PLUGIN_NAME, callChainHandler);
|
|
663
688
|
// #endregion
|
|
664
689
|
|
|
690
|
+
// #region Require bound to a const variable
|
|
691
|
+
// Track `const NAME = require(LITERAL)` so that static member accesses on
|
|
692
|
+
// `NAME` (e.g. `NAME.foo`, `NAME.foo()`) are forwarded to the same
|
|
693
|
+
// `CommonJsRequireDependency` as referenced exports — enabling tree
|
|
694
|
+
// shaking of CommonJS modules that are imported into a named binding
|
|
695
|
+
// rather than destructured.
|
|
696
|
+
parser.hooks.preDeclarator.tap(PLUGIN_NAME, (declarator, statement) => {
|
|
697
|
+
if (statement.kind !== "const") return;
|
|
698
|
+
if (declarator.id.type !== "Identifier") return;
|
|
699
|
+
if (!declarator.init || declarator.init.type !== "CallExpression") {
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
const init = declarator.init;
|
|
703
|
+
if (
|
|
704
|
+
init.callee.type !== "Identifier" ||
|
|
705
|
+
init.callee.name !== "require" ||
|
|
706
|
+
init.arguments.length !== 1
|
|
707
|
+
) {
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
const arg = init.arguments[0];
|
|
711
|
+
if (arg.type !== "Literal" || typeof arg.value !== "string") return;
|
|
712
|
+
// Only attach binding state when `require` resolves to the free
|
|
713
|
+
// `require` (i.e. it isn't shadowed in the current scope).
|
|
714
|
+
const requireInfo = parser.getFreeInfoFromVariable("require");
|
|
715
|
+
if (!requireInfo || requireInfo.name !== "require") return;
|
|
716
|
+
/** @type {RequireBindingData} */
|
|
717
|
+
const binding = {
|
|
718
|
+
referencedExports: [],
|
|
719
|
+
dep: null
|
|
720
|
+
};
|
|
721
|
+
requireBindingData.set(init, binding);
|
|
722
|
+
parser.tagVariable(declarator.id.name, REQUIRE_BINDING_TAG, binding);
|
|
723
|
+
return true;
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
parser.hooks.expression.for(REQUIRE_BINDING_TAG).tap(PLUGIN_NAME, () => {
|
|
727
|
+
const binding =
|
|
728
|
+
/** @type {RequireBindingData} */
|
|
729
|
+
(parser.currentTagData);
|
|
730
|
+
if (binding && binding.dep) {
|
|
731
|
+
// `NAME` is read as a value (not as the object of a static member
|
|
732
|
+
// chain), so we have to assume the whole exports object is used.
|
|
733
|
+
binding.dep.referencedExports = null;
|
|
734
|
+
}
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
parser.hooks.expressionMemberChain
|
|
738
|
+
.for(REQUIRE_BINDING_TAG)
|
|
739
|
+
.tap(PLUGIN_NAME, (_expr, members) => {
|
|
740
|
+
const binding =
|
|
741
|
+
/** @type {RequireBindingData} */
|
|
742
|
+
(parser.currentTagData);
|
|
743
|
+
if (binding && binding.dep && binding.dep.referencedExports) {
|
|
744
|
+
binding.dep.referencedExports.push(members);
|
|
745
|
+
}
|
|
746
|
+
// Returning truthy suppresses the parser's fallback chain (which
|
|
747
|
+
// would otherwise walk `NAME` as a bare expression and trigger our
|
|
748
|
+
// `expression` hook above, marking the whole namespace as used).
|
|
749
|
+
return true;
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
parser.hooks.callMemberChain
|
|
753
|
+
.for(REQUIRE_BINDING_TAG)
|
|
754
|
+
.tap(PLUGIN_NAME, (expr, members) => {
|
|
755
|
+
const binding =
|
|
756
|
+
/** @type {RequireBindingData} */
|
|
757
|
+
(parser.currentTagData);
|
|
758
|
+
if (binding && binding.dep && binding.dep.referencedExports) {
|
|
759
|
+
if (members.length === 0) {
|
|
760
|
+
// `NAME(...)` — calling the require result directly; the
|
|
761
|
+
// whole exports object is observable.
|
|
762
|
+
binding.dep.referencedExports = null;
|
|
763
|
+
} else {
|
|
764
|
+
binding.dep.referencedExports.push(members);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
if (expr.arguments) parser.walkExpressions(expr.arguments);
|
|
768
|
+
return true;
|
|
769
|
+
});
|
|
770
|
+
// #endregion
|
|
771
|
+
|
|
665
772
|
// #region Require.resolve
|
|
666
773
|
/**
|
|
667
774
|
* Processes the provided expr.
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
"use strict";
|
|
7
7
|
|
|
8
|
+
const { ASSET_URL_TYPE } = require("../ModuleSourceTypeConstants");
|
|
8
9
|
const RawDataUrlModule = require("../asset/RawDataUrlModule");
|
|
9
10
|
const makeSerializable = require("../util/makeSerializable");
|
|
10
11
|
const memoize = require("../util/memoize");
|
|
@@ -208,8 +209,8 @@ CssUrlDependency.Template = class CssUrlDependencyTemplate extends (
|
|
|
208
209
|
const data = codeGen.data;
|
|
209
210
|
if (!data) return "data:,";
|
|
210
211
|
const url = data.get("url");
|
|
211
|
-
if (!url || !url[
|
|
212
|
-
return url[
|
|
212
|
+
if (!url || !url[ASSET_URL_TYPE]) return "data:,";
|
|
213
|
+
return url[ASSET_URL_TYPE];
|
|
213
214
|
}
|
|
214
215
|
};
|
|
215
216
|
|
|
@@ -92,6 +92,19 @@ module.exports = class HarmonyDetectionParserPlugin {
|
|
|
92
92
|
}
|
|
93
93
|
};
|
|
94
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Walks call arguments so import bindings used inside callbacks are
|
|
97
|
+
* still tracked, then skips default AMD/CommonJS handling.
|
|
98
|
+
* @param {import("estree").CallExpression} expr call expression
|
|
99
|
+
* @returns {boolean | undefined} true if in harmony
|
|
100
|
+
*/
|
|
101
|
+
const walkArgumentsAndSkipInHarmony = (expr) => {
|
|
102
|
+
if (HarmonyExports.isEnabled(parser.state)) {
|
|
103
|
+
if (expr.arguments) parser.walkExpressions(expr.arguments);
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
95
108
|
const nonHarmonyIdentifiers = ["define", "exports"];
|
|
96
109
|
for (const identifier of nonHarmonyIdentifiers) {
|
|
97
110
|
parser.hooks.evaluateTypeof
|
|
@@ -100,7 +113,14 @@ module.exports = class HarmonyDetectionParserPlugin {
|
|
|
100
113
|
parser.hooks.typeof.for(identifier).tap(PLUGIN_NAME, skipInHarmony);
|
|
101
114
|
parser.hooks.evaluate.for(identifier).tap(PLUGIN_NAME, nullInHarmony);
|
|
102
115
|
parser.hooks.expression.for(identifier).tap(PLUGIN_NAME, skipInHarmony);
|
|
103
|
-
parser.hooks.call
|
|
116
|
+
parser.hooks.call
|
|
117
|
+
.for(identifier)
|
|
118
|
+
.tap(
|
|
119
|
+
PLUGIN_NAME,
|
|
120
|
+
identifier === "define"
|
|
121
|
+
? walkArgumentsAndSkipInHarmony
|
|
122
|
+
: skipInHarmony
|
|
123
|
+
);
|
|
104
124
|
}
|
|
105
125
|
}
|
|
106
126
|
};
|
|
@@ -4,12 +4,18 @@
|
|
|
4
4
|
|
|
5
5
|
"use strict";
|
|
6
6
|
|
|
7
|
+
const {
|
|
8
|
+
CSS_IMPORT_TYPE,
|
|
9
|
+
CSS_TYPE,
|
|
10
|
+
JAVASCRIPT_TYPE
|
|
11
|
+
} = require("../ModuleSourceTypeConstants");
|
|
7
12
|
const makeSerializable = require("../util/makeSerializable");
|
|
8
13
|
const CssUrlDependency = require("./CssUrlDependency");
|
|
9
14
|
const ModuleDependency = require("./ModuleDependency");
|
|
10
15
|
|
|
11
16
|
/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
|
|
12
17
|
/** @typedef {import("../Chunk")} Chunk */
|
|
18
|
+
/** @typedef {import("../ChunkGraph")} ChunkGraph */
|
|
13
19
|
/** @typedef {import("../Dependency")} Dependency */
|
|
14
20
|
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
|
|
15
21
|
/** @typedef {import("../Entrypoint")} Entrypoint */
|
|
@@ -184,6 +190,144 @@ const getEntrypointChunksInLoadOrder = (entrypoint) => {
|
|
|
184
190
|
return ordered;
|
|
185
191
|
};
|
|
186
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Whether webpack will emit a `.js` file for this chunk that must be
|
|
195
|
+
* loaded with a `<script>` tag. Covers three independent reasons a
|
|
196
|
+
* chunk needs JS output: it owns one or more JS-source-type modules;
|
|
197
|
+
* it has entry modules whose source types include JavaScript (entry
|
|
198
|
+
* modules don't show up in `getChunkModulesIterableBySourceType` until
|
|
199
|
+
* they're connected as regular modules — this is why
|
|
200
|
+
* `JavascriptModulesPlugin#_chunkHasJs` checks them separately); or it
|
|
201
|
+
* is a runtime chunk — `chunk.hasRuntime()` — which produces a `.js`
|
|
202
|
+
* file holding the webpack runtime, but its `RuntimeModule`s live in
|
|
203
|
+
* a separate `runtimeModules` set and are *not* surfaced via
|
|
204
|
+
* `getChunkModulesIterableBySourceType`. Missing the runtime case
|
|
205
|
+
* would cause a `runtimeChunk`-split chunk to fall out of the
|
|
206
|
+
* `<script>` list and re-emerge after the chunks that depend on it,
|
|
207
|
+
* producing `__webpack_require__ is not defined` at load time.
|
|
208
|
+
* @param {Chunk} chunk chunk
|
|
209
|
+
* @param {ChunkGraph} chunkGraph chunk graph
|
|
210
|
+
* @returns {boolean} true if the chunk emits a `.js` file
|
|
211
|
+
*/
|
|
212
|
+
const chunkHasJs = (chunk, chunkGraph) => {
|
|
213
|
+
if (chunk.hasRuntime()) return true;
|
|
214
|
+
if (chunkGraph.getNumberOfEntryModules(chunk) > 0) {
|
|
215
|
+
for (const module of chunkGraph.getChunkEntryModulesIterable(chunk)) {
|
|
216
|
+
if (chunkGraph.getModuleSourceTypes(module).has(JAVASCRIPT_TYPE)) {
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return Boolean(
|
|
222
|
+
chunkGraph.getChunkModulesIterableBySourceType(chunk, JAVASCRIPT_TYPE)
|
|
223
|
+
);
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Whether webpack will emit a `.css` file for this chunk that must be
|
|
228
|
+
* loaded with a `<link rel="stylesheet">` tag. Matches
|
|
229
|
+
* `CssModulesPlugin.chunkHasCss` exactly — both regular CSS modules
|
|
230
|
+
* and pure `@import` placeholder modules count, since the latter
|
|
231
|
+
* still contribute a `.css` asset to the chunk.
|
|
232
|
+
* @param {Chunk} chunk chunk
|
|
233
|
+
* @param {ChunkGraph} chunkGraph chunk graph
|
|
234
|
+
* @returns {boolean} true if the chunk emits a `.css` file
|
|
235
|
+
*/
|
|
236
|
+
const chunkHasCss = (chunk, chunkGraph) =>
|
|
237
|
+
Boolean(chunkGraph.getChunkModulesIterableBySourceType(chunk, CSS_TYPE)) ||
|
|
238
|
+
Boolean(
|
|
239
|
+
chunkGraph.getChunkModulesIterableBySourceType(chunk, CSS_IMPORT_TYPE)
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Compare two chunks for a deterministic tie-break in CSS link ordering.
|
|
244
|
+
* `chunk.name` and `chunk.id` are both stable strings (when present);
|
|
245
|
+
* one of them is set for every chunk webpack emits. We can't rely on
|
|
246
|
+
* `Array.prototype.sort` being stable — webpack still supports Node
|
|
247
|
+
* 10.13 where V8's sort is not guaranteed stable for arrays larger
|
|
248
|
+
* than ten elements — so any time `firstCssModulePostOrderIndex`
|
|
249
|
+
* returns the same value for two chunks (most commonly when several
|
|
250
|
+
* chunks have no reachable CSS module in the entrypoint's dependency
|
|
251
|
+
* walk and all map to `Infinity`) this comparator picks the canonical
|
|
252
|
+
* order.
|
|
253
|
+
* @param {Chunk} a first chunk
|
|
254
|
+
* @param {Chunk} b second chunk
|
|
255
|
+
* @returns {-1 | 0 | 1} ordering
|
|
256
|
+
*/
|
|
257
|
+
const compareChunksForCssTieBreak = (a, b) => {
|
|
258
|
+
const an = `${a.name || ""} ${a.id === null || a.id === undefined ? "" : a.id}`;
|
|
259
|
+
const bn = `${b.name || ""} ${b.id === null || b.id === undefined ? "" : b.id}`;
|
|
260
|
+
if (an < bn) return -1;
|
|
261
|
+
if (an > bn) return 1;
|
|
262
|
+
return 0;
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Smallest post-order index among the CSS modules of a chunk, taken
|
|
267
|
+
* from the entrypoint's view of the dependency graph. Used to sort
|
|
268
|
+
* sibling CSS chunks so they appear in source import order in the
|
|
269
|
+
* extracted HTML — `entrypoint.chunks` itself does not give that
|
|
270
|
+
* ordering for arbitrary splitChunks layouts. Considers both
|
|
271
|
+
* `CSS_TYPE` and `CSS_IMPORT_TYPE` modules so a chunk made up
|
|
272
|
+
* exclusively of `@import` placeholder modules (e.g. when splitChunks
|
|
273
|
+
* separates them from their target CSS) still sorts by its true
|
|
274
|
+
* source position rather than collapsing to `Infinity` and relying on
|
|
275
|
+
* the chunk-name tie-breaker.
|
|
276
|
+
* @param {Chunk} chunk chunk
|
|
277
|
+
* @param {Entrypoint} entrypoint entrypoint the chunk belongs to
|
|
278
|
+
* @param {ChunkGraph} chunkGraph chunk graph
|
|
279
|
+
* @returns {number} the lowest post-order index of any CSS or
|
|
280
|
+
* CSS-import module in the chunk, or `Number.POSITIVE_INFINITY` when
|
|
281
|
+
* no such module has a defined index (e.g. for a module the
|
|
282
|
+
* entrypoint never reached on its own dependency walk — runtime-only
|
|
283
|
+
* modules, modules reached via `dependOn`, etc.) so such chunks sort
|
|
284
|
+
* last among CSS chunks
|
|
285
|
+
*/
|
|
286
|
+
const firstCssModulePostOrderIndex = (chunk, entrypoint, chunkGraph) => {
|
|
287
|
+
let min = Number.POSITIVE_INFINITY;
|
|
288
|
+
for (const sourceType of [CSS_TYPE, CSS_IMPORT_TYPE]) {
|
|
289
|
+
const modules = chunkGraph.getChunkModulesIterableBySourceType(
|
|
290
|
+
chunk,
|
|
291
|
+
sourceType
|
|
292
|
+
);
|
|
293
|
+
if (!modules) continue;
|
|
294
|
+
for (const module of modules) {
|
|
295
|
+
const idx = entrypoint.getModulePostOrderIndex(module);
|
|
296
|
+
if (idx !== undefined && idx < min) min = idx;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return min;
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
const COPYABLE_LINK_ATTRS = ["nonce", "crossorigin", "referrerpolicy"];
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Build a fresh `<link rel="stylesheet" href="…">` for a CSS chunk that
|
|
306
|
+
* was pulled in by a `<script src>` entry — the originating tag was a
|
|
307
|
+
* `<script>`, but the chunk is CSS so cloning the script tag verbatim
|
|
308
|
+
* would produce nonsense (`<script src="…\.css">`). Copy
|
|
309
|
+
* `nonce`/`crossorigin`/`referrerpolicy` from the original element so
|
|
310
|
+
* the same CSP and fetch policy applies; `defer`/`async`/`type` have no
|
|
311
|
+
* meaning on `<link>` and are dropped.
|
|
312
|
+
* @param {string} originalTag the originating `<script>`/`<link>` tag's source text
|
|
313
|
+
* @param {string} href URL for the stylesheet
|
|
314
|
+
* @returns {string} the sibling `<link>` tag's HTML
|
|
315
|
+
*/
|
|
316
|
+
const buildStylesheetLink = (originalTag, href) => {
|
|
317
|
+
let extra = "";
|
|
318
|
+
for (const attr of COPYABLE_LINK_ATTRS) {
|
|
319
|
+
// Match ` <attr>`, ` <attr>=value`, ` <attr>="value"`, ` <attr>='value'`.
|
|
320
|
+
const re = new RegExp(
|
|
321
|
+
`\\s${attr}(?:\\s*=\\s*(?:"[^"]*"|'[^']*'|[^\\s>]+))?(?=[\\s/>])`,
|
|
322
|
+
"i"
|
|
323
|
+
);
|
|
324
|
+
const m = originalTag.match(re);
|
|
325
|
+
if (m) extra += m[0];
|
|
326
|
+
}
|
|
327
|
+
const safeHref = href.replace(/"/g, """);
|
|
328
|
+
return `<link rel="stylesheet" href="${safeHref}"${extra}>`;
|
|
329
|
+
};
|
|
330
|
+
|
|
187
331
|
/**
|
|
188
332
|
* Clone the original `<script>`/`<link>` opening tag with its `src`/`href`
|
|
189
333
|
* value swapped for a different chunk URL. Reusing the source text verbatim
|
|
@@ -252,6 +396,7 @@ HtmlScriptSrcDependency.Template = class HtmlScriptSrcDependencyTemplate extends
|
|
|
252
396
|
const { runtimeTemplate } = templateContext;
|
|
253
397
|
const dep = /** @type {HtmlScriptSrcDependency} */ (dependency);
|
|
254
398
|
const compilation = runtimeTemplate.compilation;
|
|
399
|
+
const { chunkGraph } = compilation;
|
|
255
400
|
const entrypoint = /** @type {Entrypoint | undefined} */ (
|
|
256
401
|
compilation.entrypoints.get(dep.entryName)
|
|
257
402
|
);
|
|
@@ -263,50 +408,144 @@ HtmlScriptSrcDependency.Template = class HtmlScriptSrcDependencyTemplate extends
|
|
|
263
408
|
|
|
264
409
|
const orderedChunks = getEntrypointChunksInLoadOrder(entrypoint);
|
|
265
410
|
const entryChunk = orderedChunks[orderedChunks.length - 1];
|
|
266
|
-
const
|
|
267
|
-
|
|
411
|
+
const isStylesheet = dep.elementKind === "stylesheet";
|
|
412
|
+
|
|
413
|
+
// Rewrite the originating tag's src/href to the entry chunk's
|
|
414
|
+
// primary asset for that element kind: `.css` for
|
|
415
|
+
// `<link rel="stylesheet">`, `.js` for everything else.
|
|
416
|
+
const entryContentHashType = isStylesheet ? "css" : "javascript";
|
|
268
417
|
const entryUrl = `${CssUrlDependency.PUBLIC_PATH_AUTO}${getChunkFilename(
|
|
269
418
|
entryChunk,
|
|
270
419
|
compilation,
|
|
271
|
-
|
|
420
|
+
entryContentHashType
|
|
272
421
|
)}`;
|
|
273
422
|
source.replace(dep.range[0], dep.range[1] - 1, entryUrl);
|
|
274
423
|
|
|
275
|
-
if (
|
|
276
|
-
orderedChunks.length <= 1 ||
|
|
277
|
-
dep.tagStart < 0 ||
|
|
278
|
-
dep.tagOpenEnd <= dep.tagStart
|
|
279
|
-
) {
|
|
424
|
+
if (dep.tagStart < 0 || dep.tagOpenEnd <= dep.tagStart) {
|
|
280
425
|
return;
|
|
281
426
|
}
|
|
282
427
|
|
|
283
|
-
// The browser must load every chunk
|
|
284
|
-
// entry chunk.
|
|
285
|
-
//
|
|
286
|
-
//
|
|
428
|
+
// The browser must load every chunk the entry needs, not just the
|
|
429
|
+
// entry chunk. For `<script>` entries that's the JS for sibling
|
|
430
|
+
// chunks plus — critically — the CSS for any chunk that holds
|
|
431
|
+
// stylesheets imported transitively from the JS source. Previously
|
|
432
|
+
// every sibling was cloned as a `<script>` pointing at a `.js`
|
|
433
|
+
// filename, so CSS chunks ended up as `<script src="foo.css">`
|
|
434
|
+
// pointing at non-existent `.js` files (the bug in
|
|
435
|
+
// html-webpack-plugin#1838 / webpack/mini-css-extract-plugin#959,
|
|
436
|
+
// magnified here because the entry chunk's own CSS was emitted to
|
|
437
|
+
// disk but never linked from the HTML at all).
|
|
287
438
|
const originalContent = /** @type {string} */ (source.original().source());
|
|
288
439
|
const originalTag = originalContent.slice(dep.tagStart, dep.tagOpenEnd);
|
|
289
440
|
const srcStartInTag = dep.range[0] - dep.tagStart;
|
|
290
441
|
const srcEndInTag = dep.range[1] - dep.tagStart;
|
|
291
442
|
|
|
292
|
-
|
|
293
|
-
|
|
443
|
+
/**
|
|
444
|
+
* @param {Chunk} chunk chunk to emit a sibling tag for
|
|
445
|
+
* @param {"javascript" | "css"} kind content type slice of the chunk to emit
|
|
446
|
+
* @returns {string} a single sibling tag's HTML
|
|
447
|
+
*/
|
|
448
|
+
const buildSibling = (chunk, kind) => {
|
|
294
449
|
const url = `${CssUrlDependency.PUBLIC_PATH_AUTO}${getChunkFilename(
|
|
295
|
-
|
|
450
|
+
chunk,
|
|
296
451
|
compilation,
|
|
297
|
-
|
|
452
|
+
kind
|
|
298
453
|
)}`;
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
454
|
+
if (kind === "css" && !isStylesheet) {
|
|
455
|
+
// Originating tag is `<script>` (or `<link rel=modulepreload>`)
|
|
456
|
+
// but this chunk is CSS — emit a fresh `<link>` rather than
|
|
457
|
+
// cloning the script.
|
|
458
|
+
return buildStylesheetLink(originalTag, url);
|
|
459
|
+
}
|
|
460
|
+
return cloneTagWithUrl(
|
|
461
|
+
originalTag,
|
|
462
|
+
srcStartInTag,
|
|
463
|
+
srcEndInTag,
|
|
464
|
+
url,
|
|
465
|
+
dep.elementKind
|
|
307
466
|
);
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
const siblings = [];
|
|
470
|
+
|
|
471
|
+
if (isStylesheet) {
|
|
472
|
+
// `<link rel="stylesheet">` entries are CSS-only — every sibling
|
|
473
|
+
// chunk in the entrypoint is also CSS. Keep cloning the original
|
|
474
|
+
// `<link>` for them so attributes like `media` carry over.
|
|
475
|
+
for (let i = 0; i < orderedChunks.length - 1; i++) {
|
|
476
|
+
siblings.push(buildSibling(orderedChunks[i], "css"));
|
|
477
|
+
}
|
|
478
|
+
} else {
|
|
479
|
+
// CSS chunks are emitted before JS chunks so the cascade is set
|
|
480
|
+
// up before any script runs. Within CSS the order needs to match
|
|
481
|
+
// the source's import order — `entrypoint.chunks` alone doesn't
|
|
482
|
+
// give us that for arbitrary splitChunks layouts (splitChunks
|
|
483
|
+
// inserts each new chunk before the entry chunk via
|
|
484
|
+
// `insertChunk(_, before)`, so split CSS siblings end up in
|
|
485
|
+
// *reverse* of the order they were processed — exactly the
|
|
486
|
+
// html-webpack-plugin#1838 / mini-css-extract#959 symptom). We
|
|
487
|
+
// re-derive the order from the entrypoint's module post-order
|
|
488
|
+
// index, which mirrors the dependency walk and so reflects the
|
|
489
|
+
// import order.
|
|
490
|
+
/** @type {{ chunk: Chunk, index: number }[]} */
|
|
491
|
+
const cssChunkOrder = [];
|
|
492
|
+
/** @type {Chunk[]} */
|
|
493
|
+
const jsChunks = [];
|
|
494
|
+
for (let i = 0; i < orderedChunks.length - 1; i++) {
|
|
495
|
+
const chunk = orderedChunks[i];
|
|
496
|
+
const hasCss = chunkHasCss(chunk, chunkGraph);
|
|
497
|
+
const hasJs = chunkHasJs(chunk, chunkGraph);
|
|
498
|
+
if (hasCss) {
|
|
499
|
+
cssChunkOrder.push({
|
|
500
|
+
chunk,
|
|
501
|
+
index: firstCssModulePostOrderIndex(chunk, entrypoint, chunkGraph)
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
// Anything that isn't CSS-only stays on the JS lane, in the
|
|
505
|
+
// `orderedChunks` order — that preserves the runtime-first /
|
|
506
|
+
// vendor-before-entry invariant of `getEntrypointChunksInLoadOrder`.
|
|
507
|
+
// Chunks that produce no `.js` and no `.css` (e.g. wasm-only
|
|
508
|
+
// or asset-only) still get a `<script>` clone here so we
|
|
509
|
+
// keep prior behavior for users who relied on it.
|
|
510
|
+
if (hasJs || !hasCss) jsChunks.push(chunk);
|
|
511
|
+
}
|
|
512
|
+
// If the entry chunk itself contains CSS (entry JS imports CSS
|
|
513
|
+
// without splitChunks separating it), fold it into the same CSS
|
|
514
|
+
// ordering so the entry-chunk `<link>` lands in the correct
|
|
515
|
+
// cascade position relative to sibling CSS chunks.
|
|
516
|
+
if (chunkHasCss(entryChunk, chunkGraph)) {
|
|
517
|
+
cssChunkOrder.push({
|
|
518
|
+
chunk: entryChunk,
|
|
519
|
+
index: firstCssModulePostOrderIndex(
|
|
520
|
+
entryChunk,
|
|
521
|
+
entrypoint,
|
|
522
|
+
chunkGraph
|
|
523
|
+
)
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
cssChunkOrder.sort((a, b) => {
|
|
527
|
+
// Direct subtraction would yield `NaN` when both indices are
|
|
528
|
+
// `Infinity` (the documented fallback for chunks whose CSS
|
|
529
|
+
// modules the entrypoint's walk never reaches), and
|
|
530
|
+
// `Array#sort` doesn't promise stable ordering on the legacy
|
|
531
|
+
// Node 10 targets this repo still supports — so the
|
|
532
|
+
// tie-breaker must always run when the indices match,
|
|
533
|
+
// including the `Infinity === Infinity` case.
|
|
534
|
+
if (a.index < b.index) return -1;
|
|
535
|
+
if (a.index > b.index) return 1;
|
|
536
|
+
return compareChunksForCssTieBreak(a.chunk, b.chunk);
|
|
537
|
+
});
|
|
538
|
+
for (const { chunk } of cssChunkOrder) {
|
|
539
|
+
siblings.push(buildSibling(chunk, "css"));
|
|
540
|
+
}
|
|
541
|
+
for (const chunk of jsChunks) {
|
|
542
|
+
siblings.push(buildSibling(chunk, "javascript"));
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if (siblings.length > 0) {
|
|
547
|
+
source.insert(dep.tagStart, siblings.join(""));
|
|
308
548
|
}
|
|
309
|
-
source.insert(dep.tagStart, siblings.join(""));
|
|
310
549
|
}
|
|
311
550
|
};
|
|
312
551
|
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
"use strict";
|
|
7
7
|
|
|
8
|
+
const { ASSET_URL_TYPE } = require("../ModuleSourceTypeConstants");
|
|
8
9
|
const RawDataUrlModule = require("../asset/RawDataUrlModule");
|
|
9
10
|
const makeSerializable = require("../util/makeSerializable");
|
|
10
11
|
const memoize = require("../util/memoize");
|
|
@@ -114,8 +115,8 @@ HtmlSourceDependency.Template = class HtmlSourceDependencyTemplate extends (
|
|
|
114
115
|
const data = codeGen.data;
|
|
115
116
|
if (!data) return "data:,";
|
|
116
117
|
const url = data.get("url");
|
|
117
|
-
if (!url || !url[
|
|
118
|
-
return url[
|
|
118
|
+
if (!url || !url[ASSET_URL_TYPE]) return "data:,";
|
|
119
|
+
return url[ASSET_URL_TYPE];
|
|
119
120
|
}
|
|
120
121
|
};
|
|
121
122
|
|
|
@@ -255,11 +255,7 @@ class HtmlModulesPlugin {
|
|
|
255
255
|
options
|
|
256
256
|
)
|
|
257
257
|
);
|
|
258
|
-
return new HtmlGenerator(
|
|
259
|
-
/** @type {import("../../declarations/WebpackOptions").HtmlGeneratorOptions} */
|
|
260
|
-
(generatorOptions),
|
|
261
|
-
compilation.moduleGraph
|
|
262
|
-
);
|
|
258
|
+
return new HtmlGenerator(generatorOptions, compilation.moduleGraph);
|
|
263
259
|
});
|
|
264
260
|
|
|
265
261
|
NormalModule.getCompilationHooks(compilation).processResult.tap(
|