| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- /*
- MIT License http://www.opensource.org/licenses/mit-license.php
- Author Ivan Kopeykin @vankop
- */
- "use strict";
- const {
- ASSET_URL_TYPE,
- JAVASCRIPT_TYPE
- } = require("../ModuleSourceTypeConstants");
- const RuntimeGlobals = require("../RuntimeGlobals");
- const RawDataUrlModule = require("../asset/RawDataUrlModule");
- const {
- getDependencyUsedByExportsCondition
- } = require("../optimize/InnerGraph");
- const { toJsStringLiteral } = require("../util/identifier");
- const makeSerializable = require("../util/makeSerializable");
- const memoize = require("../util/memoize");
- const {
- PUBLIC_PATH_AUTO,
- PUBLIC_PATH_FULL_HASH
- } = require("../util/publicPathPlaceholder");
- const ModuleDependency = require("./ModuleDependency");
- /** @import { ReplaceSource } from "webpack-sources" */
- /**
- * @import Dependency, {
- * GetConditionFn,
- * UpdateHashContext
- * } from "../Dependency"
- */
- /** @import { DependencyTemplateContext } from "../DependencyTemplate" */
- /** @import Module from "../Module" */
- /** @import ModuleGraph from "../ModuleGraph" */
- /** @import Hash from "../util/Hash" */
- /** @import { Range } from "../javascript/JavascriptParser" */
- /** @import { UsedByExports } from "../optimize/InnerGraph" */
- /** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext<[Range, boolean, UsedByExports | undefined, true | undefined, true | undefined, ("high" | "low" | "auto" | undefined), string | undefined, string | undefined, string | undefined]>} ObjectDeserializerContext */
- /** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext<[Range, boolean, UsedByExports | undefined, true | undefined, true | undefined, ("high" | "low" | "auto" | undefined), string | undefined, string | undefined, string | undefined]>} ObjectSerializerContext */
- const getIgnoredRawDataUrlModule = memoize(
- () => new RawDataUrlModule("data:,", "ignored-asset", "(ignored asset)")
- );
- /**
- * Resolves the static literal specifier (already quoted) for `new URL(<here>, import.meta.url)`,
- * or `null` when the asset url can't be determined statically (e.g. a runtime/dynamic publicPath).
- * @param {URLDependency} dep the dependency
- * @param {DependencyTemplateContext} templateContext the template context
- * @returns {string | null} a JS string literal, or `null` to fall back to the runtime form
- */
- const getAnalyzableUrlSpecifier = (dep, templateContext) => {
- const {
- module: consumingModule,
- moduleGraph,
- chunkGraph,
- runtime,
- codeGenerationResults,
- runtimeTemplate
- } = templateContext;
- const assetModule = moduleGraph.getModule(dep);
- if (!assetModule || !codeGenerationResults.has(assetModule, runtime)) {
- return null;
- }
- const urlData = codeGenerationResults.getData(assetModule, runtime, "url");
- const jsUrl = urlData && urlData[JAVASCRIPT_TYPE];
- if (
- typeof jsUrl === "string" &&
- !jsUrl.includes(RuntimeGlobals.require) &&
- // An analyzable wrapper is an expression, not a value — quoting it would nest
- // one `new URL(…)` inside another. Its own literal is rebuilt below instead.
- !jsUrl.includes(runtimeTemplate.outputOptions.importMetaName)
- ) {
- // An already-quoted literal (generator `publicPath` / data: url) is used as-is;
- // a raw value (external asset) is normalized to a quoted string.
- return jsUrl.startsWith('"') ? jsUrl : toJsStringLiteral(jsUrl);
- }
- if (urlData && jsUrl === undefined) {
- // Wrapper dropped for an `asset-url` consumer: an absolute public path resolves
- // the same url from every chunk, so it is already a literal.
- const assetUrl = urlData[ASSET_URL_TYPE];
- // A css or html consumer drops the wrapper too, and its value carries a
- // placeholder only those assets are rendered with — so use the name below.
- if (
- typeof assetUrl === "string" &&
- !assetUrl.includes(PUBLIC_PATH_AUTO) &&
- !assetUrl.includes(PUBLIC_PATH_FULL_HASH)
- ) {
- return toJsStringLiteral(assetUrl);
- }
- }
- // The wrapper concatenates the runtime public path, or was dropped as unread —
- // either way the name is what the literal is built from.
- const filename = codeGenerationResults.getData(
- assetModule,
- runtime,
- "filename"
- );
- if (typeof filename !== "string") return null;
- return runtimeTemplate.getAnalyzableAssetUrl(
- consumingModule,
- chunkGraph,
- filename,
- runtime
- );
- };
- class URLDependency extends ModuleDependency {
- /**
- * Creates an instance of URLDependency.
- * @param {string} request request
- * @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
- */
- constructor(request, range, outerRange, relative) {
- super(request);
- this.range = range;
- this.outerRange = outerRange;
- /** @type {boolean} */
- this.relative = relative || false;
- /** @type {UsedByExports | undefined} */
- this.usedByExports = undefined;
- /** @type {true | undefined} */
- this.prefetch = undefined;
- /** @type {true | undefined} */
- this.preload = undefined;
- /** @type {"high" | "low" | "auto" | undefined} */
- this.fetchPriority = undefined;
- /** @type {string | undefined} */
- this.asAttribute = undefined;
- /** @type {string | undefined} */
- this.typeAttribute = undefined;
- /** @type {string | undefined} */
- this.mediaAttribute = undefined;
- }
- /**
- * Updates the hash with the data contributed by this instance.
- * @param {Hash} hash hash to be updated
- * @param {UpdateHashContext} context context
- * @returns {void}
- */
- updateHash(hash, context) {
- super.updateHash(hash, context);
- const { runtimeTemplate, runtime } = context;
- // Only a baked url reads the base; the runtime form is the same text under every
- // base, so hashing one there would cost the code generation cache for nothing.
- if (
- runtimeTemplate === undefined ||
- this.relative ||
- !runtimeTemplate.analyzableUrlReadsBaseUri()
- ) {
- return;
- }
- const base = runtimeTemplate.entryBaseUri(runtime);
- // Disagreeing entries keep the runtime form, different code again from the
- // base-less literal `undefined` bakes; an empty base must differ from both.
- if (base === null) hash.update("|");
- // Left out entirely where no entry sets one, so ordinary builds hash as before.
- else if (typeof base === "string") hash.update(`=${base}`);
- }
- get type() {
- return "new URL()";
- }
- get category() {
- return "url";
- }
- /**
- * Returns function to determine if the connection is active.
- * @param {ModuleGraph} moduleGraph module graph
- * @returns {null | false | GetConditionFn} function to determine if the connection is active
- */
- getCondition(moduleGraph) {
- return getDependencyUsedByExportsCondition(this, moduleGraph);
- }
- /**
- * Creates an ignored module.
- * @param {string} context context directory
- * @returns {Module} ignored module
- */
- createIgnoredModule(context) {
- return getIgnoredRawDataUrlModule();
- }
- /**
- * Serializes this instance into the provided serializer context.
- * @param {ObjectSerializerContext} context context
- */
- serialize(context) {
- context
- .write(this.outerRange)
- .write(this.relative)
- .write(this.usedByExports)
- .write(this.prefetch)
- .write(this.preload)
- .write(this.fetchPriority)
- .write(this.asAttribute)
- .write(this.typeAttribute)
- .write(this.mediaAttribute);
- super.serialize(context);
- }
- /**
- * Restores this instance from the provided deserializer context.
- * @param {ObjectDeserializerContext} context context
- */
- deserialize(context) {
- this.outerRange = context.read();
- const c1 = context.rest;
- this.relative = c1.read();
- const c2 = c1.rest;
- this.usedByExports = c2.read();
- const c3 = c2.rest;
- this.prefetch = c3.read();
- const c4 = c3.rest;
- this.preload = c4.read();
- const c5 = c4.rest;
- this.fetchPriority = c5.read();
- const c6 = c5.rest;
- this.asAttribute = c6.read();
- const c7 = c6.rest;
- this.typeAttribute = c7.read();
- const c8 = c7.rest;
- this.mediaAttribute = c8.read();
- super.deserialize(c8.rest);
- }
- }
- URLDependency.Template = class URLDependencyTemplate extends (
- ModuleDependency.Template
- ) {
- /**
- * Applies the plugin by registering its hooks on the compiler.
- * @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,
- codeGenerationResults,
- runtime
- } = 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;
- }
- // For ESM module output, emit the analyzable `new URL("./asset", import.meta.url)`
- // form (literal specifier, no runtime helpers) so other bundlers and webpack itself
- // can statically follow the asset. `url: "relative"` keeps the runtime form.
- // A prefetch/preload hint doesn't force it: the `<link>` is emitted separately at
- // chunk startup (`StartupAssetHintRuntimeModule`), independent of the call site.
- if (
- !dep.relative &&
- runtimeTemplate.supportsAnalyzable(
- "url",
- chunkGraph,
- templateContext.module
- )
- ) {
- const specifier = getAnalyzableUrlSpecifier(dep, templateContext);
- if (specifier !== null) {
- source.replace(
- dep.range[0],
- dep.range[1] - 1,
- `/* asset import */ ${specifier}, ${runtimeTemplate.outputOptions.importMetaName}.url`
- );
- return;
- }
- }
- const module = moduleGraph.getModule(dep);
- // A wrapper-less asset has no exports to require, so concatenate what the wrapper
- // would have — `AssetModulesPlugin` emulates the same for build-time execution.
- const wrapperLess =
- module !== null && !module.getSourceTypes().has(JAVASCRIPT_TYPE);
- const filename =
- wrapperLess && codeGenerationResults.has(module, runtime)
- ? codeGenerationResults.getData(module, runtime, "filename")
- : undefined;
- let moduleRaw;
- if (wrapperLess && typeof filename === "string") {
- runtimeRequirements.add(RuntimeGlobals.publicPath);
- moduleRaw = `${RuntimeGlobals.publicPath} + ${toJsStringLiteral(filename)}`;
- } else {
- runtimeRequirements.add(RuntimeGlobals.require);
- if (wrapperLess) runtimeRequirements.add(RuntimeGlobals.publicPath);
- moduleRaw = runtimeTemplate.moduleRaw({
- chunkGraph,
- module,
- request: dep.request,
- runtimeRequirements,
- weak: false
- });
- }
- if (dep.relative) {
- runtimeRequirements.add(RuntimeGlobals.relativeUrl);
- source.replace(
- dep.outerRange[0],
- dep.outerRange[1] - 1,
- `/* asset import */ new ${RuntimeGlobals.relativeUrl}(${moduleRaw})`
- );
- } else {
- runtimeRequirements.add(RuntimeGlobals.baseURI);
- source.replace(
- dep.range[0],
- dep.range[1] - 1,
- `/* asset import */ ${moduleRaw}, ${RuntimeGlobals.baseURI}`
- );
- }
- }
- };
- makeSerializable(URLDependency, "webpack/lib/dependencies/URLDependency");
- module.exports = URLDependency;
|