Skip to content

Commit bc6b5b0

Browse files
authored
Merge pull request webpack#7444 from MLoughry/prefetch-from-entry
Prefetch and preload from entry chunk.
2 parents fe3ca80 + c65d164 commit bc6b5b0

File tree

9 files changed

+178
-139
lines changed

9 files changed

+178
-139
lines changed

lib/Chunk.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -607,10 +607,10 @@ class Chunk {
607607
return result;
608608
}
609609

610-
getChildIdsByOrdersMap() {
610+
getChildIdsByOrdersMap(includeDirectChildren) {
611611
const chunkMaps = Object.create(null);
612612

613-
for (const chunk of this.getAllAsyncChunks()) {
613+
const addChildIdsByOrdersToMap = chunk => {
614614
const data = chunk.getChildIdsByOrders();
615615
for (const key of Object.keys(data)) {
616616
let chunkMap = chunkMaps[key];
@@ -619,7 +619,16 @@ class Chunk {
619619
}
620620
chunkMap[chunk.id] = data[key];
621621
}
622+
};
623+
624+
if (includeDirectChildren) {
625+
addChildIdsByOrdersToMap(this);
622626
}
627+
628+
for (const chunk of this.getAllAsyncChunks()) {
629+
addChildIdsByOrdersToMap(chunk);
630+
}
631+
623632
return chunkMaps;
624633
}
625634

lib/web/JsonpChunkTemplatePlugin.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class JsonpChunkTemplatePlugin {
1414
const jsonpFunction = chunkTemplate.outputOptions.jsonpFunction;
1515
const globalObject = chunkTemplate.outputOptions.globalObject;
1616
const source = new ConcatSource();
17+
const prefetchChunks = chunk.getChildIdsByOrders().prefetch;
1718
source.add(
1819
`(${globalObject}[${JSON.stringify(
1920
jsonpFunction
@@ -31,6 +32,12 @@ class JsonpChunkTemplatePlugin {
3132
);
3233
if (entries.length > 0) {
3334
source.add(`,${JSON.stringify(entries)}`);
35+
} else if (prefetchChunks && prefetchChunks.length) {
36+
source.add(`,0`);
37+
}
38+
39+
if (prefetchChunks && prefetchChunks.length) {
40+
source.add(`,${JSON.stringify(prefetchChunks)}`);
3441
}
3542
source.add("])");
3643
return source;

lib/web/JsonpMainTemplatePlugin.js

Lines changed: 55 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ class JsonpMainTemplatePlugin {
2828
}
2929
return false;
3030
};
31+
const needPrefetchingCode = chunk => {
32+
const allPrefetchChunks = chunk.getChildIdsByOrdersMap(true).prefetch;
33+
return allPrefetchChunks && Object.keys(allPrefetchChunks).length;
34+
};
3135
// TODO refactor this
3236
if (!mainTemplate.hooks.jsonpScript) {
3337
mainTemplate.hooks.jsonpScript = new SyncWaterfallHook([
@@ -232,9 +236,21 @@ class JsonpMainTemplatePlugin {
232236
mainTemplate.hooks.linkPrefetch.tap(
233237
"JsonpMainTemplatePlugin",
234238
(_, chunk, hash) => {
239+
const crossOriginLoading =
240+
mainTemplate.outputOptions.crossOriginLoading;
241+
235242
return Template.asString([
236243
"var link = document.createElement('link');",
244+
crossOriginLoading
245+
? `link.crossOrigin = ${JSON.stringify(crossOriginLoading)};`
246+
: "",
247+
`if (${mainTemplate.requireFn}.nc) {`,
248+
Template.indent(
249+
`link.setAttribute("nonce", ${mainTemplate.requireFn}.nc);`
250+
),
251+
"}",
237252
'link.rel = "prefetch";',
253+
'link.as = "script";',
238254
"link.href = jsonpScriptSrc(chunkId);"
239255
]);
240256
}
@@ -288,7 +304,7 @@ class JsonpMainTemplatePlugin {
288304
"",
289305
"// chunk preloadng for javascript",
290306
"",
291-
`var chunkPreloadMap = ${JSON.stringify(chunkMap, null, "\t")}`,
307+
`var chunkPreloadMap = ${JSON.stringify(chunkMap, null, "\t")};`,
292308
"",
293309
"var chunkPreloadData = chunkPreloadMap[chunkId];",
294310
"if(chunkPreloadData) {",
@@ -310,45 +326,6 @@ class JsonpMainTemplatePlugin {
310326
]);
311327
}
312328
);
313-
mainTemplate.hooks.requireEnsure.tap(
314-
{
315-
name: "JsonpMainTemplatePlugin prefetch",
316-
stage: 20
317-
},
318-
(source, chunk, hash) => {
319-
const chunkMap = chunk.getChildIdsByOrdersMap().prefetch;
320-
if (!chunkMap || Object.keys(chunkMap).length === 0) return source;
321-
return Template.asString([
322-
source,
323-
"",
324-
"// chunk prefetching for javascript",
325-
"",
326-
`var chunkPrefetchMap = ${JSON.stringify(chunkMap, null, "\t")}`,
327-
"",
328-
"var chunkPrefetchData = chunkPrefetchMap[chunkId];",
329-
"if(chunkPrefetchData) {",
330-
Template.indent([
331-
"Promise.all(promises).then(function() {",
332-
Template.indent([
333-
"var head = document.getElementsByTagName('head')[0];",
334-
"chunkPrefetchData.forEach(function(chunkId) {",
335-
Template.indent([
336-
"if(installedChunks[chunkId] === undefined) {",
337-
Template.indent([
338-
"installedChunks[chunkId] = null;",
339-
mainTemplate.hooks.linkPrefetch.call("", chunk, hash),
340-
"head.appendChild(link);"
341-
]),
342-
"}"
343-
]),
344-
"});"
345-
]),
346-
"})"
347-
]),
348-
"}"
349-
]);
350-
}
351-
);
352329
mainTemplate.hooks.requireExtensions.tap(
353330
"JsonpMainTemplatePlugin",
354331
(source, chunk) => {
@@ -369,6 +346,7 @@ class JsonpMainTemplatePlugin {
369346
(source, chunk, hash) => {
370347
if (needChunkLoadingCode(chunk)) {
371348
const withDefer = needEntryDeferringCode(chunk);
349+
const withPrefetch = needPrefetchingCode(chunk);
372350
return Template.asString([
373351
source,
374352
"",
@@ -378,6 +356,7 @@ class JsonpMainTemplatePlugin {
378356
"var chunkIds = data[0];",
379357
"var moreModules = data[1];",
380358
withDefer ? "var executeModules = data[2];" : "",
359+
withPrefetch ? "var prefetchChunks = data[3] || [];" : "",
381360
'// add "moreModules" to the modules object,',
382361
'// then flag all "chunkIds" as loaded and fire callback',
383362
"var moduleId, chunkId, i = 0, resolves = [];",
@@ -405,6 +384,23 @@ class JsonpMainTemplatePlugin {
405384
]),
406385
"}",
407386
"if(parentJsonpFunction) parentJsonpFunction(data);",
387+
withPrefetch
388+
? Template.asString([
389+
"// chunk prefetching for javascript",
390+
"var head = document.getElementsByTagName('head')[0];",
391+
"prefetchChunks.forEach(function(chunkId) {",
392+
Template.indent([
393+
"if(installedChunks[chunkId] === undefined) {",
394+
Template.indent([
395+
"installedChunks[chunkId] = null;",
396+
mainTemplate.hooks.linkPrefetch.call("", chunk, hash),
397+
"head.appendChild(link);"
398+
]),
399+
"}"
400+
]),
401+
"});"
402+
])
403+
: "",
408404
"while(resolves.length) {",
409405
Template.indent("resolves.shift()();"),
410406
"}",
@@ -479,6 +475,25 @@ class JsonpMainTemplatePlugin {
479475
return source;
480476
}
481477
);
478+
mainTemplate.hooks.beforeStartup.tap(
479+
"JsonpMainTemplatePlugin",
480+
(source, chunk, hash) => {
481+
const prefetchChunks = chunk.getChildIdsByOrders().prefetch;
482+
if (
483+
needChunkLoadingCode(chunk) &&
484+
prefetchChunks &&
485+
prefetchChunks.length
486+
) {
487+
return Template.asString([
488+
source,
489+
`webpackJsonpCallback([[], {}, 0, ${JSON.stringify(
490+
prefetchChunks
491+
)}]);`
492+
]);
493+
}
494+
return source;
495+
}
496+
);
482497
mainTemplate.hooks.startup.tap(
483498
"JsonpMainTemplatePlugin",
484499
(source, chunk, hash) => {

test/ConfigTestCases.test.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const vm = require("vm");
77
const mkdirp = require("mkdirp");
88
const rimraf = require("rimraf");
99
const checkArrayExpectation = require("./checkArrayExpectation");
10+
const FakeDocument = require("./helpers/FakeDocument");
1011

1112
const Stats = require("../lib/Stats");
1213
const webpack = require("../lib/webpack");
@@ -176,7 +177,8 @@ describe("ConfigTestCases", () => {
176177
console: console,
177178
expect: expect,
178179
setTimeout: setTimeout,
179-
clearTimeout: clearTimeout
180+
clearTimeout: clearTimeout,
181+
document: new FakeDocument()
180182
};
181183

182184
function _require(currentDirectory, module) {

0 commit comments

Comments
 (0)