webpack/lib/dependencies/URLDependency.js

265 lines
8.2 KiB
JavaScript
Raw Normal View History

2020-08-05 05:42:29 +08:00
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Ivan Kopeykin @vankop
*/
"use strict";
const RuntimeGlobals = require("../RuntimeGlobals");
2024-02-28 23:23:23 +08:00
const RawDataUrlModule = require("../asset/RawDataUrlModule");
2021-01-27 20:32:09 +08:00
const {
getDependencyUsedByExportsCondition
} = require("../optimize/InnerGraph");
2020-08-07 01:00:22 +08:00
const makeSerializable = require("../util/makeSerializable");
const memoize = require("../util/memoize");
2020-08-05 05:42:29 +08:00
const ModuleDependency = require("./ModuleDependency");
/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
/** @typedef {import("../ChunkGraph")} ChunkGraph */
/** @typedef {import("../Dependency")} Dependency */
2024-02-22 22:36:36 +08:00
/** @typedef {import("../Dependency").GetConditionFn} GetConditionFn */
2020-08-05 05:42:29 +08:00
/** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
/** @typedef {import("../Module")} Module */
2020-08-05 05:42:29 +08:00
/** @typedef {import("../ModuleGraph")} ModuleGraph */
/** @typedef {import("../ModuleGraphConnection")} ModuleGraphConnection */
/** @typedef {import("../ModuleGraphConnection").ConnectionState} ConnectionState */
2023-05-22 04:31:30 +08:00
/** @typedef {import("../javascript/JavascriptParser").Range} Range */
2023-04-12 03:22:51 +08:00
/** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
/** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext} ObjectSerializerContext */
2020-08-05 05:42:29 +08:00
/** @typedef {import("../util/Hash")} Hash */
/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
2020-08-05 05:42:29 +08:00
2024-07-31 11:31:11 +08:00
const getIgnoredRawDataUrlModule = memoize(
2024-07-31 12:23:44 +08:00
() => new RawDataUrlModule("data:,", "ignored-asset", "(ignored asset)")
2024-07-31 11:31:11 +08:00
);
2020-08-05 05:42:29 +08:00
class URLDependency extends ModuleDependency {
/**
* @param {string} request request
2023-05-22 04:31:30 +08:00
* @param {Range} range range of the arguments of new URL( |> ... <| )
* @param {Range} outerRange range of the full |> new URL(...) <|
* @param {boolean=} relative use relative urls instead of absolute with base uri
2020-08-05 05:42:29 +08:00
*/
constructor(request, range, outerRange, relative) {
2020-08-05 05:42:29 +08:00
super(request);
this.range = range;
this.outerRange = outerRange;
this.relative = relative || false;
2023-06-17 02:24:34 +08:00
/** @type {Set<string> | boolean | undefined} */
this.usedByExports = undefined;
2020-08-05 05:42:29 +08:00
}
get type() {
return "new URL()";
}
get category() {
return "url";
}
/**
* @param {ModuleGraph} moduleGraph module graph
2024-02-22 22:20:17 +08:00
* @returns {null | false | GetConditionFn} function to determine if the connection is active
*/
getCondition(moduleGraph) {
2021-01-27 20:32:09 +08:00
return getDependencyUsedByExportsCondition(
this,
this.usedByExports,
moduleGraph
);
}
/**
* @param {string} context context directory
2025-05-13 21:03:58 +08:00
* @returns {Module} ignored module
*/
createIgnoredModule(context) {
2024-02-28 23:08:34 +08:00
return getIgnoredRawDataUrlModule();
}
2023-04-12 03:22:51 +08:00
/**
* @param {ObjectSerializerContext} context context
*/
serialize(context) {
const { write } = context;
write(this.outerRange);
write(this.relative);
write(this.usedByExports);
super.serialize(context);
}
2023-04-12 03:22:51 +08:00
/**
* @param {ObjectDeserializerContext} context context
*/
deserialize(context) {
const { read } = context;
this.outerRange = read();
this.relative = read();
this.usedByExports = read();
super.deserialize(context);
}
2020-08-05 05:42:29 +08:00
}
2020-11-26 17:52:55 +08:00
URLDependency.Template = class URLDependencyTemplate extends (
ModuleDependency.Template
) {
2020-08-05 05:42:29 +08:00
/**
* @param {Dependency} dependency the dependency for which the template should be applied
* @param {ReplaceSource} source the current replace source which can be modified
* @param {DependencyTemplateContext} templateContext the context object
* @returns {void}
*/
apply(dependency, source, templateContext) {
const {
chunkGraph,
moduleGraph,
runtimeRequirements,
runtimeTemplate,
runtime
2020-08-05 05:42:29 +08:00
} = templateContext;
const dep = /** @type {URLDependency} */ (dependency);
const connection = moduleGraph.getConnection(dep);
// Skip rendering depending when dependency is conditional
if (connection && !connection.isTargetActive(runtime)) {
source.replace(
dep.outerRange[0],
dep.outerRange[1] - 1,
"/* unused asset import */ undefined"
);
return;
}
2020-08-05 05:42:29 +08:00
runtimeRequirements.add(RuntimeGlobals.require);
// Check if we need to add prefetch/preload runtime
const needsPrefetch = dep.prefetch !== undefined && dep.prefetch !== false;
const needsPreload = dep.preload !== undefined && dep.preload !== false;
if (needsPrefetch || needsPreload) {
// Get the module to determine asset type
const module = moduleGraph.getModule(dep);
let asType = "";
if (module) {
const request = module.request || "";
// Determine the 'as' attribute based on file extension
// Reference: MDN rel=preload documentation (https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel/preload)
// Valid 'as' values: fetch, font, image, script, style, track
// Note: audio/video are in spec but not supported by major browsers as of 2025
if (/\.(png|jpe?g|gif|svg|webp|avif|bmp|ico|tiff?)$/i.test(request)) {
asType = "image";
} else if (/\.(woff2?|ttf|otf|eot)$/i.test(request)) {
asType = "font";
} else if (/\.(js|mjs|jsx|ts|tsx)$/i.test(request)) {
asType = "script";
} else if (/\.css$/i.test(request)) {
asType = "style";
} else if (/\.vtt$/i.test(request)) {
asType = "track"; // WebVTT files for video subtitles/captions
} else if (
/\.(mp4|webm|ogg|mp3|wav|flac|aac|m4a|avi|mov|wmv|mkv)$/i.test(
request
)
) {
// Audio/video files: use 'fetch' as fallback since as='audio'/'video' not supported
// Reference: https://github.com/mdn/browser-compat-data/issues/9577
asType = "fetch";
} else if (/\.(json|xml|txt|csv|pdf|doc|docx|wasm)$/i.test(request)) {
asType = "fetch"; // Data files, documents, WebAssembly
} else {
asType = "fetch"; // Generic fetch for unknown types
}
}
// Generate the module expression (just the module id)
const moduleExpr = runtimeTemplate.moduleRaw({
chunkGraph,
module: moduleGraph.getModule(dep),
request: dep.request,
runtimeRequirements,
weak: false
});
// Build the prefetch/preload code
const hintCode = [];
const fetchPriority = dep.fetchPriority
? `"${dep.fetchPriority}"`
: "undefined";
if (needsPrefetch && !needsPreload) {
// Only prefetch
runtimeRequirements.add(RuntimeGlobals.prefetchAsset);
hintCode.push(
`${RuntimeGlobals.prefetchAsset}(url, "${asType}", ${fetchPriority});`
);
} else if (needsPreload) {
// Preload (takes precedence over prefetch)
runtimeRequirements.add(RuntimeGlobals.preloadAsset);
hintCode.push(
`${RuntimeGlobals.preloadAsset}(url, "${asType}", ${fetchPriority});`
);
}
// Wrap in IIFE to execute hint code and return URL
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 (dep.relative) {
// No prefetch/preload - use original code
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}`
);
}
2020-08-05 05:42:29 +08:00
}
};
2020-08-07 01:00:22 +08:00
makeSerializable(URLDependency, "webpack/lib/dependencies/URLDependency");
2020-08-05 05:42:29 +08:00
module.exports = URLDependency;