mirror of https://github.com/webpack/webpack.git
feat: support prefetch/preload for module chunk format
This commit is contained in:
commit
e660ad8b75
|
|
@ -2292,6 +2292,10 @@ export interface Environment {
|
|||
* The environment supports destructuring ('{ a, b } = obj').
|
||||
*/
|
||||
destructuring?: boolean;
|
||||
/**
|
||||
* The environment supports 'document'.
|
||||
*/
|
||||
document?: boolean;
|
||||
/**
|
||||
* The environment supports an async import() function to import EcmaScript modules.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1142,6 +1142,11 @@ const applyOutputDefaults = (
|
|||
output.module
|
||||
)
|
||||
);
|
||||
F(
|
||||
environment,
|
||||
"document",
|
||||
() => tp && optimistic(/** @type {boolean | undefined} */ (tp.document))
|
||||
);
|
||||
|
||||
const { trustedTypes } = output;
|
||||
if (trustedTypes) {
|
||||
|
|
|
|||
|
|
@ -505,19 +505,17 @@ class CssModulesPlugin {
|
|||
}
|
||||
if (hasFailed) {
|
||||
// There is a not resolve-able conflict with the selectedModule
|
||||
if (compilation) {
|
||||
// TODO print better warning
|
||||
compilation.warnings.push(
|
||||
new WebpackError(
|
||||
`chunk ${chunk.name || chunk.id}\nConflicting order between ${
|
||||
/** @type {Module} */
|
||||
(hasFailed).readableIdentifier(compilation.requestShortener)
|
||||
} and ${selectedModule.readableIdentifier(
|
||||
compilation.requestShortener
|
||||
)}`
|
||||
)
|
||||
);
|
||||
}
|
||||
// TODO print better warning
|
||||
compilation.warnings.push(
|
||||
new WebpackError(
|
||||
`chunk ${chunk.name || chunk.id}\nConflicting order between ${
|
||||
/** @type {Module} */
|
||||
(hasFailed).readableIdentifier(compilation.requestShortener)
|
||||
} and ${selectedModule.readableIdentifier(
|
||||
compilation.requestShortener
|
||||
)}`
|
||||
)
|
||||
);
|
||||
selectedModule = /** @type {Module} */ (hasFailed);
|
||||
}
|
||||
// Insert the selected module into the final modules list
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ class ModuleChunkLoadingRuntimeModule extends RuntimeModule {
|
|||
const chunk = /** @type {Chunk} */ (this.chunk);
|
||||
const {
|
||||
runtimeTemplate,
|
||||
outputOptions: { importFunctionName }
|
||||
outputOptions: { environment, importFunctionName, crossOriginLoading }
|
||||
} = compilation;
|
||||
const fn = RuntimeGlobals.ensureChunkHandlers;
|
||||
const withBaseURI = this._runtimeRequirements.has(RuntimeGlobals.baseURI);
|
||||
|
|
@ -105,6 +105,14 @@ class ModuleChunkLoadingRuntimeModule extends RuntimeModule {
|
|||
const withHmr = this._runtimeRequirements.has(
|
||||
RuntimeGlobals.hmrDownloadUpdateHandlers
|
||||
);
|
||||
const { linkPreload, linkPrefetch } =
|
||||
ModuleChunkLoadingRuntimeModule.getCompilationHooks(compilation);
|
||||
const withPrefetch =
|
||||
environment.document &&
|
||||
this._runtimeRequirements.has(RuntimeGlobals.prefetchChunkHandlers);
|
||||
const withPreload =
|
||||
environment.document &&
|
||||
this._runtimeRequirements.has(RuntimeGlobals.preloadChunkHandlers);
|
||||
const conditionMap = chunkGraph.getChunkConditionMap(chunk, chunkHasJs);
|
||||
const hasJsMatcher = compileBooleanMatcher(conditionMap);
|
||||
const initialChunkIds = getInitialChunkIds(chunk, chunkGraph, chunkHasJs);
|
||||
|
|
@ -229,6 +237,86 @@ class ModuleChunkLoadingRuntimeModule extends RuntimeModule {
|
|||
])
|
||||
: "// no chunk on demand loading",
|
||||
"",
|
||||
withPrefetch && hasJsMatcher !== false
|
||||
? `${
|
||||
RuntimeGlobals.prefetchChunkHandlers
|
||||
}.j = ${runtimeTemplate.basicFunction("chunkId", [
|
||||
`if((!${
|
||||
RuntimeGlobals.hasOwnProperty
|
||||
}(installedChunks, chunkId) || installedChunks[chunkId] === undefined) && ${
|
||||
hasJsMatcher === true ? "true" : hasJsMatcher("chunkId")
|
||||
}) {`,
|
||||
Template.indent([
|
||||
"installedChunks[chunkId] = null;",
|
||||
linkPrefetch.call(
|
||||
Template.asString([
|
||||
"var link = document.createElement('link');",
|
||||
crossOriginLoading
|
||||
? `link.crossOrigin = ${JSON.stringify(
|
||||
crossOriginLoading
|
||||
)};`
|
||||
: "",
|
||||
`if (${RuntimeGlobals.scriptNonce}) {`,
|
||||
Template.indent(
|
||||
`link.setAttribute("nonce", ${RuntimeGlobals.scriptNonce});`
|
||||
),
|
||||
"}",
|
||||
'link.rel = "prefetch";',
|
||||
'link.as = "script";',
|
||||
`link.href = ${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkScriptFilename}(chunkId);`
|
||||
]),
|
||||
chunk
|
||||
),
|
||||
"document.head.appendChild(link);"
|
||||
]),
|
||||
"}"
|
||||
])};`
|
||||
: "// no prefetching",
|
||||
"",
|
||||
withPreload && hasJsMatcher !== false
|
||||
? `${
|
||||
RuntimeGlobals.preloadChunkHandlers
|
||||
}.j = ${runtimeTemplate.basicFunction("chunkId", [
|
||||
`if((!${
|
||||
RuntimeGlobals.hasOwnProperty
|
||||
}(installedChunks, chunkId) || installedChunks[chunkId] === undefined) && ${
|
||||
hasJsMatcher === true ? "true" : hasJsMatcher("chunkId")
|
||||
}) {`,
|
||||
Template.indent([
|
||||
"installedChunks[chunkId] = null;",
|
||||
linkPreload.call(
|
||||
Template.asString([
|
||||
"var link = document.createElement('link');",
|
||||
"link.charset = 'utf-8';",
|
||||
`if (${RuntimeGlobals.scriptNonce}) {`,
|
||||
Template.indent(
|
||||
`link.setAttribute("nonce", ${RuntimeGlobals.scriptNonce});`
|
||||
),
|
||||
"}",
|
||||
'link.rel = "modulepreload";',
|
||||
`link.href = ${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkScriptFilename}(chunkId);`,
|
||||
crossOriginLoading
|
||||
? crossOriginLoading === "use-credentials"
|
||||
? 'link.crossOrigin = "use-credentials";'
|
||||
: Template.asString([
|
||||
"if (link.href.indexOf(window.location.origin + '/') !== 0) {",
|
||||
Template.indent(
|
||||
`link.crossOrigin = ${JSON.stringify(
|
||||
crossOriginLoading
|
||||
)};`
|
||||
),
|
||||
"}"
|
||||
])
|
||||
: ""
|
||||
]),
|
||||
chunk
|
||||
),
|
||||
"document.head.appendChild(link);"
|
||||
]),
|
||||
"}"
|
||||
])};`
|
||||
: "// no preloaded",
|
||||
"",
|
||||
withExternalInstallChunk
|
||||
? Template.asString([
|
||||
`${RuntimeGlobals.externalInstallChunk} = installChunk;`
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -865,6 +865,10 @@
|
|||
"description": "The environment supports destructuring ('{ a, b } = obj').",
|
||||
"type": "boolean"
|
||||
},
|
||||
"document": {
|
||||
"description": "The environment supports 'document'.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"dynamicImport": {
|
||||
"description": "The environment supports an async import() function to import EcmaScript modules.",
|
||||
"type": "boolean"
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ describe("snapshots", () => {
|
|||
"bigIntLiteral": undefined,
|
||||
"const": true,
|
||||
"destructuring": true,
|
||||
"document": true,
|
||||
"dynamicImport": undefined,
|
||||
"dynamicImportInWorker": undefined,
|
||||
"forOf": true,
|
||||
|
|
@ -347,6 +348,7 @@ describe("snapshots", () => {
|
|||
"bigIntLiteral": undefined,
|
||||
"const": true,
|
||||
"destructuring": true,
|
||||
"document": true,
|
||||
"dynamicImport": undefined,
|
||||
"dynamicImportInWorker": undefined,
|
||||
"forOf": true,
|
||||
|
|
@ -1310,6 +1312,9 @@ describe("snapshots", () => {
|
|||
- "web": true,
|
||||
+ "web": false,
|
||||
@@ ... @@
|
||||
- "document": true,
|
||||
+ "document": false,
|
||||
@@ ... @@
|
||||
- "target": "web",
|
||||
+ "target": "node",
|
||||
@@ ... @@
|
||||
|
|
@ -1336,6 +1341,9 @@ describe("snapshots", () => {
|
|||
- "fetch",
|
||||
+ "async-node",
|
||||
@@ ... @@
|
||||
- "document": true,
|
||||
+ "document": false,
|
||||
@@ ... @@
|
||||
- "globalObject": "self",
|
||||
+ "globalObject": "global",
|
||||
@@ ... @@
|
||||
|
|
@ -1426,12 +1434,18 @@ describe("snapshots", () => {
|
|||
- Expected
|
||||
+ Received
|
||||
|
||||
@@ ... @@
|
||||
- "document": true,
|
||||
+ "document": false,
|
||||
@@ ... @@
|
||||
- "chunkLoading": "jsonp",
|
||||
+ "chunkLoading": "import-scripts",
|
||||
@@ ... @@
|
||||
- "jsonp",
|
||||
@@ ... @@
|
||||
- "document": true,
|
||||
+ "document": false,
|
||||
@@ ... @@
|
||||
+ "worker",
|
||||
@@ ... @@
|
||||
- "target": "web",
|
||||
|
|
@ -1455,6 +1469,9 @@ describe("snapshots", () => {
|
|||
- "web": true,
|
||||
+ "web": false,
|
||||
@@ ... @@
|
||||
- "document": true,
|
||||
+ "document": false,
|
||||
@@ ... @@
|
||||
- "target": "web",
|
||||
+ "target": "electron-main",
|
||||
@@ ... @@
|
||||
|
|
@ -1481,6 +1498,9 @@ describe("snapshots", () => {
|
|||
- "fetch",
|
||||
+ "async-node",
|
||||
@@ ... @@
|
||||
- "document": true,
|
||||
+ "document": false,
|
||||
@@ ... @@
|
||||
- "globalObject": "self",
|
||||
+ "globalObject": "global",
|
||||
@@ ... @@
|
||||
|
|
@ -1582,6 +1602,9 @@ describe("snapshots", () => {
|
|||
- "node": false,
|
||||
+ "node": true,
|
||||
@@ ... @@
|
||||
- "document": true,
|
||||
+ "document": false,
|
||||
@@ ... @@
|
||||
- "target": "web",
|
||||
+ "target": "electron-preload",
|
||||
@@ ... @@
|
||||
|
|
@ -1608,6 +1631,9 @@ describe("snapshots", () => {
|
|||
- "fetch",
|
||||
+ "async-node",
|
||||
@@ ... @@
|
||||
- "document": true,
|
||||
+ "document": false,
|
||||
@@ ... @@
|
||||
- "globalObject": "self",
|
||||
+ "globalObject": "global",
|
||||
@@ ... @@
|
||||
|
|
@ -2035,6 +2061,12 @@ describe("snapshots", () => {
|
|||
- "bigIntLiteral": undefined,
|
||||
- "const": true,
|
||||
- "destructuring": true,
|
||||
+ "arrowFunction": false,
|
||||
+ "asyncFunction": false,
|
||||
+ "bigIntLiteral": false,
|
||||
+ "const": false,
|
||||
+ "destructuring": false,
|
||||
@@ ... @@
|
||||
- "dynamicImport": undefined,
|
||||
- "dynamicImportInWorker": undefined,
|
||||
- "forOf": true,
|
||||
|
|
@ -2042,11 +2074,6 @@ describe("snapshots", () => {
|
|||
- "module": undefined,
|
||||
- "optionalChaining": true,
|
||||
- "templateLiteral": true,
|
||||
+ "arrowFunction": false,
|
||||
+ "asyncFunction": false,
|
||||
+ "bigIntLiteral": false,
|
||||
+ "const": false,
|
||||
+ "destructuring": false,
|
||||
+ "dynamicImport": false,
|
||||
+ "dynamicImportInWorker": false,
|
||||
+ "forOf": false,
|
||||
|
|
@ -2066,6 +2093,12 @@ describe("snapshots", () => {
|
|||
- "bigIntLiteral": undefined,
|
||||
- "const": true,
|
||||
- "destructuring": true,
|
||||
+ "arrowFunction": false,
|
||||
+ "asyncFunction": false,
|
||||
+ "bigIntLiteral": false,
|
||||
+ "const": false,
|
||||
+ "destructuring": false,
|
||||
@@ ... @@
|
||||
- "dynamicImport": undefined,
|
||||
- "dynamicImportInWorker": undefined,
|
||||
- "forOf": true,
|
||||
|
|
@ -2073,11 +2106,6 @@ describe("snapshots", () => {
|
|||
- "module": undefined,
|
||||
- "optionalChaining": true,
|
||||
- "templateLiteral": true,
|
||||
+ "arrowFunction": false,
|
||||
+ "asyncFunction": false,
|
||||
+ "bigIntLiteral": false,
|
||||
+ "const": false,
|
||||
+ "destructuring": false,
|
||||
+ "dynamicImport": false,
|
||||
+ "dynamicImportInWorker": false,
|
||||
+ "forOf": false,
|
||||
|
|
|
|||
|
|
@ -6283,6 +6283,19 @@ Object {
|
|||
"multiple": false,
|
||||
"simpleType": "boolean",
|
||||
},
|
||||
"output-environment-document": Object {
|
||||
"configs": Array [
|
||||
Object {
|
||||
"description": "The environment supports 'document'.",
|
||||
"multiple": false,
|
||||
"path": "output.environment.document",
|
||||
"type": "boolean",
|
||||
},
|
||||
],
|
||||
"description": "The environment supports 'document'.",
|
||||
"multiple": false,
|
||||
"simpleType": "boolean",
|
||||
},
|
||||
"output-environment-dynamic-import": Object {
|
||||
"configs": Array [
|
||||
Object {
|
||||
|
|
|
|||
|
|
@ -5,10 +5,17 @@ __webpack_public_path__ = "https://example.com/public/path/";
|
|||
it("should prefetch and preload child chunks on chunk load", () => {
|
||||
let link, script;
|
||||
|
||||
expect(document.head._children).toHaveLength(1);
|
||||
expect(document.head._children).toHaveLength(2);
|
||||
|
||||
// Test preload
|
||||
link = document.head._children[0];
|
||||
expect(link._type).toBe("link");
|
||||
expect(link.rel).toBe("prefetch");
|
||||
expect(link.as).toBe("script");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk1.mjs");
|
||||
|
||||
// Test prefetch
|
||||
link = document.head._children[0];
|
||||
link = document.head._children[1];
|
||||
expect(link._type).toBe("link");
|
||||
expect(link.rel).toBe("prefetch");
|
||||
expect(link.as).toBe("style");
|
||||
|
|
@ -18,37 +25,54 @@ it("should prefetch and preload child chunks on chunk load", () => {
|
|||
/* webpackChunkName: "chunk1", webpackPrefetch: true */ "./chunk1.mjs"
|
||||
);
|
||||
|
||||
expect(document.head._children).toHaveLength(2);
|
||||
expect(document.head._children).toHaveLength(4);
|
||||
|
||||
// Test normal script loading
|
||||
link = document.head._children[1];
|
||||
link = document.head._children[2];
|
||||
expect(link._type).toBe("link");
|
||||
expect(link.rel).toBe("preload");
|
||||
expect(link.as).toBe("style");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk1-a-css.css");
|
||||
|
||||
link = document.head._children[3];
|
||||
expect(link._type).toBe("link");
|
||||
expect(link.rel).toBe("modulepreload");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk1-b.mjs");
|
||||
|
||||
return promise.then(() => {
|
||||
expect(document.head._children).toHaveLength(2);
|
||||
expect(document.head._children).toHaveLength(6);
|
||||
|
||||
link = document.head._children[4];
|
||||
expect(link._type).toBe("link");
|
||||
expect(link.rel).toBe("prefetch");
|
||||
expect(link.as).toBe("script");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk1-c.mjs");
|
||||
|
||||
link = document.head._children[5];
|
||||
expect(link._type).toBe("link");
|
||||
expect(link.rel).toBe("prefetch");
|
||||
expect(link.as).toBe("script");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk1-a.mjs");
|
||||
|
||||
const promise2 = import(
|
||||
/* webpackChunkName: "chunk1", webpackPrefetch: true */ "./chunk1.mjs"
|
||||
);
|
||||
|
||||
// Loading chunk1 again should not trigger prefetch/preload
|
||||
expect(document.head._children).toHaveLength(2);
|
||||
expect(document.head._children).toHaveLength(6);
|
||||
|
||||
const promise3 = import(/* webpackChunkName: "chunk2" */ "./chunk2.mjs");
|
||||
|
||||
expect(document.head._children).toHaveLength(2);
|
||||
expect(document.head._children).toHaveLength(6);
|
||||
|
||||
return promise3.then(() => {
|
||||
expect(document.head._children).toHaveLength(2);
|
||||
expect(document.head._children).toHaveLength(6);
|
||||
|
||||
const promise4 = import(/* webpackChunkName: "chunk1-css" */ "./chunk1.css");
|
||||
|
||||
expect(document.head._children).toHaveLength(3);
|
||||
expect(document.head._children).toHaveLength(7);
|
||||
|
||||
link = document.head._children[2];
|
||||
link = document.head._children[6];
|
||||
expect(link._type).toBe("link");
|
||||
expect(link.rel).toBe("stylesheet");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk1-css.css");
|
||||
|
|
@ -56,9 +80,9 @@ it("should prefetch and preload child chunks on chunk load", () => {
|
|||
|
||||
const promise5 = import(/* webpackChunkName: "chunk2-css", webpackPrefetch: true */ "./chunk2.css");
|
||||
|
||||
expect(document.head._children).toHaveLength(4);
|
||||
expect(document.head._children).toHaveLength(8);
|
||||
|
||||
link = document.head._children[3];
|
||||
link = document.head._children[7];
|
||||
expect(link._type).toBe("link");
|
||||
expect(link.rel).toBe("stylesheet");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk2-css.css");
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ module.exports = {
|
|||
"bigIntLiteral": false,
|
||||
"const": false,
|
||||
"destructuring": false,
|
||||
"document": true,
|
||||
"dynamicImport": false,
|
||||
"dynamicImportInWorker": false,
|
||||
"forOf": false,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ module.exports = {
|
|||
"bigIntLiteral": false,
|
||||
"const": false,
|
||||
"destructuring": false,
|
||||
"document": true,
|
||||
"dynamicImport": false,
|
||||
"dynamicImportInWorker": false,
|
||||
"forOf": false,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ module.exports = {
|
|||
"bigIntLiteral": false,
|
||||
"const": false,
|
||||
"destructuring": false,
|
||||
"document": true,
|
||||
"dynamicImport": false,
|
||||
"dynamicImportInWorker": false,
|
||||
"forOf": false,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ module.exports = {
|
|||
"bigIntLiteral": true,
|
||||
"const": true,
|
||||
"destructuring": true,
|
||||
"document": false,
|
||||
"dynamicImport": true,
|
||||
"dynamicImportInWorker": false,
|
||||
"forOf": true,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
a {
|
||||
color: red;
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
a {
|
||||
color: red;
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
a {
|
||||
color: blue;
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
// This config need to be set on initial evaluation to be effective
|
||||
__webpack_nonce__ = "nonce";
|
||||
__webpack_public_path__ = "https://example.com/public/path/";
|
||||
|
||||
it("should prefetch and preload child chunks on chunk load", () => {
|
||||
let link, script;
|
||||
|
||||
expect(document.head._children).toHaveLength(2);
|
||||
|
||||
// Test prefetch from entry chunk
|
||||
link = document.head._children[0];
|
||||
expect(link._type).toBe("link");
|
||||
expect(link.rel).toBe("prefetch");
|
||||
expect(link.as).toBe("script");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk1.js");
|
||||
|
||||
link = document.head._children[1];
|
||||
expect(link._type).toBe("link");
|
||||
expect(link.rel).toBe("prefetch");
|
||||
expect(link.as).toBe("style");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk2-css.css");
|
||||
|
||||
const promise = import(
|
||||
/* webpackChunkName: "chunk1", webpackPrefetch: true */ "./chunk1.js"
|
||||
);
|
||||
|
||||
expect(document.head._children).toHaveLength(5);
|
||||
|
||||
// Test normal script loading
|
||||
script = document.head._children[2];
|
||||
expect(script._type).toBe("script");
|
||||
expect(script.src).toBe("https://example.com/public/path/chunk1.js");
|
||||
expect(script.getAttribute("nonce")).toBe("nonce");
|
||||
expect(script.crossOrigin).toBe("anonymous");
|
||||
expect(script.onload).toBeTypeOf("function");
|
||||
|
||||
// Test preload of chunk1-b
|
||||
link = document.head._children[3];
|
||||
expect(link._type).toBe("link");
|
||||
expect(link.rel).toBe("modulepreload");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk1-b.js");
|
||||
expect(link.charset).toBe("utf-8");
|
||||
expect(link.getAttribute("nonce")).toBe("nonce");
|
||||
expect(link.crossOrigin).toBe("anonymous");
|
||||
|
||||
// Test preload of chunk1-a-css
|
||||
link = document.head._children[4];
|
||||
expect(link._type).toBe("link");
|
||||
expect(link.rel).toBe("preload");
|
||||
expect(link.as).toBe("style");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk1-a-css.css");
|
||||
|
||||
// Run the script
|
||||
import(/* webpackIgnore: true */ "./chunk1.js");
|
||||
|
||||
script.onload();
|
||||
|
||||
return promise.then(() => {
|
||||
expect(document.head._children).toHaveLength(6);
|
||||
|
||||
// Test prefetching for chunk1-c and chunk1-a in this order
|
||||
link = document.head._children[4];
|
||||
expect(link._type).toBe("link");
|
||||
expect(link.rel).toBe("prefetch");
|
||||
expect(link.as).toBe("script");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk1-c.js");
|
||||
expect(link.crossOrigin).toBe("anonymous");
|
||||
|
||||
link = document.head._children[5];
|
||||
expect(link._type).toBe("link");
|
||||
expect(link.rel).toBe("prefetch");
|
||||
expect(link.as).toBe("script");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk1-a.js");
|
||||
expect(link.crossOrigin).toBe("anonymous");
|
||||
|
||||
const promise2 = import(
|
||||
/* webpackChunkName: "chunk1", webpackPrefetch: true */ "./chunk1.js"
|
||||
);
|
||||
|
||||
// Loading chunk1 again should not trigger prefetch/preload
|
||||
expect(document.head._children).toHaveLength(6);
|
||||
|
||||
const promise3 = import(/* webpackChunkName: "chunk2" */ "./chunk2.js");
|
||||
|
||||
expect(document.head._children).toHaveLength(7);
|
||||
|
||||
// Test normal script loading
|
||||
script = document.head._children[6];
|
||||
expect(script._type).toBe("script");
|
||||
expect(script.src).toBe("https://example.com/public/path/chunk2.js");
|
||||
expect(script.getAttribute("nonce")).toBe("nonce");
|
||||
expect(script.crossOrigin).toBe("anonymous");
|
||||
expect(script.onload).toBeTypeOf("function");
|
||||
|
||||
// Run the script
|
||||
import(/* webpackIgnore: true */ "./chunk2.js");
|
||||
|
||||
script.onload();
|
||||
|
||||
return promise3.then(() => {
|
||||
// Loading chunk2 again should not trigger prefetch/preload as it's already prefetch/preloaded
|
||||
expect(document.head._children).toHaveLength(6);
|
||||
|
||||
const promise4 = import(/* webpackChunkName: "chunk1-css" */ "./chunk1.css");
|
||||
|
||||
expect(document.head._children).toHaveLength(7);
|
||||
|
||||
link = document.head._children[6];
|
||||
expect(link._type).toBe("link");
|
||||
expect(link.rel).toBe("stylesheet");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk1-css.css");
|
||||
expect(link.crossOrigin).toBe("anonymous");
|
||||
|
||||
const promise5 = import(/* webpackChunkName: "chunk2-css", webpackPrefetch: true */ "./chunk2.css");
|
||||
|
||||
expect(document.head._children).toHaveLength(8);
|
||||
|
||||
link = document.head._children[7];
|
||||
expect(link._type).toBe("link");
|
||||
expect(link.rel).toBe("stylesheet");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk2-css.css");
|
||||
expect(link.crossOrigin).toBe("anonymous");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/** @type {import("../../../../").Configuration} */
|
||||
module.exports = {
|
||||
entry: "./index.mjs",
|
||||
experiments: {
|
||||
outputModule: true,
|
||||
css: true
|
||||
},
|
||||
name: "esm",
|
||||
target: "web",
|
||||
output: {
|
||||
publicPath: "",
|
||||
module: true,
|
||||
filename: "bundle0.mjs",
|
||||
chunkFilename: "[name].js",
|
||||
crossOriginLoading: "anonymous"
|
||||
},
|
||||
performance: {
|
||||
hints: false
|
||||
},
|
||||
optimization: {
|
||||
minimize: false
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
export default function() {
|
||||
import(/* webpackPrefetch: true, webpackChunkName: "chunk1-a" */ "./chunk1-a.mjs");
|
||||
import(/* webpackPreload: true, webpackChunkName: "chunk1-b" */ "./chunk1-b.mjs");
|
||||
import(/* webpackPreload: true, webpackChunkName: "chunk1-a-css" */ "./chunk1-a.css");
|
||||
import(/* webpackPrefetch: 10, webpackChunkName: "chunk1-c" */ "./chunk1-c.mjs");
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
export default function() {
|
||||
import(/* webpackPrefetch: true, webpackChunkName: "chunk1-a" */ "./chunk1-a.mjs");
|
||||
import(/* webpackPreload: true, webpackChunkName: "chunk1-b" */ "./chunk1-b.mjs");
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ __webpack_nonce__ = "nonce";
|
|||
__webpack_public_path__ = "https://example.com/public/path/";
|
||||
|
||||
it("should prefetch and preload child chunks on chunk load", () => {
|
||||
let link, script;
|
||||
let link;
|
||||
|
||||
expect(document.head._children).toHaveLength(2);
|
||||
|
||||
|
|
@ -12,7 +12,7 @@ it("should prefetch and preload child chunks on chunk load", () => {
|
|||
expect(link._type).toBe("link");
|
||||
expect(link.rel).toBe("prefetch");
|
||||
expect(link.as).toBe("script");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk1.js");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk1.mjs");
|
||||
|
||||
link = document.head._children[1];
|
||||
expect(link._type).toBe("link");
|
||||
|
|
@ -21,40 +21,29 @@ it("should prefetch and preload child chunks on chunk load", () => {
|
|||
expect(link.href).toBe("https://example.com/public/path/chunk2-css.css");
|
||||
|
||||
const promise = import(
|
||||
/* webpackChunkName: "chunk1", webpackPrefetch: true */ "./chunk1.js"
|
||||
/* webpackChunkName: "chunk1", webpackPrefetch: true */ "./chunk1.mjs"
|
||||
);
|
||||
|
||||
expect(document.head._children).toHaveLength(5);
|
||||
expect(document.head._children).toHaveLength(4);
|
||||
|
||||
// Test normal script loading
|
||||
script = document.head._children[2];
|
||||
expect(script._type).toBe("script");
|
||||
expect(script.src).toBe("https://example.com/public/path/chunk1.js");
|
||||
expect(script.getAttribute("nonce")).toBe("nonce");
|
||||
expect(script.crossOrigin).toBe("anonymous");
|
||||
expect(script.onload).toBeTypeOf("function");
|
||||
link = document.head._children[2];
|
||||
expect(link._type).toBe("link");
|
||||
expect(link.rel).toBe("preload");
|
||||
expect(link.as).toBe("style");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk1-a-css.css");
|
||||
expect(link.getAttribute("nonce")).toBe("nonce");
|
||||
expect(link.crossOrigin).toBe("anonymous");
|
||||
|
||||
// Test preload of chunk1-b
|
||||
link = document.head._children[3];
|
||||
expect(link._type).toBe("link");
|
||||
expect(link.rel).toBe("modulepreload");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk1-b.js");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk1-b.mjs");
|
||||
expect(link.charset).toBe("utf-8");
|
||||
expect(link.getAttribute("nonce")).toBe("nonce");
|
||||
expect(link.crossOrigin).toBe("anonymous");
|
||||
|
||||
// Test preload of chunk1-a-css
|
||||
link = document.head._children[4];
|
||||
expect(link._type).toBe("link");
|
||||
expect(link.rel).toBe("preload");
|
||||
expect(link.as).toBe("style");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk1-a-css.css");
|
||||
|
||||
// Run the script
|
||||
import(/* webpackIgnore: true */ "./chunk1.js");
|
||||
|
||||
script.onload();
|
||||
|
||||
return promise.then(() => {
|
||||
expect(document.head._children).toHaveLength(6);
|
||||
|
||||
|
|
@ -63,39 +52,25 @@ it("should prefetch and preload child chunks on chunk load", () => {
|
|||
expect(link._type).toBe("link");
|
||||
expect(link.rel).toBe("prefetch");
|
||||
expect(link.as).toBe("script");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk1-c.js");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk1-c.mjs");
|
||||
expect(link.crossOrigin).toBe("anonymous");
|
||||
|
||||
link = document.head._children[5];
|
||||
expect(link._type).toBe("link");
|
||||
expect(link.rel).toBe("prefetch");
|
||||
expect(link.as).toBe("script");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk1-a.js");
|
||||
expect(link.href).toBe("https://example.com/public/path/chunk1-a.mjs");
|
||||
expect(link.getAttribute("nonce")).toBe("nonce");
|
||||
expect(link.crossOrigin).toBe("anonymous");
|
||||
|
||||
const promise2 = import(
|
||||
/* webpackChunkName: "chunk1", webpackPrefetch: true */ "./chunk1.js"
|
||||
/* webpackChunkName: "chunk1", webpackPrefetch: true */ "./chunk1.mjs"
|
||||
);
|
||||
|
||||
// Loading chunk1 again should not trigger prefetch/preload
|
||||
expect(document.head._children).toHaveLength(6);
|
||||
|
||||
const promise3 = import(/* webpackChunkName: "chunk2" */ "./chunk2.js");
|
||||
const promise3 = import(/* webpackChunkName: "chunk2" */ "./chunk2.mjs");
|
||||
|
||||
expect(document.head._children).toHaveLength(7);
|
||||
|
||||
// Test normal script loading
|
||||
script = document.head._children[6];
|
||||
expect(script._type).toBe("script");
|
||||
expect(script.src).toBe("https://example.com/public/path/chunk2.js");
|
||||
expect(script.getAttribute("nonce")).toBe("nonce");
|
||||
expect(script.crossOrigin).toBe("anonymous");
|
||||
expect(script.onload).toBeTypeOf("function");
|
||||
|
||||
// Run the script
|
||||
import(/* webpackIgnore: true */ "./chunk2.js");
|
||||
|
||||
script.onload();
|
||||
expect(document.head._children).toHaveLength(6);
|
||||
|
||||
return promise3.then(() => {
|
||||
// Loading chunk2 again should not trigger prefetch/preload as it's already prefetch/preloaded
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ module.exports = {
|
|||
publicPath: "",
|
||||
module: true,
|
||||
filename: "bundle0.mjs",
|
||||
chunkFilename: "[name].js",
|
||||
chunkFilename: "[name].mjs",
|
||||
chunkFormat: "module",
|
||||
crossOriginLoading: "anonymous"
|
||||
},
|
||||
performance: {
|
||||
|
|
|
|||
|
|
@ -3918,6 +3918,11 @@ declare interface Environment {
|
|||
*/
|
||||
destructuring?: boolean;
|
||||
|
||||
/**
|
||||
* The environment supports 'document'.
|
||||
*/
|
||||
document?: boolean;
|
||||
|
||||
/**
|
||||
* The environment supports an async import() function to import EcmaScript modules.
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in New Issue