| | |
| | | /** @typedef {import("../Compiler")} Compiler */ |
| | | /** @typedef {typeof import("../util/Hash")} Hash */ |
| | | |
| | | /** |
| | | * Defines the comparator type used by this module. |
| | | * @template T |
| | | * @typedef {import("../util/comparators").Comparator<T>} Comparator |
| | | */ |
| | | |
| | | /** @type {Hashes} */ |
| | | const EMPTY_SET = new Set(); |
| | | |
| | | /** |
| | | * Adds the provided item or item to this object. |
| | | * @template T |
| | | * @param {T | T[]} itemOrItems item or items |
| | | * @param {Set<T>} list list |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Map and deduplicate buffers. |
| | | * @template T |
| | | * @param {T[]} input list |
| | | * @param {(item: T) => Buffer} fn map function |
| | |
| | | // Buffer.equals compares size first so this should be efficient enough |
| | | // If it becomes a performance problem we can use a map and group by size |
| | | // instead of looping over all assets. |
| | | /** @type {Buffer[]} */ |
| | | const result = []; |
| | | outer: for (const value of input) { |
| | | const buf = fn(value); |
| | |
| | | */ |
| | | const quoteMeta = (str) => str.replace(/[-[\]\\/{}()*+?.^$|]/g, "\\$&"); |
| | | |
| | | /** @type {WeakMap<Source, CachedSource>} */ |
| | | const cachedSourceMap = new WeakMap(); |
| | | |
| | | /** |
| | | * Returns cached source. |
| | | * @param {Source} source source |
| | | * @returns {CachedSource} cached source |
| | | */ |
| | |
| | | return newSource; |
| | | }; |
| | | |
| | | /** @typedef {Set<string>} OwnHashes */ |
| | | /** @typedef {Set<string>} ReferencedHashes */ |
| | | /** @typedef {Set<string>} Hashes */ |
| | | |
| | | /** |
| | | * Defines the asset info for real content hash type used by this module. |
| | | * @typedef {object} AssetInfoForRealContentHash |
| | | * @property {string} name |
| | | * @property {AssetInfo} info |
| | |
| | | * @property {RawSource | undefined} newSource |
| | | * @property {RawSource | undefined} newSourceWithoutOwn |
| | | * @property {string} content |
| | | * @property {OwnHashes | undefined} ownHashes |
| | | * @property {Hashes | undefined} ownHashes |
| | | * @property {Promise<void> | undefined} contentComputePromise |
| | | * @property {Promise<void> | undefined} contentComputeWithoutOwnPromise |
| | | * @property {ReferencedHashes | undefined} referencedHashes |
| | | * @property {Hashes | undefined} referencedHashes |
| | | * @property {Hashes} hashes |
| | | */ |
| | | |
| | | /** |
| | | * Defines the compilation hooks type used by this module. |
| | | * @typedef {object} CompilationHooks |
| | | * @property {SyncBailHook<[Buffer[], string], string | void>} updateHash |
| | | */ |
| | |
| | | const compilationHooksMap = new WeakMap(); |
| | | |
| | | /** |
| | | * Defines the real content hash plugin options type used by this module. |
| | | * @typedef {object} RealContentHashPluginOptions |
| | | * @property {HashFunction} hashFunction the hash function to use |
| | | * @property {HashDigest} hashDigest the hash digest to use |
| | |
| | | |
| | | class RealContentHashPlugin { |
| | | /** |
| | | * Returns the attached hooks. |
| | | * @param {Compilation} compilation the compilation |
| | | * @returns {CompilationHooks} the attached hooks |
| | | */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * Creates an instance of RealContentHashPlugin. |
| | | * @param {RealContentHashPluginOptions} options options |
| | | */ |
| | | constructor({ hashFunction, hashDigest }) { |
| | | /** @type {HashFunction} */ |
| | | this._hashFunction = hashFunction; |
| | | /** @type {HashDigest} */ |
| | | this._hashDigest = hashDigest; |
| | | } |
| | | |
| | | /** |
| | | * Apply the plugin |
| | | * Applies the plugin by registering its hooks on the compiler. |
| | | * @param {Compiler} compiler the compiler instance |
| | | * @returns {void} |
| | | */ |
| | |
| | | ); |
| | | [asset.referencedHashes, asset.ownHashes] = |
| | | await cacheAnalyse.providePromise(name, etag, () => { |
| | | /** @type {Hashes} */ |
| | | const referencedHashes = new Set(); |
| | | /** @type {Hashes} */ |
| | | const ownHashes = new Set(); |
| | | const inContent = content.match(hashRegExp); |
| | | if (inContent) { |
| | |
| | | }) |
| | | ); |
| | | /** |
| | | * Returns the referenced hashes. |
| | | * @param {string} hash the hash |
| | | * @returns {undefined | ReferencedHashes} the referenced hashes |
| | | * @returns {undefined | Hashes} the referenced hashes |
| | | */ |
| | | const getDependencies = (hash) => { |
| | | const assets = hashToAssets.get(hash); |
| | | if (!assets) { |
| | | const referencingAssets = assetsWithInfo.filter((asset) => |
| | | /** @type {ReferencedHashes} */ (asset.referencedHashes).has( |
| | | hash |
| | | ) |
| | | /** @type {Hashes} */ (asset.referencedHashes).has(hash) |
| | | ); |
| | | const err = new WebpackError(`RealContentHashPlugin |
| | | Some kind of unexpected caching problem occurred. |
| | |
| | | compilation.errors.push(err); |
| | | return; |
| | | } |
| | | /** @type {Hashes} */ |
| | | const hashes = new Set(); |
| | | for (const { referencedHashes, ownHashes } of assets) { |
| | | if (!(/** @type {OwnHashes} */ (ownHashes).has(hash))) { |
| | | for (const hash of /** @type {OwnHashes} */ (ownHashes)) { |
| | | if (!(/** @type {Hashes} */ (ownHashes).has(hash))) { |
| | | for (const hash of /** @type {Hashes} */ (ownHashes)) { |
| | | hashes.add(hash); |
| | | } |
| | | } |
| | | for (const hash of /** @type {ReferencedHashes} */ ( |
| | | referencedHashes |
| | | )) { |
| | | for (const hash of /** @type {Hashes} */ (referencedHashes)) { |
| | | hashes.add(hash); |
| | | } |
| | | } |
| | | return hashes; |
| | | }; |
| | | /** |
| | | * Returns the hash info. |
| | | * @param {string} hash the hash |
| | | * @returns {string} the hash info |
| | | */ |
| | |
| | | (a) => a.name |
| | | )})`; |
| | | }; |
| | | /** @type {Set<string>} */ |
| | | /** @type {Hashes} */ |
| | | const hashesInOrder = new Set(); |
| | | for (const hash of hashToAssets.keys()) { |
| | | /** |
| | | * Processes the provided hash. |
| | | * @param {string} hash the hash |
| | | * @param {Set<string>} stack stack of hashes |
| | | */ |
| | |
| | | /** @type {Map<string, string>} */ |
| | | const hashToNewHash = new Map(); |
| | | /** |
| | | * Returns etag. |
| | | * @param {AssetInfoForRealContentHash} asset asset info |
| | | * @returns {Etag} etag |
| | | */ |
| | |
| | | cacheGenerate.mergeEtags( |
| | | cacheGenerate.getLazyHashedEtag(asset.source), |
| | | Array.from( |
| | | /** @type {ReferencedHashes} */ (asset.referencedHashes), |
| | | /** @type {Hashes} */ (asset.referencedHashes), |
| | | (hash) => hashToNewHash.get(hash) |
| | | ).join("|") |
| | | ); |
| | | /** |
| | | * Compute new content. |
| | | * @param {AssetInfoForRealContentHash} asset asset info |
| | | * @returns {Promise<void>} |
| | | */ |
| | |
| | | if (asset.contentComputePromise) return asset.contentComputePromise; |
| | | return (asset.contentComputePromise = (async () => { |
| | | if ( |
| | | /** @type {OwnHashes} */ (asset.ownHashes).size > 0 || |
| | | [ |
| | | .../** @type {ReferencedHashes} */ (asset.referencedHashes) |
| | | ].some((hash) => hashToNewHash.get(hash) !== hash) |
| | | /** @type {Hashes} */ (asset.ownHashes).size > 0 || |
| | | [.../** @type {Hashes} */ (asset.referencedHashes)].some( |
| | | (hash) => hashToNewHash.get(hash) !== hash |
| | | ) |
| | | ) { |
| | | const identifier = asset.name; |
| | | const etag = getEtag(asset); |
| | |
| | | })()); |
| | | }; |
| | | /** |
| | | * Compute new content without own. |
| | | * @param {AssetInfoForRealContentHash} asset asset info |
| | | * @returns {Promise<void>} |
| | | */ |
| | |
| | | } |
| | | return (asset.contentComputeWithoutOwnPromise = (async () => { |
| | | if ( |
| | | /** @type {OwnHashes} */ (asset.ownHashes).size > 0 || |
| | | [ |
| | | .../** @type {ReferencedHashes} */ (asset.referencedHashes) |
| | | ].some((hash) => hashToNewHash.get(hash) !== hash) |
| | | /** @type {Hashes} */ (asset.ownHashes).size > 0 || |
| | | [.../** @type {Hashes} */ (asset.referencedHashes)].some( |
| | | (hash) => hashToNewHash.get(hash) !== hash |
| | | ) |
| | | ) { |
| | | const identifier = `${asset.name}|without-own`; |
| | | const etag = getEtag(asset); |
| | |
| | | hashRegExp, |
| | | (hash) => { |
| | | if ( |
| | | /** @type {OwnHashes} */ |
| | | /** @type {Hashes} */ |
| | | (asset.ownHashes).has(hash) |
| | | ) { |
| | | return ""; |
| | |
| | | } |
| | | })()); |
| | | }; |
| | | /** @type {Comparator<AssetInfoForRealContentHash>} */ |
| | | const comparator = compareSelect((a) => a.name, compareStrings); |
| | | for (const oldHash of hashesInOrder) { |
| | | const assets = |
| | |
| | | assets.sort(comparator); |
| | | await Promise.all( |
| | | assets.map((asset) => |
| | | /** @type {OwnHashes} */ (asset.ownHashes).has(oldHash) |
| | | /** @type {Hashes} */ (asset.ownHashes).has(oldHash) |
| | | ? computeNewContentWithoutOwn(asset) |
| | | : computeNewContent(asset) |
| | | ) |
| | | ); |
| | | const assetsContent = mapAndDeduplicateBuffers(assets, (asset) => { |
| | | if (/** @type {OwnHashes} */ (asset.ownHashes).has(oldHash)) { |
| | | if (/** @type {Hashes} */ (asset.ownHashes).has(oldHash)) { |
| | | return asset.newSourceWithoutOwn |
| | | ? asset.newSourceWithoutOwn.buffer() |
| | | : asset.source.buffer(); |
| | |
| | | ); |
| | | |
| | | const infoUpdate = {}; |
| | | const hash = /** @type {string} */ (asset.info.contenthash); |
| | | const hash = |
| | | /** @type {Exclude<AssetInfo["contenthash"], undefined>} */ |
| | | (asset.info.contenthash); |
| | | infoUpdate.contenthash = Array.isArray(hash) |
| | | ? hash.map( |
| | | (hash) => /** @type {string} */ (hashToNewHash.get(hash)) |