Skip to content

Commit ee25e51

Browse files
authored
feat: add support for link preload/prefetch (#1043)
1 parent 1a56673 commit ee25e51

File tree

33 files changed

+1074
-5
lines changed

33 files changed

+1074
-5
lines changed

Diff for: src/index.js

+123-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const {
1515
compareModulesByIdentifier,
1616
getUndoPath,
1717
BASE_URI,
18+
compileBooleanMatcher,
1819
} = require("./utils");
1920

2021
/** @typedef {import("schema-utils/declarations/validate").Schema} Schema */
@@ -106,6 +107,8 @@ const CODE_GENERATION_RESULT = {
106107
/**
107108
* @typedef {Object} MiniCssExtractPluginCompilationHooks
108109
* @property {import("tapable").SyncWaterfallHook<[string, VarNames], string>} beforeTagInsert
110+
* @property {SyncWaterfallHook<[string, Chunk]>} linkPreload
111+
* @property {SyncWaterfallHook<[string, Chunk]>} linkPrefetch
109112
*/
110113

111114
/**
@@ -527,7 +530,8 @@ class MiniCssExtractPlugin {
527530

528531
/**
529532
* Returns all hooks for the given compilation
530-
* @param {Compilation} compilation
533+
* @param {Compilation} compilation the compilation
534+
* @returns {MiniCssExtractPluginCompilationHooks} hooks
531535
*/
532536
static getCompilationHooks(compilation) {
533537
let hooks = compilationHooksMap.get(compilation);
@@ -538,6 +542,8 @@ class MiniCssExtractPlugin {
538542
["source", "varNames"],
539543
"string"
540544
),
545+
linkPreload: new SyncWaterfallHook(["source", "chunk"]),
546+
linkPrefetch: new SyncWaterfallHook(["source", "chunk"]),
541547
};
542548
compilationHooksMap.set(compilation, hooks);
543549
}
@@ -842,6 +848,20 @@ class MiniCssExtractPlugin {
842848
return obj;
843849
};
844850

851+
/**
852+
* @param {Chunk} chunk chunk
853+
* @param {ChunkGraph} chunkGraph chunk graph
854+
* @returns {boolean} true, when the chunk has css
855+
*/
856+
function chunkHasCss(chunk, chunkGraph) {
857+
// this function replace:
858+
// const chunkHasCss = require("webpack/lib/css/CssModulesPlugin").chunkHasCss;
859+
return !!chunkGraph.getChunkModulesIterableBySourceType(
860+
chunk,
861+
"css/mini-extract"
862+
);
863+
}
864+
845865
class CssLoadingRuntimeModule extends RuntimeModule {
846866
/**
847867
* @param {Set<string>} runtimeRequirements
@@ -855,7 +875,7 @@ class MiniCssExtractPlugin {
855875
}
856876

857877
generate() {
858-
const { chunk, runtimeRequirements } = this;
878+
const { chunkGraph, chunk, runtimeRequirements } = this;
859879
const {
860880
runtimeTemplate,
861881
outputOptions: { crossOriginLoading },
@@ -864,7 +884,6 @@ class MiniCssExtractPlugin {
864884
/** @type {Chunk} */ (chunk),
865885
/** @type {Compilation} */ (this.compilation)
866886
);
867-
868887
const withLoading =
869888
runtimeRequirements.has(RuntimeGlobals.ensureChunkHandlers) &&
870889
Object.keys(chunkMap).length > 0;
@@ -875,6 +894,20 @@ class MiniCssExtractPlugin {
875894
if (!withLoading && !withHmr) {
876895
return "";
877896
}
897+
898+
const conditionMap = /** @type {ChunkGraph} */ (
899+
chunkGraph
900+
).getChunkConditionMap(/** @type {Chunk} */ (chunk), chunkHasCss);
901+
const hasCssMatcher = compileBooleanMatcher(conditionMap);
902+
const withPrefetch = runtimeRequirements.has(
903+
RuntimeGlobals.prefetchChunkHandlers
904+
);
905+
const withPreload = runtimeRequirements.has(
906+
RuntimeGlobals.preloadChunkHandlers
907+
);
908+
const { linkPreload, linkPrefetch } =
909+
MiniCssExtractPlugin.getCompilationHooks(compilation);
910+
878911
return Template.asString([
879912
'if (typeof document === "undefined") return;',
880913
`var createStylesheet = ${runtimeTemplate.basicFunction(
@@ -1089,6 +1122,87 @@ class MiniCssExtractPlugin {
10891122
)}`,
10901123
])
10911124
: "// no hmr",
1125+
"",
1126+
withPrefetch && hasCssMatcher !== false
1127+
? `${
1128+
RuntimeGlobals.prefetchChunkHandlers
1129+
}.miniCss = ${runtimeTemplate.basicFunction("chunkId", [
1130+
`if((!${
1131+
RuntimeGlobals.hasOwnProperty
1132+
}(installedCssChunks, chunkId) || installedCssChunks[chunkId] === undefined) && ${
1133+
hasCssMatcher === true ? "true" : hasCssMatcher("chunkId")
1134+
}) {`,
1135+
Template.indent([
1136+
"installedCssChunks[chunkId] = null;",
1137+
linkPrefetch.call(
1138+
Template.asString([
1139+
"var link = document.createElement('link');",
1140+
crossOriginLoading
1141+
? `link.crossOrigin = ${JSON.stringify(
1142+
crossOriginLoading
1143+
)};`
1144+
: "",
1145+
`if (${RuntimeGlobals.scriptNonce}) {`,
1146+
Template.indent(
1147+
`link.setAttribute("nonce", ${RuntimeGlobals.scriptNonce});`
1148+
),
1149+
"}",
1150+
'link.rel = "prefetch";',
1151+
'link.as = "style";',
1152+
`link.href = ${RuntimeGlobals.publicPath} + ${RuntimeGlobals.require}.miniCssF(chunkId);`,
1153+
]),
1154+
/** @type {Chunk} */ (chunk)
1155+
),
1156+
"document.head.appendChild(link);",
1157+
]),
1158+
"}",
1159+
])};`
1160+
: "// no prefetching",
1161+
"",
1162+
withPreload && hasCssMatcher !== false
1163+
? `${
1164+
RuntimeGlobals.preloadChunkHandlers
1165+
}.miniCss = ${runtimeTemplate.basicFunction("chunkId", [
1166+
`if((!${
1167+
RuntimeGlobals.hasOwnProperty
1168+
}(installedCssChunks, chunkId) || installedCssChunks[chunkId] === undefined) && ${
1169+
hasCssMatcher === true ? "true" : hasCssMatcher("chunkId")
1170+
}) {`,
1171+
Template.indent([
1172+
"installedCssChunks[chunkId] = null;",
1173+
linkPreload.call(
1174+
Template.asString([
1175+
"var link = document.createElement('link');",
1176+
"link.charset = 'utf-8';",
1177+
`if (${RuntimeGlobals.scriptNonce}) {`,
1178+
Template.indent(
1179+
`link.setAttribute("nonce", ${RuntimeGlobals.scriptNonce});`
1180+
),
1181+
"}",
1182+
'link.rel = "preload";',
1183+
'link.as = "style";',
1184+
`link.href = ${RuntimeGlobals.publicPath} + ${RuntimeGlobals.require}.miniCssF(chunkId);`,
1185+
crossOriginLoading
1186+
? crossOriginLoading === "use-credentials"
1187+
? 'link.crossOrigin = "use-credentials";'
1188+
: Template.asString([
1189+
"if (link.href.indexOf(window.location.origin + '/') !== 0) {",
1190+
Template.indent(
1191+
`link.crossOrigin = ${JSON.stringify(
1192+
crossOriginLoading
1193+
)};`
1194+
),
1195+
"}",
1196+
])
1197+
: "",
1198+
]),
1199+
/** @type {Chunk} */ (chunk)
1200+
),
1201+
"document.head.appendChild(link);",
1202+
]),
1203+
"}",
1204+
])};`
1205+
: "// no preloaded",
10921206
]);
10931207
}
10941208
}
@@ -1150,6 +1264,12 @@ class MiniCssExtractPlugin {
11501264
compilation.hooks.runtimeRequirementInTree
11511265
.for(RuntimeGlobals.hmrDownloadUpdateHandlers)
11521266
.tap(pluginName, handler);
1267+
compilation.hooks.runtimeRequirementInTree
1268+
.for(RuntimeGlobals.prefetchChunkHandlers)
1269+
.tap(pluginName, handler);
1270+
compilation.hooks.runtimeRequirementInTree
1271+
.for(RuntimeGlobals.preloadChunkHandlers)
1272+
.tap(pluginName, handler);
11531273
});
11541274
}
11551275

0 commit comments

Comments
 (0)