webpack 5.107.0 → 5.107.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/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/Compiler.js +27 -3
- package/lib/ContextModuleFactory.js +45 -38
- 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/MultiCompiler.js +14 -0
- package/lib/NormalModule.js +531 -53
- package/lib/NormalModuleFactory.js +38 -26
- package/lib/ProgressPlugin.js +1 -1
- package/lib/RuntimePlugin.js +1 -1
- package/lib/SourceMapDevToolPlugin.js +335 -57
- 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/buildChunkGraph.js +24 -2
- package/lib/cache/getLazyHashedEtag.js +9 -2
- 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/HarmonyExportInitFragment.js +8 -9
- package/lib/dependencies/HtmlInlineScriptDependency.js +3 -14
- package/lib/dependencies/HtmlInlineStyleDependency.js +17 -0
- package/lib/dependencies/HtmlScriptSrcDependency.js +265 -65
- package/lib/dependencies/HtmlSourceDependency.js +21 -2
- package/lib/dependencies/WorkerPlugin.js +18 -4
- package/lib/hmr/LazyCompilationPlugin.js +104 -0
- package/lib/html/HtmlGenerator.js +81 -33
- package/lib/html/HtmlModulesPlugin.js +87 -28
- package/lib/html/walkHtmlTokens.js +641 -125
- package/lib/index.js +2 -0
- package/lib/javascript/JavascriptModulesPlugin.js +2 -2
- package/lib/javascript/JavascriptParser.js +1 -1
- package/lib/library/ModuleLibraryPlugin.js +30 -24
- package/lib/node/NodeWatchFileSystem.js +37 -22
- package/lib/optimize/ConcatenatedModule.js +3 -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/util/fs.js +6 -1
- package/lib/wasm-async/AsyncWebAssemblyModulesPlugin.js +1 -2
- package/package.json +5 -5
- 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 +472 -149
|
@@ -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");
|
|
12
|
+
const HtmlGenerator = require("../html/HtmlGenerator");
|
|
7
13
|
const makeSerializable = require("../util/makeSerializable");
|
|
8
|
-
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 */
|
|
@@ -89,39 +95,6 @@ class HtmlScriptSrcDependency extends ModuleDependency {
|
|
|
89
95
|
}
|
|
90
96
|
}
|
|
91
97
|
|
|
92
|
-
/**
|
|
93
|
-
* @param {Chunk} chunk a chunk
|
|
94
|
-
* @param {import("../Compilation")} compilation compilation
|
|
95
|
-
* @param {"javascript" | "css"} contentHashType which content hash to plug into the filename template
|
|
96
|
-
* @returns {string} chunk filename path (no public-path prefix)
|
|
97
|
-
*/
|
|
98
|
-
const getChunkFilename = (chunk, compilation, contentHashType) => {
|
|
99
|
-
const outputOptions = compilation.outputOptions;
|
|
100
|
-
let filenameTemplate;
|
|
101
|
-
if (contentHashType === "css") {
|
|
102
|
-
// For a CSS-typed chunk, use the same template the CSS pipeline
|
|
103
|
-
// will use when it actually emits the `.css` file, so the `<link
|
|
104
|
-
// rel="stylesheet" href>` URL we write into the HTML matches the
|
|
105
|
-
// asset on disk.
|
|
106
|
-
filenameTemplate =
|
|
107
|
-
require("../css/CssModulesPlugin").getChunkFilenameTemplate(
|
|
108
|
-
chunk,
|
|
109
|
-
outputOptions
|
|
110
|
-
);
|
|
111
|
-
} else {
|
|
112
|
-
filenameTemplate =
|
|
113
|
-
chunk.filenameTemplate ||
|
|
114
|
-
(chunk.canBeInitial()
|
|
115
|
-
? outputOptions.filename
|
|
116
|
-
: outputOptions.chunkFilename);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return compilation.getPath(filenameTemplate, {
|
|
120
|
-
chunk,
|
|
121
|
-
contentHashType
|
|
122
|
-
});
|
|
123
|
-
};
|
|
124
|
-
|
|
125
98
|
/**
|
|
126
99
|
* @param {Entrypoint} entrypoint entrypoint
|
|
127
100
|
* @returns {Chunk[]} every chunk this entrypoint needs in load order: the
|
|
@@ -184,6 +157,144 @@ const getEntrypointChunksInLoadOrder = (entrypoint) => {
|
|
|
184
157
|
return ordered;
|
|
185
158
|
};
|
|
186
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Whether webpack will emit a `.js` file for this chunk that must be
|
|
162
|
+
* loaded with a `<script>` tag. Covers three independent reasons a
|
|
163
|
+
* chunk needs JS output: it owns one or more JS-source-type modules;
|
|
164
|
+
* it has entry modules whose source types include JavaScript (entry
|
|
165
|
+
* modules don't show up in `getChunkModulesIterableBySourceType` until
|
|
166
|
+
* they're connected as regular modules — this is why
|
|
167
|
+
* `JavascriptModulesPlugin#_chunkHasJs` checks them separately); or it
|
|
168
|
+
* is a runtime chunk — `chunk.hasRuntime()` — which produces a `.js`
|
|
169
|
+
* file holding the webpack runtime, but its `RuntimeModule`s live in
|
|
170
|
+
* a separate `runtimeModules` set and are *not* surfaced via
|
|
171
|
+
* `getChunkModulesIterableBySourceType`. Missing the runtime case
|
|
172
|
+
* would cause a `runtimeChunk`-split chunk to fall out of the
|
|
173
|
+
* `<script>` list and re-emerge after the chunks that depend on it,
|
|
174
|
+
* producing `__webpack_require__ is not defined` at load time.
|
|
175
|
+
* @param {Chunk} chunk chunk
|
|
176
|
+
* @param {ChunkGraph} chunkGraph chunk graph
|
|
177
|
+
* @returns {boolean} true if the chunk emits a `.js` file
|
|
178
|
+
*/
|
|
179
|
+
const chunkHasJs = (chunk, chunkGraph) => {
|
|
180
|
+
if (chunk.hasRuntime()) return true;
|
|
181
|
+
if (chunkGraph.getNumberOfEntryModules(chunk) > 0) {
|
|
182
|
+
for (const module of chunkGraph.getChunkEntryModulesIterable(chunk)) {
|
|
183
|
+
if (chunkGraph.getModuleSourceTypes(module).has(JAVASCRIPT_TYPE)) {
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return Boolean(
|
|
189
|
+
chunkGraph.getChunkModulesIterableBySourceType(chunk, JAVASCRIPT_TYPE)
|
|
190
|
+
);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Whether webpack will emit a `.css` file for this chunk that must be
|
|
195
|
+
* loaded with a `<link rel="stylesheet">` tag. Matches
|
|
196
|
+
* `CssModulesPlugin.chunkHasCss` exactly — both regular CSS modules
|
|
197
|
+
* and pure `@import` placeholder modules count, since the latter
|
|
198
|
+
* still contribute a `.css` asset to the chunk.
|
|
199
|
+
* @param {Chunk} chunk chunk
|
|
200
|
+
* @param {ChunkGraph} chunkGraph chunk graph
|
|
201
|
+
* @returns {boolean} true if the chunk emits a `.css` file
|
|
202
|
+
*/
|
|
203
|
+
const chunkHasCss = (chunk, chunkGraph) =>
|
|
204
|
+
Boolean(chunkGraph.getChunkModulesIterableBySourceType(chunk, CSS_TYPE)) ||
|
|
205
|
+
Boolean(
|
|
206
|
+
chunkGraph.getChunkModulesIterableBySourceType(chunk, CSS_IMPORT_TYPE)
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Compare two chunks for a deterministic tie-break in CSS link ordering.
|
|
211
|
+
* `chunk.name` and `chunk.id` are both stable strings (when present);
|
|
212
|
+
* one of them is set for every chunk webpack emits. We can't rely on
|
|
213
|
+
* `Array.prototype.sort` being stable — webpack still supports Node
|
|
214
|
+
* 10.13 where V8's sort is not guaranteed stable for arrays larger
|
|
215
|
+
* than ten elements — so any time `firstCssModulePostOrderIndex`
|
|
216
|
+
* returns the same value for two chunks (most commonly when several
|
|
217
|
+
* chunks have no reachable CSS module in the entrypoint's dependency
|
|
218
|
+
* walk and all map to `Infinity`) this comparator picks the canonical
|
|
219
|
+
* order.
|
|
220
|
+
* @param {Chunk} a first chunk
|
|
221
|
+
* @param {Chunk} b second chunk
|
|
222
|
+
* @returns {-1 | 0 | 1} ordering
|
|
223
|
+
*/
|
|
224
|
+
const compareChunksForCssTieBreak = (a, b) => {
|
|
225
|
+
const an = `${a.name || ""} ${a.id === null || a.id === undefined ? "" : a.id}`;
|
|
226
|
+
const bn = `${b.name || ""} ${b.id === null || b.id === undefined ? "" : b.id}`;
|
|
227
|
+
if (an < bn) return -1;
|
|
228
|
+
if (an > bn) return 1;
|
|
229
|
+
return 0;
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Smallest post-order index among the CSS modules of a chunk, taken
|
|
234
|
+
* from the entrypoint's view of the dependency graph. Used to sort
|
|
235
|
+
* sibling CSS chunks so they appear in source import order in the
|
|
236
|
+
* extracted HTML — `entrypoint.chunks` itself does not give that
|
|
237
|
+
* ordering for arbitrary splitChunks layouts. Considers both
|
|
238
|
+
* `CSS_TYPE` and `CSS_IMPORT_TYPE` modules so a chunk made up
|
|
239
|
+
* exclusively of `@import` placeholder modules (e.g. when splitChunks
|
|
240
|
+
* separates them from their target CSS) still sorts by its true
|
|
241
|
+
* source position rather than collapsing to `Infinity` and relying on
|
|
242
|
+
* the chunk-name tie-breaker.
|
|
243
|
+
* @param {Chunk} chunk chunk
|
|
244
|
+
* @param {Entrypoint} entrypoint entrypoint the chunk belongs to
|
|
245
|
+
* @param {ChunkGraph} chunkGraph chunk graph
|
|
246
|
+
* @returns {number} the lowest post-order index of any CSS or
|
|
247
|
+
* CSS-import module in the chunk, or `Number.POSITIVE_INFINITY` when
|
|
248
|
+
* no such module has a defined index (e.g. for a module the
|
|
249
|
+
* entrypoint never reached on its own dependency walk — runtime-only
|
|
250
|
+
* modules, modules reached via `dependOn`, etc.) so such chunks sort
|
|
251
|
+
* last among CSS chunks
|
|
252
|
+
*/
|
|
253
|
+
const firstCssModulePostOrderIndex = (chunk, entrypoint, chunkGraph) => {
|
|
254
|
+
let min = Number.POSITIVE_INFINITY;
|
|
255
|
+
for (const sourceType of [CSS_TYPE, CSS_IMPORT_TYPE]) {
|
|
256
|
+
const modules = chunkGraph.getChunkModulesIterableBySourceType(
|
|
257
|
+
chunk,
|
|
258
|
+
sourceType
|
|
259
|
+
);
|
|
260
|
+
if (!modules) continue;
|
|
261
|
+
for (const module of modules) {
|
|
262
|
+
const idx = entrypoint.getModulePostOrderIndex(module);
|
|
263
|
+
if (idx !== undefined && idx < min) min = idx;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return min;
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const COPYABLE_LINK_ATTRS = ["nonce", "crossorigin", "referrerpolicy"];
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Build a fresh `<link rel="stylesheet" href="…">` for a CSS chunk that
|
|
273
|
+
* was pulled in by a `<script src>` entry — the originating tag was a
|
|
274
|
+
* `<script>`, but the chunk is CSS so cloning the script tag verbatim
|
|
275
|
+
* would produce nonsense (`<script src="…\.css">`). Copy
|
|
276
|
+
* `nonce`/`crossorigin`/`referrerpolicy` from the original element so
|
|
277
|
+
* the same CSP and fetch policy applies; `defer`/`async`/`type` have no
|
|
278
|
+
* meaning on `<link>` and are dropped.
|
|
279
|
+
* @param {string} originalTag the originating `<script>`/`<link>` tag's source text
|
|
280
|
+
* @param {string} href URL for the stylesheet
|
|
281
|
+
* @returns {string} the sibling `<link>` tag's HTML
|
|
282
|
+
*/
|
|
283
|
+
const buildStylesheetLink = (originalTag, href) => {
|
|
284
|
+
let extra = "";
|
|
285
|
+
for (const attr of COPYABLE_LINK_ATTRS) {
|
|
286
|
+
// Match ` <attr>`, ` <attr>=value`, ` <attr>="value"`, ` <attr>='value'`.
|
|
287
|
+
const re = new RegExp(
|
|
288
|
+
`\\s${attr}(?:\\s*=\\s*(?:"[^"]*"|'[^']*'|[^\\s>]+))?(?=[\\s/>])`,
|
|
289
|
+
"i"
|
|
290
|
+
);
|
|
291
|
+
const m = originalTag.match(re);
|
|
292
|
+
if (m) extra += m[0];
|
|
293
|
+
}
|
|
294
|
+
const safeHref = href.replace(/"/g, """);
|
|
295
|
+
return `<link rel="stylesheet" href="${safeHref}"${extra}>`;
|
|
296
|
+
};
|
|
297
|
+
|
|
187
298
|
/**
|
|
188
299
|
* Clone the original `<script>`/`<link>` opening tag with its `src`/`href`
|
|
189
300
|
* value swapped for a different chunk URL. Reusing the source text verbatim
|
|
@@ -252,6 +363,7 @@ HtmlScriptSrcDependency.Template = class HtmlScriptSrcDependencyTemplate extends
|
|
|
252
363
|
const { runtimeTemplate } = templateContext;
|
|
253
364
|
const dep = /** @type {HtmlScriptSrcDependency} */ (dependency);
|
|
254
365
|
const compilation = runtimeTemplate.compilation;
|
|
366
|
+
const { chunkGraph } = compilation;
|
|
255
367
|
const entrypoint = /** @type {Entrypoint | undefined} */ (
|
|
256
368
|
compilation.entrypoints.get(dep.entryName)
|
|
257
369
|
);
|
|
@@ -263,50 +375,138 @@ HtmlScriptSrcDependency.Template = class HtmlScriptSrcDependencyTemplate extends
|
|
|
263
375
|
|
|
264
376
|
const orderedChunks = getEntrypointChunksInLoadOrder(entrypoint);
|
|
265
377
|
const entryChunk = orderedChunks[orderedChunks.length - 1];
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
378
|
+
const isStylesheet = dep.elementKind === "stylesheet";
|
|
379
|
+
|
|
380
|
+
// Rewrite src/href to a chunk-URL sentinel (resolved by renderManifest):
|
|
381
|
+
// `.css` for `<link rel="stylesheet">`, `.js` for everything else.
|
|
382
|
+
const entryContentHashType = isStylesheet ? "css" : "javascript";
|
|
383
|
+
const entryUrl = HtmlGenerator.makeChunkUrlSentinel(
|
|
269
384
|
entryChunk,
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
)}`;
|
|
385
|
+
entryContentHashType
|
|
386
|
+
);
|
|
273
387
|
source.replace(dep.range[0], dep.range[1] - 1, entryUrl);
|
|
274
388
|
|
|
275
|
-
if (
|
|
276
|
-
orderedChunks.length <= 1 ||
|
|
277
|
-
dep.tagStart < 0 ||
|
|
278
|
-
dep.tagOpenEnd <= dep.tagStart
|
|
279
|
-
) {
|
|
389
|
+
if (dep.tagStart < 0 || dep.tagOpenEnd <= dep.tagStart) {
|
|
280
390
|
return;
|
|
281
391
|
}
|
|
282
392
|
|
|
283
|
-
// The browser must load every chunk
|
|
284
|
-
// entry chunk.
|
|
285
|
-
//
|
|
286
|
-
//
|
|
393
|
+
// The browser must load every chunk the entry needs, not just the
|
|
394
|
+
// entry chunk. For `<script>` entries that's the JS for sibling
|
|
395
|
+
// chunks plus — critically — the CSS for any chunk that holds
|
|
396
|
+
// stylesheets imported transitively from the JS source. Previously
|
|
397
|
+
// every sibling was cloned as a `<script>` pointing at a `.js`
|
|
398
|
+
// filename, so CSS chunks ended up as `<script src="foo.css">`
|
|
399
|
+
// pointing at non-existent `.js` files (the bug in
|
|
400
|
+
// html-webpack-plugin#1838 / webpack/mini-css-extract-plugin#959,
|
|
401
|
+
// magnified here because the entry chunk's own CSS was emitted to
|
|
402
|
+
// disk but never linked from the HTML at all).
|
|
287
403
|
const originalContent = /** @type {string} */ (source.original().source());
|
|
288
404
|
const originalTag = originalContent.slice(dep.tagStart, dep.tagOpenEnd);
|
|
289
405
|
const srcStartInTag = dep.range[0] - dep.tagStart;
|
|
290
406
|
const srcEndInTag = dep.range[1] - dep.tagStart;
|
|
291
407
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
)
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
408
|
+
/**
|
|
409
|
+
* @param {Chunk} chunk chunk to emit a sibling tag for
|
|
410
|
+
* @param {"javascript" | "css"} kind content type slice of the chunk to emit
|
|
411
|
+
* @returns {string} a single sibling tag's HTML
|
|
412
|
+
*/
|
|
413
|
+
const buildSibling = (chunk, kind) => {
|
|
414
|
+
const url = HtmlGenerator.makeChunkUrlSentinel(chunk, kind);
|
|
415
|
+
if (kind === "css" && !isStylesheet) {
|
|
416
|
+
// Originating tag is `<script>` (or `<link rel=modulepreload>`)
|
|
417
|
+
// but this chunk is CSS — emit a fresh `<link>` rather than
|
|
418
|
+
// cloning the script.
|
|
419
|
+
return buildStylesheetLink(originalTag, url);
|
|
420
|
+
}
|
|
421
|
+
return cloneTagWithUrl(
|
|
422
|
+
originalTag,
|
|
423
|
+
srcStartInTag,
|
|
424
|
+
srcEndInTag,
|
|
425
|
+
url,
|
|
426
|
+
dep.elementKind
|
|
307
427
|
);
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
const siblings = [];
|
|
431
|
+
|
|
432
|
+
if (isStylesheet) {
|
|
433
|
+
// `<link rel="stylesheet">` entries are CSS-only — every sibling
|
|
434
|
+
// chunk in the entrypoint is also CSS. Keep cloning the original
|
|
435
|
+
// `<link>` for them so attributes like `media` carry over.
|
|
436
|
+
for (let i = 0; i < orderedChunks.length - 1; i++) {
|
|
437
|
+
siblings.push(buildSibling(orderedChunks[i], "css"));
|
|
438
|
+
}
|
|
439
|
+
} else {
|
|
440
|
+
// CSS chunks are emitted before JS chunks so the cascade is set
|
|
441
|
+
// up before any script runs. Within CSS the order needs to match
|
|
442
|
+
// the source's import order — `entrypoint.chunks` alone doesn't
|
|
443
|
+
// give us that for arbitrary splitChunks layouts (splitChunks
|
|
444
|
+
// inserts each new chunk before the entry chunk via
|
|
445
|
+
// `insertChunk(_, before)`, so split CSS siblings end up in
|
|
446
|
+
// *reverse* of the order they were processed — exactly the
|
|
447
|
+
// html-webpack-plugin#1838 / mini-css-extract#959 symptom). We
|
|
448
|
+
// re-derive the order from the entrypoint's module post-order
|
|
449
|
+
// index, which mirrors the dependency walk and so reflects the
|
|
450
|
+
// import order.
|
|
451
|
+
/** @type {{ chunk: Chunk, index: number }[]} */
|
|
452
|
+
const cssChunkOrder = [];
|
|
453
|
+
/** @type {Chunk[]} */
|
|
454
|
+
const jsChunks = [];
|
|
455
|
+
for (let i = 0; i < orderedChunks.length - 1; i++) {
|
|
456
|
+
const chunk = orderedChunks[i];
|
|
457
|
+
const hasCss = chunkHasCss(chunk, chunkGraph);
|
|
458
|
+
const hasJs = chunkHasJs(chunk, chunkGraph);
|
|
459
|
+
if (hasCss) {
|
|
460
|
+
cssChunkOrder.push({
|
|
461
|
+
chunk,
|
|
462
|
+
index: firstCssModulePostOrderIndex(chunk, entrypoint, chunkGraph)
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
// Anything that isn't CSS-only stays on the JS lane, in the
|
|
466
|
+
// `orderedChunks` order — that preserves the runtime-first /
|
|
467
|
+
// vendor-before-entry invariant of `getEntrypointChunksInLoadOrder`.
|
|
468
|
+
// Chunks that produce no `.js` and no `.css` (e.g. wasm-only
|
|
469
|
+
// or asset-only) still get a `<script>` clone here so we
|
|
470
|
+
// keep prior behavior for users who relied on it.
|
|
471
|
+
if (hasJs || !hasCss) jsChunks.push(chunk);
|
|
472
|
+
}
|
|
473
|
+
// If the entry chunk itself contains CSS (entry JS imports CSS
|
|
474
|
+
// without splitChunks separating it), fold it into the same CSS
|
|
475
|
+
// ordering so the entry-chunk `<link>` lands in the correct
|
|
476
|
+
// cascade position relative to sibling CSS chunks.
|
|
477
|
+
if (chunkHasCss(entryChunk, chunkGraph)) {
|
|
478
|
+
cssChunkOrder.push({
|
|
479
|
+
chunk: entryChunk,
|
|
480
|
+
index: firstCssModulePostOrderIndex(
|
|
481
|
+
entryChunk,
|
|
482
|
+
entrypoint,
|
|
483
|
+
chunkGraph
|
|
484
|
+
)
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
cssChunkOrder.sort((a, b) => {
|
|
488
|
+
// Direct subtraction would yield `NaN` when both indices are
|
|
489
|
+
// `Infinity` (the documented fallback for chunks whose CSS
|
|
490
|
+
// modules the entrypoint's walk never reaches), and
|
|
491
|
+
// `Array#sort` doesn't promise stable ordering on the legacy
|
|
492
|
+
// Node 10 targets this repo still supports — so the
|
|
493
|
+
// tie-breaker must always run when the indices match,
|
|
494
|
+
// including the `Infinity === Infinity` case.
|
|
495
|
+
if (a.index < b.index) return -1;
|
|
496
|
+
if (a.index > b.index) return 1;
|
|
497
|
+
return compareChunksForCssTieBreak(a.chunk, b.chunk);
|
|
498
|
+
});
|
|
499
|
+
for (const { chunk } of cssChunkOrder) {
|
|
500
|
+
siblings.push(buildSibling(chunk, "css"));
|
|
501
|
+
}
|
|
502
|
+
for (const chunk of jsChunks) {
|
|
503
|
+
siblings.push(buildSibling(chunk, "javascript"));
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (siblings.length > 0) {
|
|
508
|
+
source.insert(dep.tagStart, siblings.join(""));
|
|
308
509
|
}
|
|
309
|
-
source.insert(dep.tagStart, siblings.join(""));
|
|
310
510
|
}
|
|
311
511
|
};
|
|
312
512
|
|
|
@@ -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");
|
|
@@ -12,14 +13,17 @@ const ModuleDependency = require("./ModuleDependency");
|
|
|
12
13
|
|
|
13
14
|
/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
|
|
14
15
|
/** @typedef {import("../CodeGenerationResults")} CodeGenerationResults */
|
|
16
|
+
/** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */
|
|
15
17
|
/** @typedef {import("../Dependency")} Dependency */
|
|
16
18
|
/** @typedef {import("../DependencyTemplate").CssDependencyTemplateContext} DependencyTemplateContext */
|
|
17
19
|
/** @typedef {import("../Module")} Module */
|
|
20
|
+
/** @typedef {import("../Module").BuildInfo} BuildInfo */
|
|
18
21
|
/** @typedef {import("../Module").CodeGenerationResult} CodeGenerationResult */
|
|
19
22
|
/** @typedef {import("../Module").CodeGenerationResultData} CodeGenerationResultData */
|
|
20
23
|
/** @typedef {import("../javascript/JavascriptParser").Range} Range */
|
|
21
24
|
/** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
|
|
22
25
|
/** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext} ObjectSerializerContext */
|
|
26
|
+
/** @typedef {import("../util/Hash")} Hash */
|
|
23
27
|
/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
|
|
24
28
|
|
|
25
29
|
const getIgnoredRawDataUrlModule = memoize(
|
|
@@ -54,6 +58,21 @@ class HtmlSourceDependency extends ModuleDependency {
|
|
|
54
58
|
return getIgnoredRawDataUrlModule();
|
|
55
59
|
}
|
|
56
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Updates the hash with the data contributed by this instance.
|
|
63
|
+
* @param {Hash} hash hash to be updated
|
|
64
|
+
* @param {UpdateHashContext} context context
|
|
65
|
+
* @returns {void}
|
|
66
|
+
*/
|
|
67
|
+
updateHash(hash, context) {
|
|
68
|
+
// Fold in the asset's hash so the HTML invalidates when the embedded URL changes.
|
|
69
|
+
const { chunkGraph } = context;
|
|
70
|
+
const module = chunkGraph.moduleGraph.getModule(this);
|
|
71
|
+
if (!module) return;
|
|
72
|
+
const { hash: buildHash } = /** @type {BuildInfo} */ (module.buildInfo);
|
|
73
|
+
if (buildHash) hash.update(buildHash);
|
|
74
|
+
}
|
|
75
|
+
|
|
57
76
|
/**
|
|
58
77
|
* Serializes this instance into the provided serializer context.
|
|
59
78
|
* @param {ObjectSerializerContext} context context
|
|
@@ -114,8 +133,8 @@ HtmlSourceDependency.Template = class HtmlSourceDependencyTemplate extends (
|
|
|
114
133
|
const data = codeGen.data;
|
|
115
134
|
if (!data) return "data:,";
|
|
116
135
|
const url = data.get("url");
|
|
117
|
-
if (!url || !url[
|
|
118
|
-
return url[
|
|
136
|
+
if (!url || !url[ASSET_URL_TYPE]) return "data:,";
|
|
137
|
+
return url[ASSET_URL_TYPE];
|
|
119
138
|
}
|
|
120
139
|
};
|
|
121
140
|
|
|
@@ -349,10 +349,24 @@ class WorkerPlugin {
|
|
|
349
349
|
)
|
|
350
350
|
);
|
|
351
351
|
} else {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
352
|
+
// `webpackEntryOptions` is user input from a magic
|
|
353
|
+
// comment, so copy only safe own keys to avoid
|
|
354
|
+
// prototype pollution via `__proto__`/`constructor`/
|
|
355
|
+
// `prototype`.
|
|
356
|
+
const userEntryOptions = importOptions.webpackEntryOptions;
|
|
357
|
+
for (const key of Object.keys(userEntryOptions)) {
|
|
358
|
+
if (
|
|
359
|
+
key === "__proto__" ||
|
|
360
|
+
key === "constructor" ||
|
|
361
|
+
key === "prototype"
|
|
362
|
+
) {
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
/** @type {EXPECTED_ANY} */
|
|
366
|
+
(entryOptions)[key] = /** @type {EXPECTED_ANY} */ (
|
|
367
|
+
userEntryOptions
|
|
368
|
+
)[key];
|
|
369
|
+
}
|
|
356
370
|
}
|
|
357
371
|
}
|
|
358
372
|
if (importOptions.webpackChunkName !== undefined) {
|
|
@@ -18,6 +18,7 @@ const {
|
|
|
18
18
|
const RuntimeGlobals = require("../RuntimeGlobals");
|
|
19
19
|
const Template = require("../Template");
|
|
20
20
|
const CommonJsRequireDependency = require("../dependencies/CommonJsRequireDependency");
|
|
21
|
+
const { resolveByProperty } = require("../util/cleverMerge");
|
|
21
22
|
const { registerNotSerializable } = require("../util/serialization");
|
|
22
23
|
|
|
23
24
|
/** @typedef {import("../config/defaults").WebpackOptionsNormalizedWithDefaults} WebpackOptions */
|
|
@@ -45,6 +46,96 @@ const { registerNotSerializable } = require("../util/serialization");
|
|
|
45
46
|
|
|
46
47
|
/** @typedef {{ client: string, data: string, active: boolean }} ModuleResult */
|
|
47
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Library wrappers of these types pass external modules as closure arguments
|
|
51
|
+
* (e.g. `__WEBPACK_EXTERNAL_MODULE_react__`) baked into the entry chunk at
|
|
52
|
+
* render time. When `lazyCompilation` activates a proxy for the first time,
|
|
53
|
+
* any external dependency the lazily-built module pulls in lands in a hot
|
|
54
|
+
* update chunk that lives outside the original wrapper closure, so the
|
|
55
|
+
* factory body can't resolve its closure identifier and throws at runtime.
|
|
56
|
+
* Reserving the externals up front (during the inactive build) folds them
|
|
57
|
+
* into the initial wrapper, so the closure identifiers are already defined
|
|
58
|
+
* when the activation update arrives.
|
|
59
|
+
*/
|
|
60
|
+
const CLOSURE_LIBRARY_TYPES = new Set([
|
|
61
|
+
"umd",
|
|
62
|
+
"umd2",
|
|
63
|
+
"amd",
|
|
64
|
+
"amd-require",
|
|
65
|
+
"system"
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* `enabledLibraryTypes` covers both the global `output.library.type` and any
|
|
70
|
+
* per-entry `entry.<name>.library.type`, so a UMD/AMD/System wrapper attached
|
|
71
|
+
* to an individual entry is still detected.
|
|
72
|
+
* @param {import("../../declarations/WebpackOptions").OutputNormalized} output normalized output option
|
|
73
|
+
* @returns {boolean} true when at least one library wrapper passes externals as closure arguments
|
|
74
|
+
*/
|
|
75
|
+
const hasClosureLibrary = (output) => {
|
|
76
|
+
const enabled = output.enabledLibraryTypes;
|
|
77
|
+
if (enabled) {
|
|
78
|
+
for (const type of enabled) {
|
|
79
|
+
if (CLOSURE_LIBRARY_TYPES.has(type)) return true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (output.library && output.library.type) {
|
|
83
|
+
return CLOSURE_LIBRARY_TYPES.has(output.library.type);
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Collects request strings from statically-enumerable externals (string,
|
|
90
|
+
* object, and arrays of those). Function and RegExp forms are skipped because
|
|
91
|
+
* their effective request set isn't knowable until something asks for it.
|
|
92
|
+
*
|
|
93
|
+
* Layer resolution mirrors `ExternalModuleFactoryPlugin.resolveLayer`: the
|
|
94
|
+
* effective map for the proxy's layer is computed via the same
|
|
95
|
+
* `resolveByProperty(..., "byLayer", layer)` helper that the externals system
|
|
96
|
+
* uses, so `byLayer.default` fallback and function-form `byLayer` entries are
|
|
97
|
+
* honored the same way.
|
|
98
|
+
*
|
|
99
|
+
* Entries whose effective value is `false` are skipped — `false` explicitly
|
|
100
|
+
* disables externalization for that request, and reserving it would force the
|
|
101
|
+
* real module into the entry chunk.
|
|
102
|
+
* @param {import("../../declarations/WebpackOptions").Externals | undefined} externals normalized externals option
|
|
103
|
+
* @param {string | null} layer issuer layer for which to resolve `byLayer`
|
|
104
|
+
* @returns {Set<string>} requests to reserve in the entry chunk
|
|
105
|
+
*/
|
|
106
|
+
const collectStaticExternalRequests = (externals, layer) => {
|
|
107
|
+
/** @type {Set<string>} */
|
|
108
|
+
const requests = new Set();
|
|
109
|
+
if (!externals) return requests;
|
|
110
|
+
/** @param {import("../../declarations/WebpackOptions").ExternalItem} item one item */
|
|
111
|
+
const visit = (item) => {
|
|
112
|
+
if (typeof item === "string") {
|
|
113
|
+
requests.add(item);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (!item || typeof item !== "object" || item instanceof RegExp) return;
|
|
117
|
+
const resolved = /** @type {Record<string, unknown>} */ (
|
|
118
|
+
resolveByProperty(
|
|
119
|
+
/** @type {Record<string, unknown>} */ (item),
|
|
120
|
+
"byLayer",
|
|
121
|
+
layer
|
|
122
|
+
)
|
|
123
|
+
);
|
|
124
|
+
for (const [request, value] of Object.entries(resolved)) {
|
|
125
|
+
// `false` explicitly opts the request out of externalization; reserving
|
|
126
|
+
// it would pull the actual module into the entry chunk.
|
|
127
|
+
if (value === false) continue;
|
|
128
|
+
requests.add(request);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
if (Array.isArray(externals)) {
|
|
132
|
+
for (const item of externals) visit(item);
|
|
133
|
+
} else {
|
|
134
|
+
visit(externals);
|
|
135
|
+
}
|
|
136
|
+
return requests;
|
|
137
|
+
};
|
|
138
|
+
|
|
48
139
|
/**
|
|
49
140
|
* Defines the backend api type used by this module.
|
|
50
141
|
* @typedef {object} BackendApi
|
|
@@ -213,6 +304,19 @@ class LazyCompilationProxyModule extends Module {
|
|
|
213
304
|
const block = new AsyncDependenciesBlock({});
|
|
214
305
|
block.addDependency(dep);
|
|
215
306
|
this.addBlock(block);
|
|
307
|
+
} else if (hasClosureLibrary(compilation.options.output)) {
|
|
308
|
+
// Reserve statically-declared externals as dependencies of the inactive
|
|
309
|
+
// proxy so the initial entry chunk's library wrapper already exposes
|
|
310
|
+
// their closure identifiers (e.g. `__WEBPACK_EXTERNAL_MODULE_react__`).
|
|
311
|
+
// Once the proxy activates and the lazily-built module references those
|
|
312
|
+
// externals, the identifiers resolve normally instead of throwing.
|
|
313
|
+
const requests = collectStaticExternalRequests(
|
|
314
|
+
options.externals,
|
|
315
|
+
this.layer
|
|
316
|
+
);
|
|
317
|
+
for (const request of requests) {
|
|
318
|
+
this.addDependency(new CommonJsRequireDependency(request));
|
|
319
|
+
}
|
|
216
320
|
}
|
|
217
321
|
callback();
|
|
218
322
|
}
|