webpack/lib/asset/AssetGenerator.js

796 lines
22 KiB
JavaScript
Raw Normal View History

/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Sergey Melyukov @smelukov
*/
"use strict";
2020-02-25 20:03:21 +08:00
const mimeTypes = require("mime-types");
const path = require("path");
const { RawSource } = require("webpack-sources");
2022-03-11 21:34:17 +08:00
const ConcatenationScope = require("../ConcatenationScope");
const Generator = require("../Generator");
const { ASSET_MODULE_TYPE } = require("../ModuleTypeConstants");
const RuntimeGlobals = require("../RuntimeGlobals");
2024-03-16 18:38:58 +08:00
const CssUrlDependency = require("../dependencies/CssUrlDependency");
const createHash = require("../util/createHash");
const { makePathsRelative } = require("../util/identifier");
const nonNumericOnlyHash = require("../util/nonNumericOnlyHash");
/** @typedef {import("webpack-sources").Source} Source */
2024-08-08 02:59:26 +08:00
/** @typedef {import("../../declarations/WebpackOptions").AssetGeneratorDataUrlOptions} AssetGeneratorDataUrlOptions */
/** @typedef {import("../../declarations/WebpackOptions").AssetGeneratorOptions} AssetGeneratorOptions */
2024-08-08 02:59:26 +08:00
/** @typedef {import("../../declarations/WebpackOptions").AssetModuleFilename} AssetModuleFilename */
/** @typedef {import("../../declarations/WebpackOptions").AssetModuleOutputPath} AssetModuleOutputPath */
/** @typedef {import("../../declarations/WebpackOptions").RawPublicPath} RawPublicPath */
/** @typedef {import("../Compilation")} Compilation */
/** @typedef {import("../Compilation").AssetInfo} AssetInfo */
/** @typedef {import("../Compilation").InterpolatedPathAndAssetInfo} InterpolatedPathAndAssetInfo */
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../Generator").GenerateContext} GenerateContext */
/** @typedef {import("../Generator").UpdateHashContext} UpdateHashContext */
/** @typedef {import("../Module")} Module */
2024-08-08 02:59:26 +08:00
/** @typedef {import("../Module").BuildInfo} BuildInfo */
/** @typedef {import("../Module").BuildMeta} BuildMeta */
2022-03-11 21:34:17 +08:00
/** @typedef {import("../Module").ConcatenationBailoutReasonContext} ConcatenationBailoutReasonContext */
/** @typedef {import("../ModuleGraph")} ModuleGraph */
/** @typedef {import("../NormalModule")} NormalModule */
/** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
2024-08-09 23:42:37 +08:00
/** @typedef {import("../TemplatedPathPlugin").TemplatePath} TemplatePath */
/** @typedef {import("../util/Hash")} Hash */
2024-08-09 23:42:37 +08:00
/** @typedef {import("../util/createHash").Algorithm} Algorithm */
2024-08-08 02:59:26 +08:00
/**
* @template T
* @template U
* @param {Array<T> | Set<T>} a a
* @param {Array<U> | Set<U>} b b
* @returns {Array<T> & Array<U>} array
*/
2021-03-22 22:08:06 +08:00
const mergeMaybeArrays = (a, b) => {
const set = new Set();
if (Array.isArray(a)) for (const item of a) set.add(item);
else set.add(a);
if (Array.isArray(b)) for (const item of b) set.add(item);
else set.add(b);
return Array.from(set);
};
2024-08-09 23:42:37 +08:00
/**
* @template {object} T
* @template {object} U
* @param {TODO} a a
* @param {TODO} b b
* @returns {T & U} object
*/
const mergeAssetInfo = (a, b) => {
const result = { ...a, ...b };
for (const key of Object.keys(a)) {
if (key in b) {
if (a[key] === b[key]) continue;
switch (key) {
case "fullhash":
case "chunkhash":
case "modulehash":
2021-03-22 22:08:06 +08:00
case "contenthash":
result[key] = mergeMaybeArrays(a[key], b[key]);
break;
case "immutable":
case "development":
case "hotModuleReplacement":
2021-12-06 20:15:48 +08:00
case "javascriptModule":
result[key] = a[key] || b[key];
break;
case "related":
result[key] = mergeRelatedInfo(a[key], b[key]);
break;
default:
throw new Error(`Can't handle conflicting asset info for ${key}`);
}
}
}
return result;
};
2024-08-09 23:42:37 +08:00
/**
* @template {object} T
* @template {object} U
* @param {TODO} a a
* @param {TODO} b b
* @returns {T & U} object
*/
const mergeRelatedInfo = (a, b) => {
const result = { ...a, ...b };
for (const key of Object.keys(a)) {
if (key in b) {
if (a[key] === b[key]) continue;
2021-03-22 22:08:06 +08:00
result[key] = mergeMaybeArrays(a[key], b[key]);
}
}
return result;
};
2024-08-08 02:59:26 +08:00
/**
* @param {"base64" | false} encoding encoding
* @param {Source} source source
* @returns {string} encoded data
*/
2022-01-13 00:23:16 +08:00
const encodeDataUri = (encoding, source) => {
2024-08-08 02:59:26 +08:00
/** @type {string | undefined} */
2022-01-13 00:23:16 +08:00
let encodedContent;
switch (encoding) {
case "base64": {
encodedContent = source.buffer().toString("base64");
break;
}
case false: {
const content = source.source();
if (typeof content !== "string") {
encodedContent = content.toString("utf-8");
}
2024-08-08 02:59:26 +08:00
encodedContent = encodeURIComponent(
/** @type {string} */
(encodedContent)
).replace(
2022-01-13 00:23:16 +08:00
/[!'()*]/g,
2024-08-08 02:59:26 +08:00
character =>
`%${/** @type {number} */ (character.codePointAt(0)).toString(16)}`
2022-01-13 00:23:16 +08:00
);
break;
}
default:
throw new Error(`Unsupported encoding '${encoding}'`);
}
return encodedContent;
};
/**
* @param {string} encoding encoding
* @param {string} content content
* @returns {Buffer} decoded content
*/
2022-01-17 23:52:55 +08:00
const decodeDataUriContent = (encoding, content) => {
const isBase64 = encoding === "base64";
2023-04-13 08:10:12 +08:00
if (isBase64) {
return Buffer.from(content, "base64");
}
// If we can't decode return the original body
try {
return Buffer.from(decodeURIComponent(content), "ascii");
} catch (_) {
return Buffer.from(content, "ascii");
}
2022-01-17 23:52:55 +08:00
};
const NO_TYPES = new Set();
const ASSET_TYPES = new Set([ASSET_MODULE_TYPE]);
const JS_TYPES = new Set(["javascript"]);
const CSS_TYPES = new Set(["css-url"]);
const CSS_AND_JS_TYPES = new Set(["javascript", "css-url"]);
const JS_AND_ASSET_TYPES = new Set([ASSET_MODULE_TYPE, "javascript"]);
const CSS_AND_ASSET_TYPES = new Set([ASSET_MODULE_TYPE, "css-url"]);
const JS_AND_CSS_AND_ASSET_TYPES = new Set([
ASSET_MODULE_TYPE,
"javascript",
"css-url"
]);
2021-11-29 20:37:38 +08:00
const DEFAULT_ENCODING = "base64";
2019-11-19 21:32:40 +08:00
class AssetGenerator extends Generator {
2019-11-16 00:27:36 +08:00
/**
* @param {ModuleGraph} moduleGraph the module graph
* @param {AssetGeneratorOptions["dataUrl"]=} dataUrlOptions the options for the data url
2024-08-08 02:59:26 +08:00
* @param {AssetModuleFilename=} filename override for output.assetModuleFilename
* @param {RawPublicPath=} publicPath override for output.assetModulePublicPath
* @param {AssetModuleOutputPath=} outputPath the output path for the emitted file which is not included in the runtime import
* @param {boolean=} emit generate output asset
2019-11-16 00:27:36 +08:00
*/
constructor(
moduleGraph,
dataUrlOptions,
filename,
publicPath,
outputPath,
emit
) {
2019-11-16 00:27:36 +08:00
super();
this.dataUrlOptions = dataUrlOptions;
this.filename = filename;
2021-03-16 02:03:19 +08:00
this.publicPath = publicPath;
this.outputPath = outputPath;
this.emit = emit;
this._moduleGraph = moduleGraph;
2019-11-16 00:27:36 +08:00
}
2021-11-29 20:37:38 +08:00
/**
* @param {NormalModule} module module
* @param {RuntimeTemplate} runtimeTemplate runtime template
* @returns {string} source file name
*/
getSourceFileName(module, runtimeTemplate) {
return makePathsRelative(
runtimeTemplate.compilation.compiler.context,
module.matchResource || module.resource,
runtimeTemplate.compilation.compiler.root
).replace(/^\.\//, "");
}
2022-03-11 21:34:17 +08:00
/**
* @param {NormalModule} module module for which the bailout reason should be determined
* @param {ConcatenationBailoutReasonContext} context context
* @returns {string | undefined} reason why this module can't be concatenated, undefined when it can be concatenated
*/
getConcatenationBailoutReason(module, context) {
return undefined;
}
2021-11-29 20:37:38 +08:00
/**
* @param {NormalModule} module module
2022-02-22 19:23:24 +08:00
* @returns {string} mime type
2021-11-29 20:37:38 +08:00
*/
getMimeType(module) {
2022-02-22 19:23:24 +08:00
if (typeof this.dataUrlOptions === "function") {
throw new Error(
"This method must not be called when dataUrlOptions is a function"
);
}
2021-11-29 20:37:38 +08:00
2023-06-17 01:13:03 +08:00
/** @type {string | boolean | undefined} */
2024-08-08 02:59:26 +08:00
let mimeType =
/** @type {AssetGeneratorDataUrlOptions} */
(this.dataUrlOptions).mimetype;
2021-11-29 20:37:38 +08:00
if (mimeType === undefined) {
2024-08-08 02:59:26 +08:00
const ext = path.extname(
/** @type {string} */
(module.nameForCondition())
);
2021-11-29 20:37:38 +08:00
if (
module.resourceResolveData &&
module.resourceResolveData.mimetype !== undefined
) {
mimeType =
module.resourceResolveData.mimetype +
module.resourceResolveData.parameters;
} else if (ext) {
mimeType = mimeTypes.lookup(ext);
2022-02-22 19:23:24 +08:00
if (typeof mimeType !== "string") {
throw new Error(
"DataUrl can't be generated automatically, " +
`because there is no mimetype for "${ext}" in mimetype database. ` +
'Either pass a mimetype via "generator.mimetype" or ' +
'use type: "asset/resource" to create a resource file instead of a DataUrl'
);
}
2021-11-29 20:37:38 +08:00
}
}
2022-02-22 19:23:24 +08:00
if (typeof mimeType !== "string") {
throw new Error(
"DataUrl can't be generated automatically. " +
'Either pass a mimetype via "generator.mimetype" or ' +
'use type: "asset/resource" to create a resource file instead of a DataUrl'
);
}
2023-06-17 01:13:03 +08:00
return /** @type {string} */ (mimeType);
2021-11-29 20:37:38 +08:00
}
/**
* @param {NormalModule} module module for which the code should be generated
* @returns {string} DataURI
*/
generateDataUri(module) {
const source = /** @type {Source} */ (module.originalSource());
let encodedSource;
if (typeof this.dataUrlOptions === "function") {
encodedSource = this.dataUrlOptions.call(null, source.source(), {
filename: module.matchResource || module.resource,
module
});
} else {
/** @type {"base64" | false | undefined} */
let encoding =
/** @type {AssetGeneratorDataUrlOptions} */
(this.dataUrlOptions).encoding;
if (
encoding === undefined &&
module.resourceResolveData &&
module.resourceResolveData.encoding !== undefined
) {
encoding = module.resourceResolveData.encoding;
}
if (encoding === undefined) {
encoding = DEFAULT_ENCODING;
}
const mimeType = this.getMimeType(module);
let encodedContent;
if (
module.resourceResolveData &&
module.resourceResolveData.encoding === encoding &&
decodeDataUriContent(
module.resourceResolveData.encoding,
module.resourceResolveData.encodedContent
).equals(source.buffer())
) {
encodedContent = module.resourceResolveData.encodedContent;
} else {
encodedContent = encodeDataUri(encoding, source);
}
encodedSource = `data:${mimeType}${
encoding ? `;${encoding}` : ""
},${encodedContent}`;
}
return encodedSource;
}
/**
* @private
* @param {NormalModule} module module for which the code should be generated
* @param {GenerateContext} generateContext context for generate
* @returns {string} the full content hash
*/
_getFullContentHash(module, { runtimeTemplate }) {
const hash = createHash(
/** @type {Algorithm} */
(runtimeTemplate.outputOptions.hashFunction)
);
if (runtimeTemplate.outputOptions.hashSalt) {
hash.update(runtimeTemplate.outputOptions.hashSalt);
}
hash.update(/** @type {Source} */ (module.originalSource()).buffer());
return /** @type {string} */ (
hash.digest(runtimeTemplate.outputOptions.hashDigest)
);
}
/**
* @private
* @param {string} fullContentHash the full content hash
* @param {GenerateContext} generateContext context for generate
* @returns {string} the content hash
*/
_getContentHash(fullContentHash, generateContext) {
return nonNumericOnlyHash(
fullContentHash,
/** @type {number} */
(generateContext.runtimeTemplate.outputOptions.hashDigestLength)
);
}
/**
* @private
* @param {NormalModule} module module for which the code should be generated
* @param {GenerateContext} generateContext context for generate
* @param {string} contentHash the content hash
* @returns {{ filename: string, originalFilename: string, assetInfo: AssetInfo }} info
*/
_getFilenameWithInfo(
module,
{ runtime, runtimeTemplate, chunkGraph },
contentHash
) {
const assetModuleFilename =
this.filename ||
/** @type {AssetModuleFilename} */
(runtimeTemplate.outputOptions.assetModuleFilename);
const sourceFilename = this.getSourceFileName(module, runtimeTemplate);
let { path: filename, info: assetInfo } =
runtimeTemplate.compilation.getAssetPathWithInfo(assetModuleFilename, {
module,
runtime,
filename: sourceFilename,
chunkGraph,
contentHash
});
const originalFilename = filename;
if (this.outputPath) {
const { path: outputPath, info } =
runtimeTemplate.compilation.getAssetPathWithInfo(this.outputPath, {
module,
runtime,
filename: sourceFilename,
chunkGraph,
contentHash
});
filename = path.posix.join(outputPath, filename);
assetInfo = mergeAssetInfo(assetInfo, info);
2022-03-11 21:34:17 +08:00
}
return { originalFilename, filename, assetInfo };
}
/**
* @private
* @param {NormalModule} module module for which the code should be generated
* @param {GenerateContext} generateContext context for generate
* @param {string} filename the filename
* @param {AssetInfo} assetInfo the asset info
* @param {string} contentHash the content hash
* @returns {{ assetPath: string, assetInfo: AssetInfo }} asset path and info
*/
_getAssetPathWithInfo(
module,
{ runtimeTemplate, runtime, chunkGraph, type, runtimeRequirements },
filename,
assetInfo,
contentHash
) {
const sourceFilename = this.getSourceFileName(module, runtimeTemplate);
let assetPath;
if (this.publicPath !== undefined && type === "javascript") {
const { path, info } = runtimeTemplate.compilation.getAssetPathWithInfo(
this.publicPath,
{
module,
runtime,
filename: sourceFilename,
chunkGraph,
contentHash
}
);
assetInfo = mergeAssetInfo(assetInfo, info);
assetPath = JSON.stringify(path + filename);
} else if (this.publicPath !== undefined && type === "css-url") {
const { path, info } = runtimeTemplate.compilation.getAssetPathWithInfo(
this.publicPath,
{
module,
runtime,
filename: sourceFilename,
chunkGraph,
contentHash
}
);
assetInfo = mergeAssetInfo(assetInfo, info);
assetPath = path + filename;
} else if (type === "javascript") {
// add __webpack_require__.p
runtimeRequirements.add(RuntimeGlobals.publicPath);
assetPath = runtimeTemplate.concatenation(
{ expr: RuntimeGlobals.publicPath },
filename
);
} else if (type === "css-url") {
const compilation = runtimeTemplate.compilation;
const path =
compilation.outputOptions.publicPath === "auto"
? CssUrlDependency.PUBLIC_PATH_AUTO
: compilation.getAssetPath(
/** @type {TemplatePath} */
(compilation.outputOptions.publicPath),
{
hash: compilation.hash
}
2021-03-16 02:03:19 +08:00
);
assetPath = path + filename;
}
assetInfo = { sourceFilename, ...assetInfo };
return { assetPath, assetInfo };
}
/**
* @param {NormalModule} module module for which the code should be generated
* @param {GenerateContext} generateContext context for generate
* @returns {Source} generated code
*/
generate(module, generateContext) {
const {
type,
getData,
runtimeTemplate,
runtimeRequirements,
concatenationScope
} = generateContext;
let content;
const needContent = type === "javascript" || type === "css-url";
const data = getData
? /** @type {GenerateContext["getData"]} */
(getData)()
: undefined;
if (
/** @type {BuildInfo} */
(module.buildInfo).dataUrl &&
needContent
) {
if (data && data.has("url")) {
content = data.get("url");
} else {
const encodedSource = this.generateDataUri(module);
content = JSON.stringify(encodedSource);
if (data) {
2024-10-11 22:47:32 +08:00
data.set("url", { [type]: encodedSource });
2022-03-11 21:34:17 +08:00
}
}
} else {
/** @type {string} */
let fullHash;
if (data && data.has("fullContentHash")) {
fullHash = data.get("fullContentHash");
} else {
fullHash = this._getFullContentHash(module, generateContext);
if (data) {
data.set("fullContentHash", fullHash);
2019-11-26 16:53:47 +08:00
}
}
if (!(/** @type {BuildInfo} */ (module.buildInfo).fullContentHash)) {
/** @type {BuildInfo} */
(module.buildInfo).fullContentHash = fullHash;
}
/** @type {string} */
let contentHash;
if (data && data.has("contentHash")) {
contentHash = data.get("contentHash");
} else {
contentHash = this._getContentHash(fullHash, generateContext);
if (data) {
data.set("contentHash", contentHash);
}
}
let originalFilename;
let filename;
let assetInfo = {};
if (data && data.has("originalFilename")) {
originalFilename = data.get("originalFilename");
} else {
({ originalFilename, filename, assetInfo } = this._getFilenameWithInfo(
module,
generateContext,
contentHash
));
if (data) {
data.set("filename", filename);
data.set("assetInfo", assetInfo);
data.set("filenameWithInfo", originalFilename);
}
}
assetInfo =
data && data.has("assetInfo") ? data.get("assetInfo") : assetInfo;
/** @type {string} */
let assetPath;
if (type === "javascript" || type === "css-url") {
({ assetPath, assetInfo } = this._getAssetPathWithInfo(
module,
generateContext,
originalFilename,
assetInfo,
contentHash
));
if (data) {
data.set("url", { [type]: assetPath, ...data.get("url") });
}
}
if (data) {
data.set("assetInfo", assetInfo);
}
// Due to code generation caching module.buildInfo.XXX can't used to store such information
// It need to be stored in the code generation results instead, where it's cached too
// TODO webpack 6 For back-compat reasons we also store in on module.buildInfo
if (!module.buildInfo.filename) {
/** @type {BuildInfo} */
(module.buildInfo).filename = filename;
}
if (!module.buildInfo.assetInfo) {
/** @type {BuildInfo} */
(module.buildInfo).assetInfo = assetInfo;
}
content = assetPath;
}
if (type === "javascript") {
if (concatenationScope) {
concatenationScope.registerNamespaceExport(
ConcatenationScope.NAMESPACE_OBJECT_EXPORT
);
return new RawSource(
`${runtimeTemplate.supportsConst() ? "const" : "var"} ${
ConcatenationScope.NAMESPACE_OBJECT_EXPORT
} = ${content};`
);
}
runtimeRequirements.add(RuntimeGlobals.module);
return new RawSource(`${RuntimeGlobals.module}.exports = ${content};`);
} else if (type === "css-url") {
return null;
}
return /** @type {Source} */ (module.originalSource());
}
/**
* @param {NormalModule} module fresh module
* @returns {Set<string>} available types (do not mutate)
*/
getTypes(module) {
const sourceTypes = new Set();
const connections = this._moduleGraph.getIncomingConnections(module);
for (const connection of connections) {
sourceTypes.add(connection.originModule.type.split("/")[0]);
}
2021-07-05 17:22:13 +08:00
if ((module.buildInfo && module.buildInfo.dataUrl) || this.emit === false) {
if (sourceTypes) {
if (sourceTypes.has("javascript") && sourceTypes.has("css")) {
return CSS_AND_JS_TYPES;
} else if (sourceTypes.has("javascript")) {
return JS_TYPES;
} else if (sourceTypes.has("css")) {
return CSS_TYPES;
}
}
return NO_TYPES;
}
if (sourceTypes) {
if (sourceTypes.has("javascript") && sourceTypes.has("css")) {
return JS_AND_CSS_AND_ASSET_TYPES;
} else if (sourceTypes.has("javascript")) {
return JS_AND_ASSET_TYPES;
} else if (sourceTypes.has("css")) {
return CSS_AND_ASSET_TYPES;
}
}
return ASSET_TYPES;
}
/**
* @param {NormalModule} module the module
* @param {string=} type source type
* @returns {number} estimate size of the module
*/
2019-11-27 04:24:41 +08:00
getSize(module, type) {
switch (type) {
case ASSET_MODULE_TYPE: {
const originalSource = module.originalSource();
2019-06-06 05:29:53 +08:00
if (!originalSource) {
return 0;
}
return originalSource.size();
}
default:
2021-07-05 17:22:13 +08:00
if (module.buildInfo && module.buildInfo.dataUrl) {
const originalSource = module.originalSource();
if (!originalSource) {
return 0;
}
// roughly for data url
// Example: m.exports="data:image/png;base64,ag82/f+2=="
// 4/3 = base64 encoding
// 34 = ~ data url header + footer + rounding
return originalSource.size() * 1.34 + 36;
}
2024-07-31 04:21:27 +08:00
// it's only estimated so this number is probably fine
// Example: m.exports=r.p+"0123456789012345678901.ext"
return 42;
}
}
/**
* @param {Hash} hash hash that will be modified
* @param {UpdateHashContext} updateHashContext context for updating hash
*/
2024-08-09 23:42:37 +08:00
updateHash(hash, updateHashContext) {
const { module } = updateHashContext;
2024-08-08 02:59:26 +08:00
if (
/** @type {BuildInfo} */
(module.buildInfo).dataUrl
) {
2021-11-29 20:37:38 +08:00
hash.update("data-url");
2022-02-22 19:23:24 +08:00
// this.dataUrlOptions as function should be pure and only depend on input source and filename
// therefore it doesn't need to be hashed
2021-11-29 20:37:38 +08:00
if (typeof this.dataUrlOptions === "function") {
2022-02-22 19:23:24 +08:00
const ident = /** @type {{ ident?: string }} */ (this.dataUrlOptions)
.ident;
if (ident) hash.update(ident);
2021-11-29 20:37:38 +08:00
} else {
2024-08-08 02:59:26 +08:00
const dataUrlOptions =
/** @type {AssetGeneratorDataUrlOptions} */
(this.dataUrlOptions);
2022-02-28 16:45:00 +08:00
if (
2024-08-08 02:59:26 +08:00
dataUrlOptions.encoding &&
dataUrlOptions.encoding !== DEFAULT_ENCODING
2022-02-28 16:45:00 +08:00
) {
2024-08-08 02:59:26 +08:00
hash.update(dataUrlOptions.encoding);
2022-02-28 16:45:00 +08:00
}
2024-08-08 02:59:26 +08:00
if (dataUrlOptions.mimetype) hash.update(dataUrlOptions.mimetype);
2022-02-22 19:23:24 +08:00
// computed mimetype depends only on module filename which is already part of the hash
2021-11-29 20:37:38 +08:00
}
} else {
hash.update("resource");
2024-08-09 23:42:37 +08:00
const { module, chunkGraph, runtime } = updateHashContext;
const runtimeTemplate =
/** @type {NonNullable<UpdateHashContext["runtimeTemplate"]>} */
(updateHashContext.runtimeTemplate);
2021-11-29 20:37:38 +08:00
const pathData = {
module,
runtime,
filename: this.getSourceFileName(module, runtimeTemplate),
chunkGraph,
contentHash: runtimeTemplate.contentHashReplacement
};
if (typeof this.publicPath === "function") {
2022-02-22 19:23:24 +08:00
hash.update("path");
const assetInfo = {};
hash.update(this.publicPath(pathData, assetInfo));
hash.update(JSON.stringify(assetInfo));
2021-11-29 20:37:38 +08:00
} else if (this.publicPath) {
2022-02-22 19:23:24 +08:00
hash.update("path");
hash.update(this.publicPath);
2021-11-29 20:37:38 +08:00
} else {
hash.update("no-path");
}
const assetModuleFilename =
2024-08-09 23:42:37 +08:00
this.filename ||
/** @type {AssetModuleFilename} */
(runtimeTemplate.outputOptions.assetModuleFilename);
2022-02-22 19:23:24 +08:00
const { path: filename, info } =
2021-11-29 20:37:38 +08:00
runtimeTemplate.compilation.getAssetPathWithInfo(
assetModuleFilename,
pathData
);
hash.update(filename);
2022-02-22 19:23:24 +08:00
hash.update(JSON.stringify(info));
2021-11-29 20:37:38 +08:00
}
}
}
2019-11-19 21:32:40 +08:00
module.exports = AssetGenerator;