diff --git a/package.json b/package.json index 639a5a2..d4c6f97 100644 --- a/package.json +++ b/package.json @@ -17,12 +17,15 @@ "node": ">= 18.12.0" }, "scripts": { - "start": "npm run build -- -w", + "start": "npm-run-all -p \"watch:**\"", "clean": "del-cli dist types", "prebuild": "npm run clean", - "build:types": "tsc --declaration --emitDeclarationOnly && prettier \"types/**/*.ts\" --write", + "build:types": "tsc --declaration --emitDeclarationOnly", + "postbuild:types": "prettier \"types/**/*.ts\" --write", "build:code": "cross-env NODE_ENV=production babel src -d dist --copy-files", "build": "npm-run-all -p \"build:**\"", + "watch:types": "npm run build:types -- -w", + "watch:code": "npm run build:code -- -w", "commitlint": "commitlint --from=master", "security": "npm audit --production", "lint:prettier": "prettier --cache --list-different .", diff --git a/src/index.js b/src/index.js index 241d8f2..27474f6 100644 --- a/src/index.js +++ b/src/index.js @@ -281,8 +281,18 @@ class ImageMinimizerPlugin { * @returns {Promise>} */ const getFromCache = async (transformer) => { - const cacheName = getSerializeJavascript()({ name, transformer }); const eTag = cache.getLazyHashedEtag(source); + const cacheName = getSerializeJavascript()({ + eTag: eTag.toString(), + transformer: (Array.isArray(transformer) + ? transformer + : [transformer] + ).map(({ implementation, options }) => ({ + implementation, + options, + })), + }); + const cacheItem = cache.getItemCache(cacheName, eTag); const output = await cacheItem.getPromise(); @@ -338,6 +348,9 @@ class ImageMinimizerPlugin { const sourceFromInputSource = inputSource.source(); + const pluginName = this.constructor.name; + const logger = compilation.getLogger(pluginName); + if (!output) { input = sourceFromInputSource; @@ -356,16 +369,19 @@ class ImageMinimizerPlugin { generateFilename: compilation.getAssetPath.bind(compilation), }); - output = await worker(minifyOptions); + output = await cacheItem.providePromise(async () => { + logger.debug(`optimize cache miss: ${name}`); - output.source = new RawSource(output.data); + const result = await worker(minifyOptions); - await cacheItem.storePromise({ - source: output.source, - info: output.info, - filename: output.filename, - warnings: output.warnings, - errors: output.errors, + return { + data: result.data, + source: new RawSource(result.data), + info: result.info, + filename: result.filename, + warnings: result.warnings, + errors: result.errors, + }; }); } diff --git a/src/loader.js b/src/loader.js index 94453a4..c734ea4 100644 --- a/src/loader.js +++ b/src/loader.js @@ -6,7 +6,9 @@ const { IMAGE_MINIMIZER_PLUGIN_INFO_MAPPINGS, ABSOLUTE_URL_REGEX, WINDOWS_PATH_REGEX, + memoize, } = require("./utils.js"); +const getSerializeJavascript = memoize(() => require("serialize-javascript")); /** @typedef {import("schema-utils/declarations/validate").Schema} Schema */ /** @typedef {import("webpack").Compilation} Compilation */ @@ -210,18 +212,42 @@ async function loader(content) { ? this.resourcePath : path.relative(this.rootContext, this.resourcePath); - const minifyOptions = - /** @type {import("./index").InternalWorkerOptions} */ ({ - input: content, - filename, - severityError, - transformer, - generateFilename: - /** @type {Compilation} */ - (this._compilation).getAssetPath.bind(this._compilation), - }); - - const output = await worker(minifyOptions); + if (!this._compilation || !this._compiler) { + callback(new Error("_compilation and/or _compiler unavailable")); + return; + } + + const logger = this._compilation.getLogger("ImageMinimizerPlugin"); + const cache = this._compilation.getCache("ImageMinimizerWebpackPlugin"); + + const { RawSource } = this._compiler.webpack.sources; + const eTag = cache.getLazyHashedEtag(new RawSource(content)); + const cacheName = getSerializeJavascript()({ + eTag: eTag.toString(), + transformer: (Array.isArray(transformer) ? transformer : [transformer]).map( + (each) => ({ + implementation: each.implementation, + options: each.options, + }), + ), + }); + const cacheItem = cache.getItemCache(cacheName, eTag); + const output = await cacheItem.providePromise(() => { + const minifyOptions = + /** @type {import("./index").InternalWorkerOptions} */ ({ + input: content, + filename, + severityError, + transformer, + generateFilename: + /** @type {Compilation} */ + (this._compilation).getAssetPath.bind(this._compilation), + }); + + logger.debug(`loader cache miss: ${filename}`); + + return worker(minifyOptions); + }); if (output.errors && output.errors.length > 0) { for (const error of output.errors) {