Skip to content

Commit

Permalink
Merge pull request #17249 from webpack/fetchPriority
Browse files Browse the repository at this point in the history
feat: added `fetchPriority`
  • Loading branch information
TheLarkInn committed Jun 14, 2023
2 parents f12803b + 3a37335 commit 5ab2935
Show file tree
Hide file tree
Showing 52 changed files with 524 additions and 120 deletions.
5 changes: 3 additions & 2 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
"eval",
"Ewald",
"exitance",
"fetchpriority",
"filebase",
"fileoverview",
"filepath",
Expand All @@ -104,8 +105,8 @@
"hotupdatechunk",
"ident",
"idents",
"IIFE's",
"IIFE",
"IIFE's",
"informations",
"instanceof",
"inversed",
Expand Down Expand Up @@ -274,8 +275,8 @@
"webassembly",
"webassemblyjs",
"webmake",
"webpack's",
"webpack",
"webpack's",
"Xarray",
"Xexports",
"Xfactory",
Expand Down
4 changes: 4 additions & 0 deletions declarations/WebpackOptions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3005,6 +3005,10 @@ export interface JavascriptParserOptions {
* Enable/disable parsing "import { createRequire } from "module"" and evaluating createRequire().
*/
createRequire?: boolean | string;
/**
* Specifies global fetchPriority for dynamic import.
*/
dynamicImportFetchPriority?: "low" | "high" | "auto" | false;
/**
* Specifies global mode for dynamic import.
*/
Expand Down
1 change: 1 addition & 0 deletions lib/ChunkGroup.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const {
* @typedef {Object} RawChunkGroupOptions
* @property {number=} preloadOrder
* @property {number=} prefetchOrder
* @property {("low" | "high" | "auto")=} fetchPriority
*/

/** @typedef {RawChunkGroupOptions & { name?: string }} ChunkGroupOptions */
Expand Down
5 changes: 5 additions & 0 deletions lib/RuntimeGlobals.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,11 @@ exports.createScriptUrl = "__webpack_require__.tu";
*/
exports.getTrustedTypesPolicy = "__webpack_require__.tt";

/**
* a flag when a chunk has a fetch priority
*/
exports.hasFetchPriority = "has fetch priority";

/**
* the chunk name of the chunk with the runtime
*/
Expand Down
3 changes: 2 additions & 1 deletion lib/RuntimePlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -377,9 +377,10 @@ class RuntimePlugin {
if (withCreateScriptUrl) {
set.add(RuntimeGlobals.createScriptUrl);
}
const withFetchPriority = set.has(RuntimeGlobals.hasFetchPriority);
compilation.addRuntimeModule(
chunk,
new LoadScriptRuntimeModule(withCreateScriptUrl)
new LoadScriptRuntimeModule(withCreateScriptUrl, withFetchPriority)
);
return true;
});
Expand Down
22 changes: 20 additions & 2 deletions lib/RuntimeTemplate.js
Original file line number Diff line number Diff line change
Expand Up @@ -939,11 +939,29 @@ class RuntimeTemplate {
if (chunks.length === 1) {
const chunkId = JSON.stringify(chunks[0].id);
runtimeRequirements.add(RuntimeGlobals.ensureChunk);
return `${RuntimeGlobals.ensureChunk}(${comment}${chunkId})`;

const fetchPriority = chunkGroup.options.fetchPriority;

if (fetchPriority) {
runtimeRequirements.add(RuntimeGlobals.hasFetchPriority);
}

return `${RuntimeGlobals.ensureChunk}(${comment}${chunkId}${
fetchPriority ? `, ${JSON.stringify(fetchPriority)}` : ""
})`;
} else if (chunks.length > 0) {
runtimeRequirements.add(RuntimeGlobals.ensureChunk);

const fetchPriority = chunkGroup.options.fetchPriority;

if (fetchPriority) {
runtimeRequirements.add(RuntimeGlobals.hasFetchPriority);
}

const requireChunkId = chunk =>
`${RuntimeGlobals.ensureChunk}(${JSON.stringify(chunk.id)})`;
`${RuntimeGlobals.ensureChunk}(${JSON.stringify(chunk.id)}${
fetchPriority ? `, ${JSON.stringify(fetchPriority)}` : ""
})`;
return `Promise.all(${comment.trim()}[${chunks
.map(requireChunkId)
.join(", ")}])`;
Expand Down
1 change: 1 addition & 0 deletions lib/config/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,7 @@ const applyJavascriptParserOptionsDefaults = (
D(parserOptions, "dynamicImportMode", "lazy");
D(parserOptions, "dynamicImportPrefetch", false);
D(parserOptions, "dynamicImportPreload", false);
D(parserOptions, "dynamicImportFetchPriority", false);
D(parserOptions, "createRequire", isNode);
if (futureDefaults) D(parserOptions, "exportsPresence", "error");
};
Expand Down
19 changes: 19 additions & 0 deletions lib/dependencies/ImportMetaContextDependencyParserPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,25 @@ module.exports = class ImportMetaContextDependencyParserPlugin {
}
break;
}
case "fetchPriority": {
const expr = parser.evaluateExpression(
/** @type {Expression} */ (prop.value)
);
if (
expr.isString() &&
["high", "low", "auto"].includes(expr.string)
) {
groupOptions.fetchPriority =
/** @type {RawChunkGroupOptions["fetchPriority"]} */ (
expr.string
);
} else {
errors.push(
createPropertyParseError(prop, '"high"|"low"|"auto"')
);
}
break;
}
case "recursive": {
const recursiveExpr = parser.evaluateExpression(
/** @type {Expression} */ (prop.value)
Expand Down
26 changes: 25 additions & 1 deletion lib/dependencies/ImportParserPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ class ImportParserPlugin {
/** @type {RawChunkGroupOptions} */
const groupOptions = {};

const { dynamicImportPreload, dynamicImportPrefetch } = this.options;
const {
dynamicImportPreload,
dynamicImportPrefetch,
dynamicImportFetchPriority
} = this.options;
if (dynamicImportPreload !== undefined && dynamicImportPreload !== false)
groupOptions.preloadOrder =
dynamicImportPreload === true ? 0 : dynamicImportPreload;
Expand All @@ -57,6 +61,11 @@ class ImportParserPlugin {
)
groupOptions.prefetchOrder =
dynamicImportPrefetch === true ? 0 : dynamicImportPrefetch;
if (
dynamicImportFetchPriority !== undefined &&
dynamicImportFetchPriority !== false
)
groupOptions.fetchPriority = dynamicImportFetchPriority;

const { options: importOptions, errors: commentErrors } =
parser.parseCommentOptions(expr.range);
Expand Down Expand Up @@ -141,6 +150,21 @@ class ImportParserPlugin {
);
}
}
if (importOptions.webpackFetchPriority !== undefined) {
if (
typeof importOptions.webpackFetchPriority === "string" &&
["high", "low", "auto"].includes(importOptions.webpackFetchPriority)
) {
groupOptions.fetchPriority = importOptions.webpackFetchPriority;
} else {
parser.state.module.addWarning(
new UnsupportedFeatureWarning(
`\`webpackFetchPriority\` expected true or "low", "high" or "auto", but received: ${importOptions.webpackFetchPriority}.`,
expr.loc
)
);
}
}
if (importOptions.webpackInclude !== undefined) {
if (
!importOptions.webpackInclude ||
Expand Down
4 changes: 3 additions & 1 deletion lib/prefetch/ChunkPrefetchPreloadPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const ChunkPrefetchStartupRuntimeModule = require("./ChunkPrefetchStartupRuntime
const ChunkPrefetchTriggerRuntimeModule = require("./ChunkPrefetchTriggerRuntimeModule");
const ChunkPreloadTriggerRuntimeModule = require("./ChunkPreloadTriggerRuntimeModule");

/** @typedef {import("../Chunk")} Chunk */
/** @typedef {import("../ChunkGroup").RawChunkGroupOptions} RawChunkGroupOptions */
/** @typedef {import("../Compiler")} Compiler */

class ChunkPrefetchPreloadPlugin {
Expand Down Expand Up @@ -43,7 +45,7 @@ class ChunkPrefetchPreloadPlugin {
compilation.hooks.additionalTreeRuntimeRequirements.tap(
"ChunkPrefetchPreloadPlugin",
(chunk, set, { chunkGraph }) => {
const chunkMap = chunk.getChildIdsByOrdersMap(chunkGraph, false);
const chunkMap = chunk.getChildIdsByOrdersMap(chunkGraph);

if (chunkMap.prefetch) {
set.add(RuntimeGlobals.prefetchChunk);
Expand Down
12 changes: 10 additions & 2 deletions lib/runtime/EnsureChunkRuntimeModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,25 @@ class EnsureChunkRuntimeModule extends RuntimeModule {
const { runtimeTemplate } = this.compilation;
// Check if there are non initial chunks which need to be imported using require-ensure
if (this.runtimeRequirements.has(RuntimeGlobals.ensureChunkHandlers)) {
const withFetchPriority = this.runtimeRequirements.has(
RuntimeGlobals.hasFetchPriority
);
const handlers = RuntimeGlobals.ensureChunkHandlers;
return Template.asString([
`${handlers} = {};`,
"// This file contains only the entry chunk.",
"// The chunk loading function for additional chunks",
`${RuntimeGlobals.ensureChunk} = ${runtimeTemplate.basicFunction(
"chunkId",
`chunkId${withFetchPriority ? ", fetchPriority" : ""}`,
[
`return Promise.all(Object.keys(${handlers}).reduce(${runtimeTemplate.basicFunction(
"promises, key",
[`${handlers}[key](chunkId, promises);`, "return promises;"]
[
`${handlers}[key](chunkId, promises${
withFetchPriority ? ", fetchPriority" : ""
});`,
"return promises;"
]
)}, []));`
]
)};`
Expand Down
108 changes: 62 additions & 46 deletions lib/runtime/LoadScriptRuntimeModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,12 @@ class LoadScriptRuntimeModule extends HelperRuntimeModule {

/**
* @param {boolean=} withCreateScriptUrl use create script url for trusted types
* @param {boolean=} withFetchPriority use `fetchPriority` attribute
*/
constructor(withCreateScriptUrl) {
constructor(withCreateScriptUrl, withFetchPriority) {
super("load script");
this._withCreateScriptUrl = withCreateScriptUrl;
this._withFetchPriority = withFetchPriority;
}

/**
Expand Down Expand Up @@ -81,6 +83,15 @@ class LoadScriptRuntimeModule extends HelperRuntimeModule {
uniqueName
? 'script.setAttribute("data-webpack", dataWebpackPrefix + key);'
: "",
this._withFetchPriority
? Template.asString([
"if(fetchPriority) {",
Template.indent(
'script.setAttribute("fetchpriority", fetchPriority);'
),
"}"
])
: "",
`script.src = ${
this._withCreateScriptUrl
? `${RuntimeGlobals.createScriptUrl}(url)`
Expand All @@ -105,53 +116,58 @@ class LoadScriptRuntimeModule extends HelperRuntimeModule {
? `var dataWebpackPrefix = ${JSON.stringify(uniqueName + ":")};`
: "// data-webpack is not used as build has no uniqueName",
"// loadScript function to load a script via script tag",
`${fn} = ${runtimeTemplate.basicFunction("url, done, key, chunkId", [
"if(inProgress[url]) { inProgress[url].push(done); return; }",
"var script, needAttach;",
"if(key !== undefined) {",
Template.indent([
'var scripts = document.getElementsByTagName("script");',
"for(var i = 0; i < scripts.length; i++) {",
`${fn} = ${runtimeTemplate.basicFunction(
`url, done, key, chunkId${
this._withFetchPriority ? ", fetchPriority" : ""
}`,
[
"if(inProgress[url]) { inProgress[url].push(done); return; }",
"var script, needAttach;",
"if(key !== undefined) {",
Template.indent([
'var scripts = document.getElementsByTagName("script");',
"for(var i = 0; i < scripts.length; i++) {",
Template.indent([
"var s = scripts[i];",
`if(s.getAttribute("src") == url${
uniqueName
? ' || s.getAttribute("data-webpack") == dataWebpackPrefix + key'
: ""
}) { script = s; break; }`
]),
"}"
]),
"}",
"if(!script) {",
Template.indent([
"var s = scripts[i];",
`if(s.getAttribute("src") == url${
uniqueName
? ' || s.getAttribute("data-webpack") == dataWebpackPrefix + key'
: ""
}) { script = s; break; }`
"needAttach = true;",
createScript.call(code, this.chunk)
]),
"}"
]),
"}",
"if(!script) {",
Template.indent([
"needAttach = true;",
createScript.call(code, this.chunk)
]),
"}",
"inProgress[url] = [done];",
"var onScriptComplete = " +
runtimeTemplate.basicFunction(
"prev, event",
Template.asString([
"// avoid mem leaks in IE.",
"script.onerror = script.onload = null;",
"clearTimeout(timeout);",
"var doneFns = inProgress[url];",
"delete inProgress[url];",
"script.parentNode && script.parentNode.removeChild(script);",
`doneFns && doneFns.forEach(${runtimeTemplate.returningFunction(
"fn(event)",
"fn"
)});`,
"if(prev) return prev(event);"
])
),
`var timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), ${loadTimeout});`,
"script.onerror = onScriptComplete.bind(null, script.onerror);",
"script.onload = onScriptComplete.bind(null, script.onload);",
"needAttach && document.head.appendChild(script);"
])};`
"}",
"inProgress[url] = [done];",
"var onScriptComplete = " +
runtimeTemplate.basicFunction(
"prev, event",
Template.asString([
"// avoid mem leaks in IE.",
"script.onerror = script.onload = null;",
"clearTimeout(timeout);",
"var doneFns = inProgress[url];",
"delete inProgress[url];",
"script.parentNode && script.parentNode.removeChild(script);",
`doneFns && doneFns.forEach(${runtimeTemplate.returningFunction(
"fn(event)",
"fn"
)});`,
"if(prev) return prev(event);"
])
),
`var timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), ${loadTimeout});`,
"script.onerror = onScriptComplete.bind(null, script.onerror);",
"script.onload = onScriptComplete.bind(null, script.onload);",
"needAttach && document.head.appendChild(script);"
]
)};`
]);
}
}
Expand Down
11 changes: 9 additions & 2 deletions lib/web/JsonpChunkLoadingRuntimeModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ class JsonpChunkLoadingRuntimeModule extends RuntimeModule {
const withPreload = this._runtimeRequirements.has(
RuntimeGlobals.preloadChunkHandlers
);
const withFetchPriority = this._runtimeRequirements.has(
RuntimeGlobals.hasFetchPriority
);
const chunkLoadingGlobalExpr = `${globalObject}[${JSON.stringify(
chunkLoadingGlobal
)}]`;
Expand Down Expand Up @@ -138,7 +141,7 @@ class JsonpChunkLoadingRuntimeModule extends RuntimeModule {
withLoading
? Template.asString([
`${fn}.j = ${runtimeTemplate.basicFunction(
"chunkId, promises",
`chunkId, promises${withFetchPriority ? ", fetchPriority" : ""}`,
hasJsMatcher !== false
? Template.indent([
"// JSONP chunk loading for javascript",
Expand Down Expand Up @@ -190,7 +193,11 @@ class JsonpChunkLoadingRuntimeModule extends RuntimeModule {
"}"
]
)};`,
`${RuntimeGlobals.loadScript}(url, loadingEnded, "chunk-" + chunkId, chunkId);`
`${
RuntimeGlobals.loadScript
}(url, loadingEnded, "chunk-" + chunkId, chunkId${
withFetchPriority ? ", fetchPriority" : ""
});`
]),
hasJsMatcher === true
? "}"
Expand Down

0 comments on commit 5ab2935

Please sign in to comment.