mirror of https://github.com/webpack/webpack.git
refactor: simplify asset prefetch/preload implementation
- Consolidate multiple runtime modules into unified AssetResourcePrefetchPlugin - Remove complex startup prefetch mechanism in favor of simpler inline approach
This commit is contained in:
parent
a47f4443d1
commit
e97ae49459
|
@ -34,6 +34,8 @@ const WebpackIsIncludedPlugin = require("./WebpackIsIncludedPlugin");
|
||||||
|
|
||||||
const AssetModulesPlugin = require("./asset/AssetModulesPlugin");
|
const AssetModulesPlugin = require("./asset/AssetModulesPlugin");
|
||||||
|
|
||||||
|
const AssetResourcePrefetchPlugin = require("./asset/AssetResourcePrefetchPlugin");
|
||||||
|
|
||||||
const InferAsyncModulesPlugin = require("./async-modules/InferAsyncModulesPlugin");
|
const InferAsyncModulesPlugin = require("./async-modules/InferAsyncModulesPlugin");
|
||||||
|
|
||||||
const ResolverCachePlugin = require("./cache/ResolverCachePlugin");
|
const ResolverCachePlugin = require("./cache/ResolverCachePlugin");
|
||||||
|
@ -62,9 +64,7 @@ const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin");
|
||||||
|
|
||||||
const JsonModulesPlugin = require("./json/JsonModulesPlugin");
|
const JsonModulesPlugin = require("./json/JsonModulesPlugin");
|
||||||
|
|
||||||
const AssetPrefetchStartupPlugin = require("./prefetch/AssetPrefetchStartupPlugin");
|
|
||||||
const ChunkPrefetchPreloadPlugin = require("./prefetch/ChunkPrefetchPreloadPlugin");
|
const ChunkPrefetchPreloadPlugin = require("./prefetch/ChunkPrefetchPreloadPlugin");
|
||||||
const AssetPrefetchPreloadPlugin = require("./runtime/AssetPrefetchPreloadPlugin");
|
|
||||||
|
|
||||||
const DataUriPlugin = require("./schemes/DataUriPlugin");
|
const DataUriPlugin = require("./schemes/DataUriPlugin");
|
||||||
const FileUriPlugin = require("./schemes/FileUriPlugin");
|
const FileUriPlugin = require("./schemes/FileUriPlugin");
|
||||||
|
@ -225,8 +225,7 @@ class WebpackOptionsApply extends OptionsApply {
|
||||||
}
|
}
|
||||||
|
|
||||||
new ChunkPrefetchPreloadPlugin().apply(compiler);
|
new ChunkPrefetchPreloadPlugin().apply(compiler);
|
||||||
new AssetPrefetchPreloadPlugin().apply(compiler);
|
new AssetResourcePrefetchPlugin().apply(compiler);
|
||||||
new AssetPrefetchStartupPlugin().apply(compiler);
|
|
||||||
|
|
||||||
if (typeof options.output.chunkFormat === "string") {
|
if (typeof options.output.chunkFormat === "string") {
|
||||||
switch (options.output.chunkFormat) {
|
switch (options.output.chunkFormat) {
|
||||||
|
|
|
@ -6,40 +6,50 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const RuntimeGlobals = require("../RuntimeGlobals");
|
const RuntimeGlobals = require("../RuntimeGlobals");
|
||||||
const AssetPrefetchPreloadRuntimeModule = require("./AssetPrefetchPreloadRuntimeModule");
|
const AssetResourcePrefetchRuntimeModule = require("./AssetResourcePrefetchRuntimeModule");
|
||||||
|
|
||||||
/** @typedef {import("../Compiler")} Compiler */
|
/** @typedef {import("../Compiler")} Compiler */
|
||||||
|
|
||||||
const PLUGIN_NAME = "AssetPrefetchPreloadPlugin";
|
const PLUGIN_NAME = "AssetResourcePrefetchPlugin";
|
||||||
|
|
||||||
class AssetPrefetchPreloadPlugin {
|
class AssetResourcePrefetchPlugin {
|
||||||
/**
|
/**
|
||||||
* @param {Compiler} compiler the compiler
|
* @param {Compiler} compiler the compiler
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
apply(compiler) {
|
apply(compiler) {
|
||||||
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
|
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
|
||||||
// Register runtime module for asset prefetch
|
// prefetchAsset
|
||||||
compilation.hooks.runtimeRequirementInTree
|
compilation.hooks.runtimeRequirementInTree
|
||||||
.for(RuntimeGlobals.prefetchAsset)
|
.for(RuntimeGlobals.prefetchAsset)
|
||||||
.tap(PLUGIN_NAME, (chunk, set) => {
|
.tap(PLUGIN_NAME, (chunk, set) => {
|
||||||
|
set.add(RuntimeGlobals.publicPath);
|
||||||
|
set.add(RuntimeGlobals.require);
|
||||||
|
set.add(RuntimeGlobals.baseURI);
|
||||||
|
set.add(RuntimeGlobals.relativeUrl);
|
||||||
compilation.addRuntimeModule(
|
compilation.addRuntimeModule(
|
||||||
chunk,
|
chunk,
|
||||||
new AssetPrefetchPreloadRuntimeModule("prefetch")
|
new AssetResourcePrefetchRuntimeModule("prefetch")
|
||||||
);
|
);
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Register runtime module for asset preload
|
// preloadAsset
|
||||||
compilation.hooks.runtimeRequirementInTree
|
compilation.hooks.runtimeRequirementInTree
|
||||||
.for(RuntimeGlobals.preloadAsset)
|
.for(RuntimeGlobals.preloadAsset)
|
||||||
.tap(PLUGIN_NAME, (chunk, set) => {
|
.tap(PLUGIN_NAME, (chunk, set) => {
|
||||||
|
set.add(RuntimeGlobals.publicPath);
|
||||||
|
set.add(RuntimeGlobals.require);
|
||||||
|
set.add(RuntimeGlobals.baseURI);
|
||||||
|
set.add(RuntimeGlobals.relativeUrl);
|
||||||
compilation.addRuntimeModule(
|
compilation.addRuntimeModule(
|
||||||
chunk,
|
chunk,
|
||||||
new AssetPrefetchPreloadRuntimeModule("preload")
|
new AssetResourcePrefetchRuntimeModule("preload")
|
||||||
);
|
);
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = AssetPrefetchPreloadPlugin;
|
module.exports = AssetResourcePrefetchPlugin;
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||||
|
Author Tobias Koppers @sokra
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const RuntimeGlobals = require("../RuntimeGlobals");
|
||||||
|
const RuntimeModule = require("../RuntimeModule");
|
||||||
|
const Template = require("../Template");
|
||||||
|
|
||||||
|
/** @typedef {import("../Compilation")} Compilation */
|
||||||
|
|
||||||
|
class AssetResourcePrefetchRuntimeModule extends RuntimeModule {
|
||||||
|
/**
|
||||||
|
* @param {string} type "prefetch" or "preload"
|
||||||
|
*/
|
||||||
|
constructor(type) {
|
||||||
|
super(`asset ${type}`, RuntimeModule.STAGE_ATTACH);
|
||||||
|
this._type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {string | null} runtime code
|
||||||
|
*/
|
||||||
|
generate() {
|
||||||
|
const { compilation } = this;
|
||||||
|
if (!compilation) return null;
|
||||||
|
|
||||||
|
const { runtimeTemplate, outputOptions } = compilation;
|
||||||
|
const fnName =
|
||||||
|
this._type === "prefetch"
|
||||||
|
? RuntimeGlobals.prefetchAsset
|
||||||
|
: RuntimeGlobals.preloadAsset;
|
||||||
|
|
||||||
|
const crossOriginLoading = outputOptions.crossOriginLoading;
|
||||||
|
|
||||||
|
return Template.asString([
|
||||||
|
`${fnName} = ${runtimeTemplate.basicFunction(
|
||||||
|
"moduleId, as, fetchPriority, relative",
|
||||||
|
[
|
||||||
|
"var url;",
|
||||||
|
"if (relative) {",
|
||||||
|
Template.indent([
|
||||||
|
`url = new ${RuntimeGlobals.relativeUrl}(${RuntimeGlobals.require}(moduleId));`
|
||||||
|
]),
|
||||||
|
"} else {",
|
||||||
|
Template.indent([
|
||||||
|
`url = new URL(${RuntimeGlobals.require}(moduleId), ${RuntimeGlobals.baseURI});`
|
||||||
|
]),
|
||||||
|
"}",
|
||||||
|
"",
|
||||||
|
"var link = document.createElement('link');",
|
||||||
|
`link.rel = '${this._type}';`,
|
||||||
|
"if (as) link.as = as;",
|
||||||
|
"link.href = url.href;",
|
||||||
|
"",
|
||||||
|
"if (fetchPriority) {",
|
||||||
|
Template.indent([
|
||||||
|
"link.fetchPriority = fetchPriority;",
|
||||||
|
"link.setAttribute('fetchpriority', fetchPriority);"
|
||||||
|
]),
|
||||||
|
"}",
|
||||||
|
"",
|
||||||
|
crossOriginLoading
|
||||||
|
? Template.asString([
|
||||||
|
"if (link.href.indexOf(window.location.origin + '/') !== 0) {",
|
||||||
|
Template.indent([
|
||||||
|
`link.crossOrigin = ${JSON.stringify(crossOriginLoading)};`
|
||||||
|
]),
|
||||||
|
"}"
|
||||||
|
])
|
||||||
|
: "",
|
||||||
|
"",
|
||||||
|
"document.head.appendChild(link);"
|
||||||
|
]
|
||||||
|
)};`
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = AssetResourcePrefetchRuntimeModule;
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
const InitFragment = require("../InitFragment");
|
||||||
const RuntimeGlobals = require("../RuntimeGlobals");
|
const RuntimeGlobals = require("../RuntimeGlobals");
|
||||||
const RawDataUrlModule = require("../asset/RawDataUrlModule");
|
const RawDataUrlModule = require("../asset/RawDataUrlModule");
|
||||||
const {
|
const {
|
||||||
|
@ -49,18 +50,9 @@ class URLDependency extends ModuleDependency {
|
||||||
this.relative = relative || false;
|
this.relative = relative || false;
|
||||||
/** @type {Set<string> | boolean | undefined} */
|
/** @type {Set<string> | boolean | undefined} */
|
||||||
this.usedByExports = undefined;
|
this.usedByExports = undefined;
|
||||||
/** @type {boolean | undefined} */
|
|
||||||
this._startupPrefetch = undefined;
|
|
||||||
/** @type {boolean | undefined} */
|
|
||||||
this.prefetch = undefined;
|
this.prefetch = undefined;
|
||||||
/** @type {boolean | undefined} */
|
|
||||||
this.preload = undefined;
|
this.preload = undefined;
|
||||||
/** @type {string | undefined} */
|
|
||||||
this.fetchPriority = undefined;
|
this.fetchPriority = undefined;
|
||||||
/** @type {string | undefined} */
|
|
||||||
this.preloadAs = undefined;
|
|
||||||
/** @type {string | undefined} */
|
|
||||||
this.preloadType = undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get type() {
|
get type() {
|
||||||
|
@ -102,8 +94,6 @@ class URLDependency extends ModuleDependency {
|
||||||
write(this.prefetch);
|
write(this.prefetch);
|
||||||
write(this.preload);
|
write(this.preload);
|
||||||
write(this.fetchPriority);
|
write(this.fetchPriority);
|
||||||
write(this.preloadAs);
|
|
||||||
write(this.preloadType);
|
|
||||||
super.serialize(context);
|
super.serialize(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,8 +108,6 @@ class URLDependency extends ModuleDependency {
|
||||||
this.prefetch = read();
|
this.prefetch = read();
|
||||||
this.preload = read();
|
this.preload = read();
|
||||||
this.fetchPriority = read();
|
this.fetchPriority = read();
|
||||||
this.preloadAs = read();
|
|
||||||
this.preloadType = read();
|
|
||||||
super.deserialize(context);
|
super.deserialize(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,9 +127,12 @@ URLDependency.Template = class URLDependencyTemplate extends (
|
||||||
moduleGraph,
|
moduleGraph,
|
||||||
runtimeRequirements,
|
runtimeRequirements,
|
||||||
runtimeTemplate,
|
runtimeTemplate,
|
||||||
runtime
|
runtime,
|
||||||
|
initFragments
|
||||||
} = templateContext;
|
} = templateContext;
|
||||||
const dep = /** @type {URLDependency} */ (dependency);
|
const dep = /** @type {URLDependency} */ (dependency);
|
||||||
|
|
||||||
|
const module = moduleGraph.getModule(dep);
|
||||||
const connection = moduleGraph.getConnection(dep);
|
const connection = moduleGraph.getConnection(dep);
|
||||||
// Skip rendering depending when dependency is conditional
|
// Skip rendering depending when dependency is conditional
|
||||||
if (connection && !connection.isTargetActive(runtime)) {
|
if (connection && !connection.isTargetActive(runtime)) {
|
||||||
|
@ -153,153 +144,80 @@ URLDependency.Template = class URLDependencyTemplate extends (
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
runtimeRequirements.add(RuntimeGlobals.require);
|
// Standard URL generation
|
||||||
|
if (dep.relative) {
|
||||||
// Determine if prefetch/preload hints are specified
|
runtimeRequirements.add(RuntimeGlobals.relativeUrl);
|
||||||
const needsPrefetch = dep.prefetch !== undefined && dep.prefetch !== false;
|
source.replace(
|
||||||
const needsPreload = dep.preload !== undefined && dep.preload !== false;
|
dep.outerRange[0],
|
||||||
|
dep.outerRange[1] - 1,
|
||||||
// Generate inline prefetch/preload code if not handled by startup module
|
`/* asset import */ new ${RuntimeGlobals.relativeUrl}(${runtimeTemplate.moduleRaw(
|
||||||
if ((needsPrefetch || needsPreload) && !dep._startupPrefetch) {
|
{
|
||||||
// Resolve module to determine appropriate asset type
|
|
||||||
const module = moduleGraph.getModule(dep);
|
|
||||||
let asType = "";
|
|
||||||
|
|
||||||
if (module) {
|
|
||||||
const request = /** @type {string} */ (
|
|
||||||
/** @type {{ request?: string }} */ (module).request || ""
|
|
||||||
);
|
|
||||||
asType = getAssetType(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the module ID for runtime code generation
|
|
||||||
const moduleExpr = runtimeTemplate.moduleRaw({
|
|
||||||
chunkGraph,
|
chunkGraph,
|
||||||
module: moduleGraph.getModule(dep),
|
module,
|
||||||
request: dep.request,
|
request: dep.request,
|
||||||
runtimeRequirements,
|
runtimeRequirements,
|
||||||
weak: false
|
weak: false
|
||||||
|
}
|
||||||
|
)})`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
runtimeRequirements.add(RuntimeGlobals.baseURI);
|
||||||
|
source.replace(
|
||||||
|
dep.range[0],
|
||||||
|
dep.range[1] - 1,
|
||||||
|
`/* asset import */ ${runtimeTemplate.moduleRaw({
|
||||||
|
chunkGraph,
|
||||||
|
module,
|
||||||
|
request: dep.request,
|
||||||
|
runtimeRequirements,
|
||||||
|
weak: false
|
||||||
|
})}, ${RuntimeGlobals.baseURI}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefetch/Preload via InitFragment
|
||||||
|
if ((dep.prefetch || dep.preload) && module) {
|
||||||
|
const request = dep.request;
|
||||||
|
const assetType = getAssetType(request);
|
||||||
|
const id = chunkGraph.getModuleId(module);
|
||||||
|
if (id !== null) {
|
||||||
|
const moduleId = runtimeTemplate.moduleId({
|
||||||
|
module,
|
||||||
|
chunkGraph,
|
||||||
|
request: dep.request,
|
||||||
|
weak: false
|
||||||
});
|
});
|
||||||
|
|
||||||
// Construct prefetch/preload function calls
|
if (dep.preload) {
|
||||||
const hintCode = [];
|
|
||||||
// Validate fetchPriority against allowed values
|
|
||||||
const validFetchPriority =
|
|
||||||
dep.fetchPriority && ["high", "low", "auto"].includes(dep.fetchPriority)
|
|
||||||
? dep.fetchPriority
|
|
||||||
: undefined;
|
|
||||||
const fetchPriority = validFetchPriority
|
|
||||||
? `"${validFetchPriority}"`
|
|
||||||
: "undefined";
|
|
||||||
const preloadType = dep.preloadType
|
|
||||||
? `"${dep.preloadType}"`
|
|
||||||
: "undefined";
|
|
||||||
|
|
||||||
if (needsPrefetch && !needsPreload) {
|
|
||||||
// Generate prefetch call
|
|
||||||
runtimeRequirements.add(RuntimeGlobals.prefetchAsset);
|
|
||||||
hintCode.push(
|
|
||||||
`${RuntimeGlobals.prefetchAsset}(url, "${asType}", ${fetchPriority}, ${preloadType});`
|
|
||||||
);
|
|
||||||
} else if (needsPreload) {
|
|
||||||
// Generate preload call (overrides prefetch if both specified)
|
|
||||||
runtimeRequirements.add(RuntimeGlobals.preloadAsset);
|
runtimeRequirements.add(RuntimeGlobals.preloadAsset);
|
||||||
hintCode.push(
|
initFragments.push(
|
||||||
`${RuntimeGlobals.preloadAsset}(url, "${asType}", ${fetchPriority}, ${preloadType});`
|
new InitFragment(
|
||||||
|
`${RuntimeGlobals.preloadAsset}(${moduleId}, ${JSON.stringify(
|
||||||
|
assetType
|
||||||
|
)}${dep.fetchPriority ? `, ${JSON.stringify(dep.fetchPriority)}` : ""}, ${
|
||||||
|
dep.relative
|
||||||
|
});\n`,
|
||||||
|
InitFragment.STAGE_CONSTANTS,
|
||||||
|
-10,
|
||||||
|
`asset_preload_${moduleId}`
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
} else if (dep.prefetch) {
|
||||||
|
|
||||||
// Create IIFE that generates URL and adds resource hints
|
|
||||||
if (dep.relative) {
|
|
||||||
runtimeRequirements.add(RuntimeGlobals.relativeUrl);
|
|
||||||
source.replace(
|
|
||||||
dep.outerRange[0],
|
|
||||||
dep.outerRange[1] - 1,
|
|
||||||
`/* asset import */ (function() {
|
|
||||||
var url = new ${RuntimeGlobals.relativeUrl}(${moduleExpr});
|
|
||||||
${hintCode.join("\n")}
|
|
||||||
return url;
|
|
||||||
})()`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
runtimeRequirements.add(RuntimeGlobals.baseURI);
|
|
||||||
source.replace(
|
|
||||||
dep.range[0],
|
|
||||||
dep.range[1] - 1,
|
|
||||||
`/* asset import */ (function() {
|
|
||||||
var url = new URL(${moduleExpr}, ${RuntimeGlobals.baseURI});
|
|
||||||
${hintCode.join("\n")}
|
|
||||||
return url;
|
|
||||||
})(), ${RuntimeGlobals.baseURI}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if ((needsPrefetch || needsPreload) && dep._startupPrefetch) {
|
|
||||||
// Generate standard URL when prefetch/preload is handled by startup module
|
|
||||||
if (dep.relative) {
|
|
||||||
runtimeRequirements.add(RuntimeGlobals.relativeUrl);
|
|
||||||
source.replace(
|
|
||||||
dep.outerRange[0],
|
|
||||||
dep.outerRange[1] - 1,
|
|
||||||
`/* asset import */ new ${
|
|
||||||
RuntimeGlobals.relativeUrl
|
|
||||||
}(${runtimeTemplate.moduleRaw({
|
|
||||||
chunkGraph,
|
|
||||||
module: moduleGraph.getModule(dep),
|
|
||||||
request: dep.request,
|
|
||||||
runtimeRequirements,
|
|
||||||
weak: false
|
|
||||||
})})`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
runtimeRequirements.add(RuntimeGlobals.baseURI);
|
|
||||||
source.replace(
|
|
||||||
dep.range[0],
|
|
||||||
dep.range[1] - 1,
|
|
||||||
`/* asset import */ ${runtimeTemplate.moduleRaw({
|
|
||||||
chunkGraph,
|
|
||||||
module: moduleGraph.getModule(dep),
|
|
||||||
request: dep.request,
|
|
||||||
runtimeRequirements,
|
|
||||||
weak: false
|
|
||||||
})}, ${RuntimeGlobals.baseURI}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Register runtime requirements for prefetch/preload functions
|
|
||||||
if (needsPrefetch && !needsPreload) {
|
|
||||||
runtimeRequirements.add(RuntimeGlobals.prefetchAsset);
|
runtimeRequirements.add(RuntimeGlobals.prefetchAsset);
|
||||||
} else if (needsPreload) {
|
initFragments.push(
|
||||||
runtimeRequirements.add(RuntimeGlobals.preloadAsset);
|
new InitFragment(
|
||||||
|
`${RuntimeGlobals.prefetchAsset}(${moduleId}, ${JSON.stringify(
|
||||||
|
assetType
|
||||||
|
)}${dep.fetchPriority ? `, ${JSON.stringify(dep.fetchPriority)}` : ""}, ${
|
||||||
|
dep.relative
|
||||||
|
});\n`,
|
||||||
|
InitFragment.STAGE_CONSTANTS,
|
||||||
|
-5,
|
||||||
|
`asset_prefetch_${moduleId}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (dep.relative) {
|
|
||||||
// Standard URL generation without resource hints
|
|
||||||
runtimeRequirements.add(RuntimeGlobals.relativeUrl);
|
|
||||||
source.replace(
|
|
||||||
dep.outerRange[0],
|
|
||||||
dep.outerRange[1] - 1,
|
|
||||||
`/* asset import */ new ${
|
|
||||||
RuntimeGlobals.relativeUrl
|
|
||||||
}(${runtimeTemplate.moduleRaw({
|
|
||||||
chunkGraph,
|
|
||||||
module: moduleGraph.getModule(dep),
|
|
||||||
request: dep.request,
|
|
||||||
runtimeRequirements,
|
|
||||||
weak: false
|
|
||||||
})})`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
runtimeRequirements.add(RuntimeGlobals.baseURI);
|
|
||||||
|
|
||||||
source.replace(
|
|
||||||
dep.range[0],
|
|
||||||
dep.range[1] - 1,
|
|
||||||
`/* asset import */ ${runtimeTemplate.moduleRaw({
|
|
||||||
chunkGraph,
|
|
||||||
module: moduleGraph.getModule(dep),
|
|
||||||
request: dep.request,
|
|
||||||
runtimeRequirements,
|
|
||||||
weak: false
|
|
||||||
})}, ${RuntimeGlobals.baseURI}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -371,6 +371,60 @@ class WorkerPlugin {
|
||||||
entryOptions.name = importOptions.webpackChunkName;
|
entryOptions.name = importOptions.webpackChunkName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Support webpackPrefetch (true | number)
|
||||||
|
if (importOptions.webpackPrefetch !== undefined) {
|
||||||
|
if (importOptions.webpackPrefetch === true) {
|
||||||
|
groupOptions.prefetchOrder = 0;
|
||||||
|
} else if (typeof importOptions.webpackPrefetch === "number") {
|
||||||
|
groupOptions.prefetchOrder = importOptions.webpackPrefetch;
|
||||||
|
} else {
|
||||||
|
parser.state.module.addWarning(
|
||||||
|
new UnsupportedFeatureWarning(
|
||||||
|
`\`webpackPrefetch\` expected true or a number, but received: ${importOptions.webpackPrefetch}.`,
|
||||||
|
/** @type {DependencyLocation} */ (expr.loc)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support webpackPreload (true | number)
|
||||||
|
if (importOptions.webpackPreload !== undefined) {
|
||||||
|
if (importOptions.webpackPreload === true) {
|
||||||
|
groupOptions.preloadOrder = 0;
|
||||||
|
} else if (typeof importOptions.webpackPreload === "number") {
|
||||||
|
groupOptions.preloadOrder = importOptions.webpackPreload;
|
||||||
|
} else {
|
||||||
|
parser.state.module.addWarning(
|
||||||
|
new UnsupportedFeatureWarning(
|
||||||
|
`\`webpackPreload\` expected true or a number, but received: ${importOptions.webpackPreload}.`,
|
||||||
|
/** @type {DependencyLocation} */ (expr.loc)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support webpackFetchPriority ("high" | "low" | "auto")
|
||||||
|
if (importOptions.webpackFetchPriority !== undefined) {
|
||||||
|
if (
|
||||||
|
typeof importOptions.webpackFetchPriority === "string" &&
|
||||||
|
["high", "low", "auto"].includes(
|
||||||
|
importOptions.webpackFetchPriority
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
groupOptions.fetchPriority =
|
||||||
|
/** @type {"auto" | "high" | "low"} */ (
|
||||||
|
importOptions.webpackFetchPriority
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
parser.state.module.addWarning(
|
||||||
|
new UnsupportedFeatureWarning(
|
||||||
|
`\`webpackFetchPriority\` expected "low", "high" or "auto", but received: ${importOptions.webpackFetchPriority}.`,
|
||||||
|
/** @type {DependencyLocation} */ (expr.loc)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -407,7 +461,7 @@ class WorkerPlugin {
|
||||||
entryOptions: {
|
entryOptions: {
|
||||||
chunkLoading: this._chunkLoading,
|
chunkLoading: this._chunkLoading,
|
||||||
wasmLoading: this._wasmLoading,
|
wasmLoading: this._wasmLoading,
|
||||||
...entryOptions
|
runtime: entryOptions.runtime
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
block.loc = expr.loc;
|
block.loc = expr.loc;
|
||||||
|
|
|
@ -1,178 +0,0 @@
|
||||||
/*
|
|
||||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
||||||
Author Tobias Koppers @sokra
|
|
||||||
*/
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const RuntimeGlobals = require("../RuntimeGlobals");
|
|
||||||
const getAssetType = require("../util/assetType");
|
|
||||||
const AssetPrefetchStartupRuntimeModule = require("./AssetPrefetchStartupRuntimeModule");
|
|
||||||
|
|
||||||
/** @typedef {import("../Chunk")} Chunk */
|
|
||||||
/** @typedef {import("../Compiler")} Compiler */
|
|
||||||
/** @typedef {import("../Module")} Module */
|
|
||||||
/** @typedef {import("../NormalModule")} NormalModule */
|
|
||||||
/** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {object} AssetInfo
|
|
||||||
* @property {string} url
|
|
||||||
* @property {string} as
|
|
||||||
* @property {string=} fetchPriority
|
|
||||||
* @property {string=} type
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {object} AssetPrefetchInfo
|
|
||||||
* @property {AssetInfo[]} prefetch
|
|
||||||
* @property {AssetInfo[]} preload
|
|
||||||
*/
|
|
||||||
|
|
||||||
const PLUGIN_NAME = "AssetPrefetchStartupPlugin";
|
|
||||||
|
|
||||||
class AssetPrefetchStartupPlugin {
|
|
||||||
/**
|
|
||||||
* @param {Compiler} compiler the compiler
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
apply(compiler) {
|
|
||||||
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
|
|
||||||
const assetPrefetchMap = new WeakMap();
|
|
||||||
const chunkAssetInfoMap = new WeakMap();
|
|
||||||
|
|
||||||
// Collect URLDependencies with prefetch/preload hints during module finalization
|
|
||||||
compilation.hooks.finishModules.tap(PLUGIN_NAME, (modules) => {
|
|
||||||
for (const module of modules) {
|
|
||||||
if (!module.dependencies) continue;
|
|
||||||
|
|
||||||
// Find all URL dependencies that have prefetch or preload hints
|
|
||||||
const assetDeps = [];
|
|
||||||
for (const dep of module.dependencies) {
|
|
||||||
if (dep.constructor.name === "URLDependency") {
|
|
||||||
const urlDep =
|
|
||||||
/** @type {import("../dependencies/URLDependency")} */ (dep);
|
|
||||||
if (urlDep.prefetch || urlDep.preload) {
|
|
||||||
assetDeps.push(urlDep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (assetDeps.length > 0) {
|
|
||||||
assetPrefetchMap.set(module, assetDeps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Aggregate prefetch/preload assets by chunk during optimization
|
|
||||||
compilation.hooks.optimizeChunks.tap(
|
|
||||||
{ name: PLUGIN_NAME, stage: 1 },
|
|
||||||
(chunks) => {
|
|
||||||
const chunkGraph = compilation.chunkGraph;
|
|
||||||
const moduleGraph = compilation.moduleGraph;
|
|
||||||
|
|
||||||
for (const chunk of chunks) {
|
|
||||||
const assetInfo = {
|
|
||||||
prefetch: /** @type {AssetInfo[]} */ ([]),
|
|
||||||
preload: /** @type {AssetInfo[]} */ ([])
|
|
||||||
};
|
|
||||||
|
|
||||||
// Iterate through all modules in the chunk
|
|
||||||
for (const module of chunkGraph.getChunkModules(chunk)) {
|
|
||||||
const urlDeps = assetPrefetchMap.get(module);
|
|
||||||
if (!urlDeps) continue;
|
|
||||||
|
|
||||||
for (const dep of urlDeps) {
|
|
||||||
// Flag this dependency as handled by startup module to prevent inline generation
|
|
||||||
dep._startupPrefetch = true;
|
|
||||||
|
|
||||||
const resolvedModule = moduleGraph.getModule(dep);
|
|
||||||
if (!resolvedModule) continue;
|
|
||||||
|
|
||||||
const request = /** @type {{ request?: string }} */ (
|
|
||||||
resolvedModule
|
|
||||||
).request;
|
|
||||||
if (!request) continue;
|
|
||||||
|
|
||||||
// Extract the asset filename from module metadata
|
|
||||||
let assetUrl;
|
|
||||||
if (
|
|
||||||
resolvedModule.buildInfo &&
|
|
||||||
resolvedModule.buildInfo.filename
|
|
||||||
) {
|
|
||||||
assetUrl = resolvedModule.buildInfo.filename;
|
|
||||||
} else {
|
|
||||||
// Fall back to filename from request path
|
|
||||||
assetUrl = request.split(/[\\/]/).pop() || request;
|
|
||||||
}
|
|
||||||
|
|
||||||
const assetType = getAssetType(request);
|
|
||||||
const info = {
|
|
||||||
url: assetUrl,
|
|
||||||
as: assetType,
|
|
||||||
fetchPriority: dep.fetchPriority,
|
|
||||||
type: dep.preloadType
|
|
||||||
};
|
|
||||||
|
|
||||||
if (dep.prefetch && !dep.preload) {
|
|
||||||
assetInfo.prefetch.push(info);
|
|
||||||
} else if (dep.preload) {
|
|
||||||
assetInfo.preload.push(info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (assetInfo.prefetch.length > 0 || assetInfo.preload.length > 0) {
|
|
||||||
const existing = chunkAssetInfoMap.get(chunk);
|
|
||||||
if (!existing) {
|
|
||||||
chunkAssetInfoMap.set(chunk, assetInfo);
|
|
||||||
} else {
|
|
||||||
existing.prefetch.push(...assetInfo.prefetch);
|
|
||||||
existing.preload.push(...assetInfo.preload);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
compilation.hooks.additionalChunkRuntimeRequirements.tap(
|
|
||||||
PLUGIN_NAME,
|
|
||||||
(chunk, set) => {
|
|
||||||
const assetInfo = chunkAssetInfoMap.get(chunk);
|
|
||||||
if (!assetInfo) return;
|
|
||||||
|
|
||||||
const { prefetch, preload } = assetInfo;
|
|
||||||
|
|
||||||
if (prefetch.length > 0) {
|
|
||||||
set.add(RuntimeGlobals.prefetchAsset);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (preload.length > 0) {
|
|
||||||
set.add(RuntimeGlobals.preloadAsset);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prefetch.length > 0 || preload.length > 0) {
|
|
||||||
compilation.addRuntimeModule(
|
|
||||||
chunk,
|
|
||||||
new AssetPrefetchStartupRuntimeModule(assetInfo)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
compilation.hooks.runtimeRequirementInTree
|
|
||||||
.for(RuntimeGlobals.prefetchAsset)
|
|
||||||
.tap(PLUGIN_NAME, (chunk, set) => {
|
|
||||||
set.add(RuntimeGlobals.publicPath);
|
|
||||||
});
|
|
||||||
|
|
||||||
compilation.hooks.runtimeRequirementInTree
|
|
||||||
.for(RuntimeGlobals.preloadAsset)
|
|
||||||
.tap(PLUGIN_NAME, (chunk, set) => {
|
|
||||||
set.add(RuntimeGlobals.publicPath);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = AssetPrefetchStartupPlugin;
|
|
|
@ -1,153 +0,0 @@
|
||||||
/*
|
|
||||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
||||||
Author Tobias Koppers @sokra
|
|
||||||
*/
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const RuntimeGlobals = require("../RuntimeGlobals");
|
|
||||||
const RuntimeModule = require("../RuntimeModule");
|
|
||||||
const Template = require("../Template");
|
|
||||||
|
|
||||||
/** @typedef {import("../Chunk")} Chunk */
|
|
||||||
/** @typedef {import("../Compilation")} Compilation */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {object} AssetInfo
|
|
||||||
* @property {string} url
|
|
||||||
* @property {string} as
|
|
||||||
* @property {string=} fetchPriority
|
|
||||||
* @property {string=} type
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {object} AssetPrefetchInfo
|
|
||||||
* @property {AssetInfo[]} prefetch
|
|
||||||
* @property {AssetInfo[]} preload
|
|
||||||
*/
|
|
||||||
|
|
||||||
class AssetPrefetchStartupRuntimeModule extends RuntimeModule {
|
|
||||||
/**
|
|
||||||
* @param {AssetPrefetchInfo} assetInfo asset prefetch/preload information
|
|
||||||
*/
|
|
||||||
constructor(assetInfo) {
|
|
||||||
super("asset prefetch", RuntimeModule.STAGE_TRIGGER);
|
|
||||||
this.assetInfo = assetInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {string} a unique identifier of the module
|
|
||||||
*/
|
|
||||||
identifier() {
|
|
||||||
return `webpack/runtime/asset-prefetch-startup|${JSON.stringify(
|
|
||||||
this.assetInfo
|
|
||||||
)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {string | null} runtime code
|
|
||||||
*/
|
|
||||||
generate() {
|
|
||||||
const { assetInfo } = this;
|
|
||||||
const compilation = /** @type {Compilation} */ (this.compilation);
|
|
||||||
const { runtimeTemplate } = compilation;
|
|
||||||
|
|
||||||
const lines = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {AssetInfo} asset asset info object
|
|
||||||
* @returns {string} serialized function arguments
|
|
||||||
*/
|
|
||||||
const serializeAsset = (asset) => {
|
|
||||||
const args = [
|
|
||||||
`${RuntimeGlobals.publicPath} + ${JSON.stringify(asset.url)}`,
|
|
||||||
`"${asset.as}"`
|
|
||||||
];
|
|
||||||
|
|
||||||
if (asset.fetchPriority) {
|
|
||||||
args.push(`"${asset.fetchPriority}"`);
|
|
||||||
} else {
|
|
||||||
args.push("undefined");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (asset.type) {
|
|
||||||
args.push(`"${asset.type}"`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return args.join(", ");
|
|
||||||
};
|
|
||||||
|
|
||||||
if (assetInfo.prefetch.length > 0) {
|
|
||||||
const prefetchCode =
|
|
||||||
assetInfo.prefetch.length <= 2
|
|
||||||
? assetInfo.prefetch.map(
|
|
||||||
(asset) =>
|
|
||||||
`${RuntimeGlobals.prefetchAsset}(${serializeAsset(asset)});`
|
|
||||||
)
|
|
||||||
: Template.asString([
|
|
||||||
`[${assetInfo.prefetch
|
|
||||||
.map(
|
|
||||||
(asset) =>
|
|
||||||
`{ url: ${RuntimeGlobals.publicPath} + ${JSON.stringify(
|
|
||||||
asset.url
|
|
||||||
)}, as: "${asset.as}"${
|
|
||||||
asset.fetchPriority
|
|
||||||
? `, fetchPriority: "${asset.fetchPriority}"`
|
|
||||||
: ""
|
|
||||||
}${asset.type ? `, type: "${asset.type}"` : ""} }`
|
|
||||||
)
|
|
||||||
.join(", ")}].forEach(${runtimeTemplate.basicFunction("asset", [
|
|
||||||
`${RuntimeGlobals.prefetchAsset}(asset.url, asset.as, asset.fetchPriority, asset.type);`
|
|
||||||
])});`
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (Array.isArray(prefetchCode)) {
|
|
||||||
lines.push(...prefetchCode);
|
|
||||||
} else {
|
|
||||||
lines.push(prefetchCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (assetInfo.preload.length > 0) {
|
|
||||||
const preloadCode =
|
|
||||||
assetInfo.preload.length <= 2
|
|
||||||
? assetInfo.preload.map(
|
|
||||||
(asset) =>
|
|
||||||
`${RuntimeGlobals.preloadAsset}(${serializeAsset(asset)});`
|
|
||||||
)
|
|
||||||
: Template.asString([
|
|
||||||
`[${assetInfo.preload
|
|
||||||
.map(
|
|
||||||
(asset) =>
|
|
||||||
`{ url: ${RuntimeGlobals.publicPath} + ${JSON.stringify(
|
|
||||||
asset.url
|
|
||||||
)}, as: "${asset.as}"${
|
|
||||||
asset.fetchPriority
|
|
||||||
? `, fetchPriority: "${asset.fetchPriority}"`
|
|
||||||
: ""
|
|
||||||
}${asset.type ? `, type: "${asset.type}"` : ""} }`
|
|
||||||
)
|
|
||||||
.join(", ")}].forEach(${runtimeTemplate.basicFunction("asset", [
|
|
||||||
`${RuntimeGlobals.preloadAsset}(asset.url, asset.as, asset.fetchPriority, asset.type);`
|
|
||||||
])});`
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (Array.isArray(preloadCode)) {
|
|
||||||
lines.push(...preloadCode);
|
|
||||||
} else {
|
|
||||||
lines.push(preloadCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Template.asString(lines);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {boolean} true, if the runtime module should get it's own scope
|
|
||||||
*/
|
|
||||||
shouldIsolate() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = AssetPrefetchStartupRuntimeModule;
|
|
|
@ -1,66 +0,0 @@
|
||||||
/*
|
|
||||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
||||||
Author Tobias Koppers @sokra
|
|
||||||
*/
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const RuntimeGlobals = require("../RuntimeGlobals");
|
|
||||||
const RuntimeModule = require("../RuntimeModule");
|
|
||||||
const Template = require("../Template");
|
|
||||||
|
|
||||||
/** @typedef {import("../Compilation")} Compilation */
|
|
||||||
|
|
||||||
class AssetPrefetchPreloadRuntimeModule extends RuntimeModule {
|
|
||||||
/**
|
|
||||||
* @param {string} type "prefetch" or "preload"
|
|
||||||
*/
|
|
||||||
constructor(type) {
|
|
||||||
super(`asset ${type}`);
|
|
||||||
this._type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {string | null} runtime code
|
|
||||||
*/
|
|
||||||
generate() {
|
|
||||||
const { compilation } = this;
|
|
||||||
if (!compilation) return null;
|
|
||||||
const { runtimeTemplate } = compilation;
|
|
||||||
const fn =
|
|
||||||
this._type === "prefetch"
|
|
||||||
? RuntimeGlobals.prefetchAsset
|
|
||||||
: RuntimeGlobals.preloadAsset;
|
|
||||||
|
|
||||||
return Template.asString([
|
|
||||||
`${fn} = ${runtimeTemplate.basicFunction("url, as, fetchPriority, type", [
|
|
||||||
"var link = document.createElement('link');",
|
|
||||||
this._type === "prefetch"
|
|
||||||
? "link.rel = 'prefetch';"
|
|
||||||
: "link.rel = 'preload';",
|
|
||||||
"if(as) link.as = as;",
|
|
||||||
"if(type) link.type = type;",
|
|
||||||
"link.href = url;",
|
|
||||||
"if(fetchPriority) {",
|
|
||||||
Template.indent([
|
|
||||||
"link.fetchPriority = fetchPriority;",
|
|
||||||
"link.setAttribute('fetchpriority', fetchPriority);"
|
|
||||||
]),
|
|
||||||
"}",
|
|
||||||
// Apply nonce attribute for CSP if configured
|
|
||||||
compilation.outputOptions.crossOriginLoading
|
|
||||||
? Template.asString([
|
|
||||||
`if(${RuntimeGlobals.scriptNonce}) {`,
|
|
||||||
Template.indent(
|
|
||||||
`link.setAttribute('nonce', ${RuntimeGlobals.scriptNonce});`
|
|
||||||
),
|
|
||||||
"}"
|
|
||||||
])
|
|
||||||
: "",
|
|
||||||
"document.head.appendChild(link);"
|
|
||||||
])};`
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = AssetPrefetchPreloadRuntimeModule;
|
|
|
@ -184,14 +184,12 @@ class URLParserPlugin {
|
||||||
relative
|
relative
|
||||||
);
|
);
|
||||||
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
||||||
|
// Parse magic comments with simplified rules
|
||||||
// Process magic comments for prefetch/preload hints
|
|
||||||
if (importOptions) {
|
if (importOptions) {
|
||||||
// webpackPrefetch should be boolean true
|
// Accept only boolean true for webpackPrefetch
|
||||||
if (
|
if (importOptions.webpackPrefetch === true) {
|
||||||
importOptions.webpackPrefetch !== undefined &&
|
dep.prefetch = true;
|
||||||
importOptions.webpackPrefetch !== true
|
} else if (importOptions.webpackPrefetch !== undefined) {
|
||||||
) {
|
|
||||||
parser.state.module.addWarning(
|
parser.state.module.addWarning(
|
||||||
new UnsupportedFeatureWarning(
|
new UnsupportedFeatureWarning(
|
||||||
`\`webpackPrefetch\` expected true, but received: ${importOptions.webpackPrefetch}.`,
|
`\`webpackPrefetch\` expected true, but received: ${importOptions.webpackPrefetch}.`,
|
||||||
|
@ -200,11 +198,10 @@ class URLParserPlugin {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// webpackPreload should be boolean true
|
// Accept only boolean true for webpackPreload
|
||||||
if (
|
if (importOptions.webpackPreload === true) {
|
||||||
importOptions.webpackPreload !== undefined &&
|
dep.preload = true;
|
||||||
importOptions.webpackPreload !== true
|
} else if (importOptions.webpackPreload !== undefined) {
|
||||||
) {
|
|
||||||
parser.state.module.addWarning(
|
parser.state.module.addWarning(
|
||||||
new UnsupportedFeatureWarning(
|
new UnsupportedFeatureWarning(
|
||||||
`\`webpackPreload\` expected true, but received: ${importOptions.webpackPreload}.`,
|
`\`webpackPreload\` expected true, but received: ${importOptions.webpackPreload}.`,
|
||||||
|
@ -213,14 +210,13 @@ class URLParserPlugin {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// webpackFetchPriority should be one of: high, low, auto
|
// webpackFetchPriority: "high" | "low" | "auto"
|
||||||
if (
|
if (
|
||||||
importOptions.webpackFetchPriority !== undefined &&
|
typeof importOptions.webpackFetchPriority === "string" &&
|
||||||
(typeof importOptions.webpackFetchPriority !== "string" ||
|
["high", "low", "auto"].includes(importOptions.webpackFetchPriority)
|
||||||
!["high", "low", "auto"].includes(
|
|
||||||
importOptions.webpackFetchPriority
|
|
||||||
))
|
|
||||||
) {
|
) {
|
||||||
|
dep.fetchPriority = importOptions.webpackFetchPriority;
|
||||||
|
} else if (importOptions.webpackFetchPriority !== undefined) {
|
||||||
parser.state.module.addWarning(
|
parser.state.module.addWarning(
|
||||||
new UnsupportedFeatureWarning(
|
new UnsupportedFeatureWarning(
|
||||||
`\`webpackFetchPriority\` expected "low", "high" or "auto", but received: ${importOptions.webpackFetchPriority}.`,
|
`\`webpackFetchPriority\` expected "low", "high" or "auto", but received: ${importOptions.webpackFetchPriority}.`,
|
||||||
|
@ -228,39 +224,6 @@ class URLParserPlugin {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// webpackPreloadAs should be a string
|
|
||||||
if (
|
|
||||||
importOptions.webpackPreloadAs !== undefined &&
|
|
||||||
typeof importOptions.webpackPreloadAs !== "string"
|
|
||||||
) {
|
|
||||||
parser.state.module.addWarning(
|
|
||||||
new UnsupportedFeatureWarning(
|
|
||||||
`\`webpackPreloadAs\` expected a string, but received: ${importOptions.webpackPreloadAs}.`,
|
|
||||||
/** @type {DependencyLocation} */ (expr.loc)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// webpackPreloadType should be a string
|
|
||||||
if (
|
|
||||||
importOptions.webpackPreloadType !== undefined &&
|
|
||||||
typeof importOptions.webpackPreloadType !== "string"
|
|
||||||
) {
|
|
||||||
parser.state.module.addWarning(
|
|
||||||
new UnsupportedFeatureWarning(
|
|
||||||
`\`webpackPreloadType\` expected a string, but received: ${importOptions.webpackPreloadType}.`,
|
|
||||||
/** @type {DependencyLocation} */ (expr.loc)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store magic comment values on dependency
|
|
||||||
dep.prefetch = importOptions.webpackPrefetch;
|
|
||||||
dep.preload = importOptions.webpackPreload;
|
|
||||||
dep.fetchPriority = importOptions.webpackFetchPriority;
|
|
||||||
dep.preloadAs = importOptions.webpackPreloadAs;
|
|
||||||
dep.preloadType = importOptions.webpackPreloadType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register the dependency
|
// Register the dependency
|
||||||
|
|
|
@ -5,7 +5,4 @@
|
||||||
// Invalid fetchPriority value - should generate warning
|
// Invalid fetchPriority value - should generate warning
|
||||||
const invalidPriorityUrl = new URL(/* webpackPrefetch: true */ /* webpackFetchPriority: "invalid" */ "./assets/images/priority-invalid.png", import.meta.url);
|
const invalidPriorityUrl = new URL(/* webpackPrefetch: true */ /* webpackFetchPriority: "invalid" */ "./assets/images/priority-invalid.png", import.meta.url);
|
||||||
|
|
||||||
// Invalid webpackPreloadType value - should generate warning
|
|
||||||
const invalidTypeUrl = new URL(/* webpackPreload: true */ /* webpackPreloadType: 123 */ "./assets/styles/invalid-type.css", import.meta.url);
|
|
||||||
|
|
||||||
export default {};
|
export default {};
|
||||||
|
|
|
@ -18,9 +18,6 @@ function verifyLink(link, expectations) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expectations.type) {
|
|
||||||
expect(link.type).toBe(expectations.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (expectations.href) {
|
if (expectations.href) {
|
||||||
expect(link.href.toString()).toMatch(expectations.href);
|
expect(link.href.toString()).toMatch(expectations.href);
|
||||||
|
@ -44,11 +41,6 @@ it("should generate all prefetch and preload links", () => {
|
||||||
"./priority-auto.js",
|
"./priority-auto.js",
|
||||||
import.meta.url
|
import.meta.url
|
||||||
),
|
),
|
||||||
preloadTyped: new URL(
|
|
||||||
/* webpackPreload: true */ /* webpackPreloadType: "text/css" */
|
|
||||||
"./assets/styles/typed.css",
|
|
||||||
import.meta.url
|
|
||||||
),
|
|
||||||
bothHints: new URL(
|
bothHints: new URL(
|
||||||
/* webpackPrefetch: true */ /* webpackPreload: true */ /* webpackFetchPriority: "high" */
|
/* webpackPrefetch: true */ /* webpackPreload: true */ /* webpackFetchPriority: "high" */
|
||||||
"./assets/images/both-hints.png",
|
"./assets/images/both-hints.png",
|
||||||
|
@ -98,17 +90,6 @@ it("should generate all prefetch and preload links", () => {
|
||||||
fetchPriority: "auto"
|
fetchPriority: "auto"
|
||||||
});
|
});
|
||||||
|
|
||||||
const preloadTypedLink = document.head._children.find(
|
|
||||||
link => link.href.includes("typed.css") && link.rel === "preload"
|
|
||||||
);
|
|
||||||
expect(preloadTypedLink).toBeTruthy();
|
|
||||||
verifyLink(preloadTypedLink, {
|
|
||||||
rel: "preload",
|
|
||||||
as: "style",
|
|
||||||
type: "text/css",
|
|
||||||
href: /typed\.css$/
|
|
||||||
});
|
|
||||||
|
|
||||||
const bothHintsLink = document.head._children.find(
|
const bothHintsLink = document.head._children.find(
|
||||||
link => link.href.includes("both-hints.png")
|
link => link.href.includes("both-hints.png")
|
||||||
);
|
);
|
||||||
|
|
|
@ -22,6 +22,7 @@ const mockCreateElement = (tagName) => {
|
||||||
element.rel = "";
|
element.rel = "";
|
||||||
element.as = "";
|
element.as = "";
|
||||||
element.href = "";
|
element.href = "";
|
||||||
|
element.type = undefined;
|
||||||
element.fetchPriority = undefined;
|
element.fetchPriority = undefined;
|
||||||
} else if (tagName === "script") {
|
} else if (tagName === "script") {
|
||||||
element.src = "";
|
element.src = "";
|
||||||
|
@ -60,33 +61,5 @@ module.exports = {
|
||||||
moduleScope(scope) {
|
moduleScope(scope) {
|
||||||
// Make document available in the module scope
|
// Make document available in the module scope
|
||||||
scope.document = global.document;
|
scope.document = global.document;
|
||||||
// Inject runtime globals that would normally be provided by webpack
|
|
||||||
scope.__webpack_require__ = {
|
|
||||||
PA(url, as, fetchPriority, type) {
|
|
||||||
const link = global.document.createElement("link");
|
|
||||||
link.rel = "prefetch";
|
|
||||||
if (as) link.as = as;
|
|
||||||
if (type) link.type = type;
|
|
||||||
link.href = url;
|
|
||||||
if (fetchPriority) {
|
|
||||||
link.fetchPriority = fetchPriority;
|
|
||||||
link.setAttribute("fetchpriority", fetchPriority);
|
|
||||||
}
|
|
||||||
global.document.head.appendChild(link);
|
|
||||||
},
|
|
||||||
LA(url, as, fetchPriority, type) {
|
|
||||||
const link = global.document.createElement("link");
|
|
||||||
link.rel = "preload";
|
|
||||||
if (as) link.as = as;
|
|
||||||
if (type) link.type = type;
|
|
||||||
link.href = url;
|
|
||||||
if (fetchPriority) {
|
|
||||||
link.fetchPriority = fetchPriority;
|
|
||||||
link.setAttribute("fetchpriority", fetchPriority);
|
|
||||||
}
|
|
||||||
global.document.head.appendChild(link);
|
|
||||||
},
|
|
||||||
b: "https://test.example.com/" // baseURI
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,7 +4,5 @@ module.exports = [
|
||||||
// Invalid fetchPriority value warning
|
// Invalid fetchPriority value warning
|
||||||
[
|
[
|
||||||
/`webpackFetchPriority` expected "low", "high" or "auto", but received: invalid\./
|
/`webpackFetchPriority` expected "low", "high" or "auto", but received: invalid\./
|
||||||
],
|
]
|
||||||
// Invalid webpackPreloadType value warning
|
|
||||||
[/`webpackPreloadType` expected a string, but received: 123\./]
|
|
||||||
];
|
];
|
||||||
|
|
Loading…
Reference in New Issue