diff --git a/packages/loader/lib/index.js b/packages/loader/lib/index.js index 5ab51e0fb..ba961f215 100644 --- a/packages/loader/lib/index.js +++ b/packages/loader/lib/index.js @@ -5,13 +5,20 @@ * @typedef {Pick} Defaults * @typedef {Omit} Options * @typedef {import('webpack').LoaderContext} LoaderContext + * @typedef {import('webpack').Compiler} WebpackCompiler * @typedef {(vfileCompatible: VFileCompatible) => Promise} Process */ +import {createHash} from 'node:crypto' import {SourceMapGenerator} from 'source-map' import {createFormatAwareProcessors} from '@mdx-js/mdx/lib/util/create-format-aware-processors.js' -/** @type {WeakMap} */ +const own = {}.hasOwnProperty + +// Note: the cache is heavily inspired by: +// +const marker = /** @type {WebpackCompiler} */ ({}) +/** @type {WeakMap>} */ const cache = new WeakMap() /** @@ -28,6 +35,10 @@ export function loader(value, callback) { const defaults = this.sourceMap ? {SourceMapGenerator} : {} const options = /** @type {CompileOptions} */ (this.getOptions()) const config = {...defaults, ...options} + const hash = getOptionsHash(options) + // Some loaders set `undefined` (see `TypeStrong/ts-loader`). + /* c8 ignore next */ + const compiler = this._compiler || marker /* Removed option. */ /* c8 ignore next 5 */ @@ -37,11 +48,18 @@ export function loader(value, callback) { ) } - let process = cache.get(config) + let map = cache.get(compiler) + + if (!map) { + map = new Map() + cache.set(compiler, map) + } + + let process = map.get(hash) if (!process) { process = createFormatAwareProcessors(config).process - cache.set(config, process) + map.set(hash, process) } process({value, path: this.resourcePath}).then((file) => { @@ -49,3 +67,25 @@ export function loader(value, callback) { return file }, callback) } + +/** + * @param {Options} options + */ +function getOptionsHash(options) { + const hash = createHash('sha256') + /** @type {keyof Options} */ + let key + + for (key in options) { + if (own.call(options, key)) { + const value = options[key] + + if (value !== undefined) { + const valueString = JSON.stringify(value) + hash.update(key + valueString) + } + } + } + + return hash.digest('hex').slice(0, 16) +}