Merge pull request #7444 from MLoughry/prefetch-from-entry

Prefetch and preload  from entry chunk.
This commit is contained in:
Tobias Koppers 2018-06-04 20:07:29 +02:00 committed by GitHub
commit bc6b5b0c4f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 180 additions and 141 deletions

View File

@ -607,10 +607,10 @@ class Chunk {
return result;
}
getChildIdsByOrdersMap() {
getChildIdsByOrdersMap(includeDirectChildren) {
const chunkMaps = Object.create(null);
for (const chunk of this.getAllAsyncChunks()) {
const addChildIdsByOrdersToMap = chunk => {
const data = chunk.getChildIdsByOrders();
for (const key of Object.keys(data)) {
let chunkMap = chunkMaps[key];
@ -619,7 +619,16 @@ class Chunk {
}
chunkMap[chunk.id] = data[key];
}
};
if (includeDirectChildren) {
addChildIdsByOrdersToMap(this);
}
for (const chunk of this.getAllAsyncChunks()) {
addChildIdsByOrdersToMap(chunk);
}
return chunkMaps;
}

View File

@ -14,6 +14,7 @@ class JsonpChunkTemplatePlugin {
const jsonpFunction = chunkTemplate.outputOptions.jsonpFunction;
const globalObject = chunkTemplate.outputOptions.globalObject;
const source = new ConcatSource();
const prefetchChunks = chunk.getChildIdsByOrders().prefetch;
source.add(
`(${globalObject}[${JSON.stringify(
jsonpFunction
@ -31,6 +32,12 @@ class JsonpChunkTemplatePlugin {
);
if (entries.length > 0) {
source.add(`,${JSON.stringify(entries)}`);
} else if (prefetchChunks && prefetchChunks.length) {
source.add(`,0`);
}
if (prefetchChunks && prefetchChunks.length) {
source.add(`,${JSON.stringify(prefetchChunks)}`);
}
source.add("])");
return source;

View File

@ -28,6 +28,10 @@ class JsonpMainTemplatePlugin {
}
return false;
};
const needPrefetchingCode = chunk => {
const allPrefetchChunks = chunk.getChildIdsByOrdersMap(true).prefetch;
return allPrefetchChunks && Object.keys(allPrefetchChunks).length;
};
// TODO refactor this
if (!mainTemplate.hooks.jsonpScript) {
mainTemplate.hooks.jsonpScript = new SyncWaterfallHook([
@ -232,9 +236,21 @@ class JsonpMainTemplatePlugin {
mainTemplate.hooks.linkPrefetch.tap(
"JsonpMainTemplatePlugin",
(_, chunk, hash) => {
const crossOriginLoading =
mainTemplate.outputOptions.crossOriginLoading;
return Template.asString([
"var link = document.createElement('link');",
crossOriginLoading
? `link.crossOrigin = ${JSON.stringify(crossOriginLoading)};`
: "",
`if (${mainTemplate.requireFn}.nc) {`,
Template.indent(
`link.setAttribute("nonce", ${mainTemplate.requireFn}.nc);`
),
"}",
'link.rel = "prefetch";',
'link.as = "script";',
"link.href = jsonpScriptSrc(chunkId);"
]);
}
@ -288,7 +304,7 @@ class JsonpMainTemplatePlugin {
"",
"// chunk preloadng for javascript",
"",
`var chunkPreloadMap = ${JSON.stringify(chunkMap, null, "\t")}`,
`var chunkPreloadMap = ${JSON.stringify(chunkMap, null, "\t")};`,
"",
"var chunkPreloadData = chunkPreloadMap[chunkId];",
"if(chunkPreloadData) {",
@ -310,45 +326,6 @@ class JsonpMainTemplatePlugin {
]);
}
);
mainTemplate.hooks.requireEnsure.tap(
{
name: "JsonpMainTemplatePlugin prefetch",
stage: 20
},
(source, chunk, hash) => {
const chunkMap = chunk.getChildIdsByOrdersMap().prefetch;
if (!chunkMap || Object.keys(chunkMap).length === 0) return source;
return Template.asString([
source,
"",
"// chunk prefetching for javascript",
"",
`var chunkPrefetchMap = ${JSON.stringify(chunkMap, null, "\t")}`,
"",
"var chunkPrefetchData = chunkPrefetchMap[chunkId];",
"if(chunkPrefetchData) {",
Template.indent([
"Promise.all(promises).then(function() {",
Template.indent([
"var head = document.getElementsByTagName('head')[0];",
"chunkPrefetchData.forEach(function(chunkId) {",
Template.indent([
"if(installedChunks[chunkId] === undefined) {",
Template.indent([
"installedChunks[chunkId] = null;",
mainTemplate.hooks.linkPrefetch.call("", chunk, hash),
"head.appendChild(link);"
]),
"}"
]),
"});"
]),
"})"
]),
"}"
]);
}
);
mainTemplate.hooks.requireExtensions.tap(
"JsonpMainTemplatePlugin",
(source, chunk) => {
@ -369,6 +346,7 @@ class JsonpMainTemplatePlugin {
(source, chunk, hash) => {
if (needChunkLoadingCode(chunk)) {
const withDefer = needEntryDeferringCode(chunk);
const withPrefetch = needPrefetchingCode(chunk);
return Template.asString([
source,
"",
@ -378,6 +356,7 @@ class JsonpMainTemplatePlugin {
"var chunkIds = data[0];",
"var moreModules = data[1];",
withDefer ? "var executeModules = data[2];" : "",
withPrefetch ? "var prefetchChunks = data[3] || [];" : "",
'// add "moreModules" to the modules object,',
'// then flag all "chunkIds" as loaded and fire callback',
"var moduleId, chunkId, i = 0, resolves = [];",
@ -405,6 +384,23 @@ class JsonpMainTemplatePlugin {
]),
"}",
"if(parentJsonpFunction) parentJsonpFunction(data);",
withPrefetch
? Template.asString([
"// chunk prefetching for javascript",
"var head = document.getElementsByTagName('head')[0];",
"prefetchChunks.forEach(function(chunkId) {",
Template.indent([
"if(installedChunks[chunkId] === undefined) {",
Template.indent([
"installedChunks[chunkId] = null;",
mainTemplate.hooks.linkPrefetch.call("", chunk, hash),
"head.appendChild(link);"
]),
"}"
]),
"});"
])
: "",
"while(resolves.length) {",
Template.indent("resolves.shift()();"),
"}",
@ -479,6 +475,25 @@ class JsonpMainTemplatePlugin {
return source;
}
);
mainTemplate.hooks.beforeStartup.tap(
"JsonpMainTemplatePlugin",
(source, chunk, hash) => {
const prefetchChunks = chunk.getChildIdsByOrders().prefetch;
if (
needChunkLoadingCode(chunk) &&
prefetchChunks &&
prefetchChunks.length
) {
return Template.asString([
source,
`webpackJsonpCallback([[], {}, 0, ${JSON.stringify(
prefetchChunks
)}]);`
]);
}
return source;
}
);
mainTemplate.hooks.startup.tap(
"JsonpMainTemplatePlugin",
(source, chunk, hash) => {

View File

@ -7,6 +7,7 @@ const vm = require("vm");
const mkdirp = require("mkdirp");
const rimraf = require("rimraf");
const checkArrayExpectation = require("./checkArrayExpectation");
const FakeDocument = require("./helpers/FakeDocument");
const Stats = require("../lib/Stats");
const webpack = require("../lib/webpack");
@ -176,7 +177,8 @@ describe("ConfigTestCases", () => {
console: console,
expect: expect,
setTimeout: setTimeout,
clearTimeout: clearTimeout
clearTimeout: clearTimeout,
document: new FakeDocument()
};
function _require(currentDirectory, module) {

View File

@ -8,7 +8,7 @@ Child fitting:
Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
9ac13fb7087e9ff1b93e.js 1.05 KiB 0 [emitted]
f2e891598128a57b072c.js 11 KiB 1 [emitted]
f2e891598128a57b072c.js 11.1 KiB 1 [emitted]
d1ba53816ff760e185b0.js 1.94 KiB 2 [emitted]
7b5b0a943e9362bc88c6.js 1.94 KiB 3 [emitted]
Entrypoint main = d1ba53816ff760e185b0.js 7b5b0a943e9362bc88c6.js f2e891598128a57b072c.js
@ -34,7 +34,7 @@ Child content-change:
Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
9ac13fb7087e9ff1b93e.js 1.05 KiB 0 [emitted]
f2e891598128a57b072c.js 11 KiB 1 [emitted]
f2e891598128a57b072c.js 11.1 KiB 1 [emitted]
d1ba53816ff760e185b0.js 1.94 KiB 2 [emitted]
7b5b0a943e9362bc88c6.js 1.94 KiB 3 [emitted]
Entrypoint main = d1ba53816ff760e185b0.js 7b5b0a943e9362bc88c6.js f2e891598128a57b072c.js
@ -71,7 +71,7 @@ d6418937dfae4b3ee922.js 1 KiB 1 [emitted]
685acdc95ff4af957f47.js 1 KiB 7 [emitted]
606f48c13070850338b1.js 1.94 KiB 8 [emitted]
c5a8eae840969538f450.js 1.94 KiB 9 [emitted]
c69b2f79fdf6e98907c4.js 9.68 KiB 10 [emitted] main
c69b2f79fdf6e98907c4.js 9.7 KiB 10 [emitted] main
fcdf398c8972e4dcf788.js 1.94 KiB 11 [emitted]
Entrypoint main = c69b2f79fdf6e98907c4.js
chunk {0} fd868baa40dab4fc30fd.js 1.76 KiB <{10}> ={1}= ={2}= ={3}= ={7}= ={9}= [recorded] aggressive splitted
@ -498,7 +498,7 @@ Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
0.bundle.js 152 bytes 0 [emitted]
1.bundle.js 289 bytes 1 [emitted]
bundle.js 8.27 KiB 2 [emitted] main
bundle.js 8.29 KiB 2 [emitted] main
3.bundle.js 227 bytes 3 [emitted]
Entrypoint main = bundle.js
chunk {0} 0.bundle.js 22 bytes <{2}> [rendered]
@ -537,7 +537,7 @@ Built at: Thu Jan 01 1970 00:00:00 GMT
0.bundle.js 433 bytes 0 [emitted]
1.bundle.js 297 bytes 1 [emitted]
2.bundle.js 588 bytes 2 [emitted]
bundle.js 8.65 KiB main [emitted] main
bundle.js 8.67 KiB main [emitted] main
Entrypoint main = bundle.js
chunk {main} bundle.js (main) 73 bytes >{0}< >{1}< [entry] [rendered]
> ./index main
@ -615,7 +615,7 @@ exports[`StatsTestCases should print correct stats for commons-chunk-min-size-0
Time: Xms
Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
entry-1.js 6.58 KiB 0 [emitted] entry-1
entry-1.js 6.6 KiB 0 [emitted] entry-1
vendor-1~entry-1.js 314 bytes 1 [emitted] vendor-1~entry-1
Entrypoint entry-1 = vendor-1~entry-1.js entry-1.js
[0] ./entry-1.js 145 bytes {0} [built]
@ -632,7 +632,7 @@ exports[`StatsTestCases should print correct stats for commons-chunk-min-size-In
Time: Xms
Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
entry-1.js 6.58 KiB 0 [emitted] entry-1
entry-1.js 6.6 KiB 0 [emitted] entry-1
vendor-1.js 314 bytes 1 [emitted] vendor-1
Entrypoint entry-1 = vendor-1.js entry-1.js
[0] ./entry-1.js 145 bytes {0} [built]
@ -651,7 +651,7 @@ Child
Time: Xms
Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
app.js 6.67 KiB 0 [emitted] app
app.js 6.69 KiB 0 [emitted] app
vendor.6a3bdffda9f0de672978.js 619 bytes 1 [emitted] vendor
Entrypoint app = vendor.6a3bdffda9f0de672978.js app.js
[./constants.js] 87 bytes {1} [built]
@ -664,7 +664,7 @@ Child
Time: Xms
Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
app.js 6.69 KiB 0 [emitted] app
app.js 6.7 KiB 0 [emitted] app
vendor.6a3bdffda9f0de672978.js 619 bytes 1 [emitted] vendor
Entrypoint app = vendor.6a3bdffda9f0de672978.js app.js
[./constants.js] 87 bytes {1} [built]
@ -985,7 +985,7 @@ Built at: Thu Jan 01 1970 00:00:00 GMT
0.js 305 bytes 0 [emitted]
1.js 314 bytes 1 [emitted]
2.js 308 bytes 2 [emitted]
entry.js 9.06 KiB 3 [emitted] entry
entry.js 9.08 KiB 3 [emitted] entry
Entrypoint entry = entry.js
[0] ./templates/bar.js 38 bytes {0} [optional] [built]
[1] ./templates/baz.js 38 bytes {1} [optional] [built]
@ -1000,7 +1000,7 @@ Time: Xms
Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
0.js 149 bytes 0 [emitted]
entry.js 8.51 KiB 1 [emitted] entry
entry.js 8.53 KiB 1 [emitted] entry
Entrypoint entry = entry.js
[0] ./modules/b.js 22 bytes {0} [built]
[1] ./entry.js 120 bytes {1} [built]
@ -1036,7 +1036,7 @@ Child 1 chunks:
Time: Xms
Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
bundle.js 6.37 KiB 0 [emitted] main
bundle.js 6.39 KiB 0 [emitted] main
Entrypoint main = bundle.js
chunk {0} bundle.js (main) 191 bytes <{0}> >{0}< [entry] [rendered]
[0] ./index.js 73 bytes {0} [built]
@ -1051,7 +1051,7 @@ Child 2 chunks:
Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
0.bundle.js 632 bytes 0 [emitted]
bundle.js 8.26 KiB 1 [emitted] main
bundle.js 8.28 KiB 1 [emitted] main
Entrypoint main = bundle.js
chunk {0} 0.bundle.js 118 bytes <{0}> <{1}> >{0}< [rendered]
[0] ./d.js 22 bytes {0} [built]
@ -1068,7 +1068,7 @@ Child 3 chunks:
Asset Size Chunks Chunk Names
0.bundle.js 494 bytes 0 [emitted]
1.bundle.js 245 bytes 1 [emitted]
bundle.js 8.26 KiB 2 [emitted] main
bundle.js 8.28 KiB 2 [emitted] main
Entrypoint main = bundle.js
chunk {0} 0.bundle.js 74 bytes <{0}> <{2}> >{0}< >{1}< [rendered]
[0] ./d.js 22 bytes {0} [built]
@ -1087,7 +1087,7 @@ Child 4 chunks:
0.bundle.js 236 bytes 0 [emitted]
1.bundle.js 245 bytes 1 [emitted]
2.bundle.js 323 bytes 2 [emitted]
bundle.js 8.26 KiB 3 [emitted] main
bundle.js 8.28 KiB 3 [emitted] main
Entrypoint main = bundle.js
chunk {0} 0.bundle.js 44 bytes <{2}> <{3}> [rendered]
[0] ./d.js 22 bytes {0} [built]
@ -1179,9 +1179,9 @@ exports[`StatsTestCases should print correct stats for module-deduplication 1`]
3.js 661 bytes 3 [emitted]
4.js 661 bytes 4 [emitted]
5.js 661 bytes 5 [emitted]
e1.js 9.4 KiB 6 [emitted] e1
e2.js 9.43 KiB 7 [emitted] e2
e3.js 9.45 KiB 8 [emitted] e3
e1.js 9.42 KiB 6 [emitted] e1
e2.js 9.44 KiB 7 [emitted] e2
e3.js 9.46 KiB 8 [emitted] e3
Entrypoint e1 = e1.js
Entrypoint e2 = e2.js
Entrypoint e3 = e3.js
@ -1225,9 +1225,9 @@ exports[`StatsTestCases should print correct stats for module-deduplication-name
async3.js 818 bytes 0 [emitted] async3
async1.js 818 bytes 1 [emitted] async1
async2.js 818 bytes 2 [emitted] async2
e1.js 9.29 KiB 3 [emitted] e1
e2.js 9.31 KiB 4 [emitted] e2
e3.js 9.33 KiB 5 [emitted] e3
e1.js 9.31 KiB 3 [emitted] e1
e2.js 9.33 KiB 4 [emitted] e2
e3.js 9.35 KiB 5 [emitted] e3
Entrypoint e1 = e1.js
Entrypoint e2 = e2.js
Entrypoint e3 = e3.js
@ -1343,7 +1343,7 @@ exports[`StatsTestCases should print correct stats for named-chunks-plugin 1`] =
Time: Xms
Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
entry.js 6.43 KiB entry [emitted] entry
entry.js 6.45 KiB entry [emitted] entry
vendor.js 269 bytes vendor [emitted] vendor
Entrypoint entry = vendor.js entry.js
[./entry.js] 72 bytes {entry} [built]
@ -1359,7 +1359,7 @@ Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
chunk-containing-__a_js.js 313 bytes chunk-containing-__a_js [emitted]
chunk-containing-__b_js.js 173 bytes chunk-containing-__b_js [emitted]
entry.js 8.17 KiB entry [emitted] entry
entry.js 8.18 KiB entry [emitted] entry
Entrypoint entry = entry.js
[0] ./modules/b.js 22 bytes {chunk-containing-__b_js} [built]
[1] ./modules/a.js 37 bytes {chunk-containing-__a_js} [built]
@ -1397,7 +1397,7 @@ Built at: Thu Jan 01 1970 00:00:00 GMT
ab.js 183 bytes 1 [emitted] ab
abd.js 277 bytes 2, 1 [emitted] abd
cir2.js 299 bytes 3 [emitted] cir2
main.js 9.07 KiB 4 [emitted] main
main.js 9.09 KiB 4 [emitted] main
cir2 from cir1.js 359 bytes 5, 3 [emitted] cir2 from cir1
chunk.js 190 bytes 6, 7 [emitted] chunk
ac in ab.js 130 bytes 7 [emitted] ac in ab
@ -1690,11 +1690,11 @@ For more info visit https://webpack.js.org/guides/code-splitting/</CLR>"
exports[`StatsTestCases should print correct stats for prefetch 1`] = `
" Asset Size Chunks Chunk Names
prefetched.js 467 bytes 0 [emitted] prefetched
prefetched.js 475 bytes 0 [emitted] prefetched
normal.js 130 bytes 1 [emitted] normal
prefetched2.js 127 bytes 2 [emitted] prefetched2
prefetched3.js 130 bytes 3 [emitted] prefetched3
main.js 9.73 KiB 4 [emitted] main
main.js 9.66 KiB 4 [emitted] main
inner.js 136 bytes 5 [emitted] inner
inner2.js 201 bytes 6 [emitted] inner2
Entrypoint main = main.js (prefetch: prefetched2.js prefetched.js prefetched3.js)
@ -1727,7 +1727,7 @@ exports[`StatsTestCases should print correct stats for preload 1`] = `
normal.js 130 bytes 1 [emitted] normal
preloaded2.js 127 bytes 2 [emitted] preloaded2
preloaded3.js 130 bytes 3 [emitted] preloaded3
main.js 9.85 KiB 4 [emitted] main
main.js 9.87 KiB 4 [emitted] main
inner.js 136 bytes 5 [emitted] inner
inner2.js 201 bytes 6 [emitted] inner2
Entrypoint main = main.js (preload: preloaded2.js preloaded.js preloaded3.js)
@ -1747,7 +1747,7 @@ Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
0.js 152 bytes 0 [emitted]
1.js 289 bytes 1 [emitted]
main.js 8.28 KiB 2 [emitted] main
main.js 8.29 KiB 2 [emitted] main
3.js 227 bytes 3 [emitted]
Entrypoint main = main.js
chunk {0} 0.js 22 bytes <{2}> [rendered]
@ -1806,7 +1806,7 @@ Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
0.js 152 bytes 0 [emitted]
1.js 289 bytes 1 [emitted]
main.js 8.28 KiB 2 [emitted] main
main.js 8.29 KiB 2 [emitted] main
3.js 227 bytes 3 [emitted]
Entrypoint main = main.js
[0] ./d.js 22 bytes {3} [built]
@ -1884,7 +1884,7 @@ Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
0.js 152 bytes 0 [emitted]
1.js 289 bytes 1 [emitted]
main.js 8.28 KiB 2 [emitted] main
main.js 8.29 KiB 2 [emitted] main
3.js 227 bytes 3 [emitted]
Entrypoint main = main.js
chunk {0} 0.js 22 bytes <{2}> [rendered]
@ -1975,7 +1975,7 @@ exports[`StatsTestCases should print correct stats for runtime-chunk-integration
Asset Size Chunks Chunk Names
0.js 719 bytes 0 [emitted]
main1.js 542 bytes 1 [emitted] main1
runtime.js 8.73 KiB 2 [emitted] runtime
runtime.js 8.75 KiB 2 [emitted] runtime
Entrypoint main1 = runtime.js main1.js
[0] ./b.js 20 bytes {0} [built]
[1] ./c.js 20 bytes {0} [built]
@ -1984,7 +1984,7 @@ exports[`StatsTestCases should print correct stats for runtime-chunk-integration
Child manifest is named entry:
Asset Size Chunks Chunk Names
0.js 719 bytes 0 [emitted]
manifest.js 9.04 KiB 1 [emitted] manifest
manifest.js 9.06 KiB 1 [emitted] manifest
main1.js 542 bytes 2 [emitted] main1
Entrypoint main1 = manifest.js main1.js
Entrypoint manifest = manifest.js
@ -2090,7 +2090,7 @@ Time: Xms
Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
0.js 481 bytes 0 [emitted]
main.js 9.31 KiB 1 [emitted] main
main.js 9.32 KiB 1 [emitted] main
Entrypoint main = main.js
[0] ./components/src/CompAB/utils.js 97 bytes {1} [built]
harmony side effect evaluation ./utils [1] ./main.js + 1 modules 1:0-30

View File

@ -1,18 +1,10 @@
const FakeDocument = require("../../../helpers/FakeDocument");
beforeEach(() => {
global.document = new FakeDocument();
global.window = {};
});
afterEach(() => {
delete global.document;
delete global.window;
});
it("should be able to load the split chunk on demand", () => {
const promise = import(/* webpackChunkName: "shared" */ "./shared");
const script = document.head._children[0];
expect(script.src).toBe("dep~b~shared.js");
__non_webpack_require__("./dep~b~shared.js");
return promise;
});

View File

@ -0,0 +1,4 @@
export default function() {
import(/* webpackPrefetch: true, webpackChunkName: "chunk1-a" */ "./chunk1-a");
import(/* webpackPreload: true, webpackChunkName: "chunk1-b" */ "./chunk1-b");
}

View File

@ -1,4 +1,3 @@
const FakeDocument = require("../../../helpers/FakeDocument");
let oldNonce;
let oldPublicPath;
@ -6,54 +5,95 @@ let oldPublicPath;
beforeEach(() => {
oldNonce = __webpack_nonce__;
oldPublicPath = __webpack_public_path__;
global.document = new FakeDocument(undefined, false);
global.window = {};
});
afterEach(() => {
delete global.document;
delete global.window;
__webpack_nonce__ = oldNonce;
__webpack_public_path__ = oldPublicPath;
})
it("should prefetch and preload child chunks on chunk load", (done) => {
it("should prefetch and preload child chunks on chunk load", () => {
__webpack_nonce__ = "nonce";
__webpack_public_path__ = "/public/path/";
const promise = import(/* webpackChunkName: "chunk1" */ "./chunk1");
expect(document.head._children).toHaveLength(2);
const script = document.head._children[0];
let link, script;
expect(document.head._children).toHaveLength(1);
// Test prefetch from entry chunk
link = document.head._children[0];
expect(link._type).toBe("link");
expect(link.rel).toBe("prefetch");
expect(link.href).toMatch(/chunk1\.js$/);
const promise = import(/* webpackChunkName: "chunk1", webpackPrefetch: true */ "./chunk1");
expect(document.head._children).toHaveLength(3);
// Test normal script loading
script = document.head._children[1];
expect(script._type).toBe("script");
expect(script.src).toBe("/public/path/chunk1.js")
expect(script.src).toMatch(/chunk1\.js$/);
expect(script.getAttribute("nonce")).toBe("nonce")
expect(script.crossOrigin).toBe("anonymous");
expect(script.onload).toBeTypeOf("function");
let link = document.head._children[1];
// Test preload of chunk1-b
link = document.head._children[2];
expect(link._type).toBe("link");
expect(link.rel).toBe("preload");
expect(link.as).toBe("script");
expect(link.href).toBe("/public/path/chunk1-b.js");
expect(link.href).toMatch(/chunk1-b\.js$/);
expect(link.charset).toBe("utf-8");
expect(link.getAttribute("nonce")).toBe("nonce");
expect(link.crossOrigin).toBe("anonymous");
// Run the script
__non_webpack_require__("./chunk1.js");
script.onload();
return promise.then((ex) => {
expect(document.head._children).toHaveLength(4);
let link = document.head._children[2];
expect(link._type).toBe("link");
expect(link.rel).toBe("prefetch");
expect(link.href).toBe("/public/path/chunk1-c.js");
return promise.then(() => {
expect(document.head._children).toHaveLength(5);
// Test prefetching for chunk1-c and chunk1-a in this order
link = document.head._children[3];
expect(link._type).toBe("link");
expect(link.rel).toBe("prefetch");
expect(link.href).toBe("/public/path/chunk1-a.js");
done();
}, done);
expect(link.href).toMatch(/chunk1-c\.js$/);
expect(link.crossOrigin).toBe("anonymous");
link = document.head._children[4];
expect(link._type).toBe("link");
expect(link.rel).toBe("prefetch");
expect(link.href).toMatch(/chunk1-a\.js$/);
expect(link.crossOrigin).toBe("anonymous");
const promise2 = import(/* webpackChunkName: "chunk1", webpackPrefetch: true */ "./chunk1");
// Loading chunk1 again should not trigger prefetch/preload
expect(document.head._children).toHaveLength(5);
const promise3 = import(/* webpackChunkName: "chunk2" */ "./chunk2");
expect(document.head._children).toHaveLength(6);
// Test normal script loading
script = document.head._children[5];
expect(script._type).toBe("script");
expect(script.src).toMatch(/chunk2\.js$/);
expect(script.getAttribute("nonce")).toBe("nonce")
expect(script.crossOrigin).toBe("anonymous");
expect(script.onload).toBeTypeOf("function");
// Run the script
__non_webpack_require__("./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);
});
});
})

View File

@ -16,9 +16,8 @@ module.exports = class FakeDocument {
};
class FakeElement {
constructor(type, autoload = true) {
constructor(type) {
this._type = type;
this._autoload = autoload;
this._children = [];
this._attributes = Object.create(null);
}
@ -34,33 +33,4 @@ class FakeElement {
getAttribute(name) {
return this._attributes[name];
}
get onload() {
return this._onload;
}
set onload(script) {
if (this._autoload === true && typeof script === "function") {
script();
}
this._onload = script;
}
get src() {
return this._src;
}
set src(src) {
// eslint-disable-next-line no-undef
const publicPath = __webpack_public_path__;
eval(`
const path = require('path');
const fs = require('fs');
const content = fs.readFileSync(
path.join(__dirname, '${src}'.replace('${publicPath}', '')), "utf-8"
)
eval(content);
`);
this._src = src;
}
}