Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [18.x, 20.x, 22.x, 24.x]
node-version: [20.x, 22.x, 24.x, 25.x]
webpack-version: [latest]

runs-on: ${{ matrix.os }}
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -479,16 +479,16 @@ The plugin supports the following query parameters:
Examples:

```js
const myImage1 = new URL("image.png?width=150&height=120", import.meta.url);
const myImage2 = new URL("image.png?w=150&h=120", import.meta.url);
const myImage1 = new URL("../image.png?width=150&height=120", import.meta.url);
const myImage2 = new URL("./image.png?w=150&h=120", import.meta.url);
// You can omit one of the parameters to auto-scale
const myImage3 = new URL("image.png?w=150", import.meta.url);
const myImage3 = new URL("./image.png?w=150", import.meta.url);
// It works with the `preset` query parameter
const myImage4 = new URL("image.png?as=webp&w=150&h=120", import.meta.url);
const myImage4 = new URL("./image.png?as=webp&w=150&h=120", import.meta.url);
// You can use `auto` to reset `width` or `height` from the `preset` option
const myImage5 = new URL("image.png?as=webp&w=150&h=auto", import.meta.url);
const myImage5 = new URL("./image.png?as=webp&w=150&h=auto", import.meta.url);
// You can use `unit` to get the non-retina resize of images that are retina sized
const myImage6 = new URL("image.png?width=50&unit=percent", import.meta.url);
const myImage6 = new URL("./image.png?width=50&unit=percent", import.meta.url);
```

```css
Expand Down
6,313 changes: 3,411 additions & 2,902 deletions package-lock.json

Large diffs are not rendered by default.

29 changes: 9 additions & 20 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,38 +60,28 @@
},
"dependencies": {
"schema-utils": "^4.2.0",
"serialize-javascript": "^6.0.2"
"serialize-javascript": "^7.0.3"
},
"devDependencies": {
"@babel/cli": "^7.24.7",
"@babel/core": "^7.25.2",
"@babel/preset-env": "^7.25.3",
"@commitlint/cli": "^19.3.0",
"@commitlint/config-conventional": "^19.2.2",
"@eslint/js": "^9.33.0",
"@eslint/markdown": "^7.0.0",
"@commitlint/cli": "^20.4.2",
"@commitlint/config-conventional": "^20.4.2",
"@squoosh/lib": "^0.5.3",
"@stylistic/eslint-plugin": "^5.2.3",
"@types/imagemin": "^9.0.0",
"@types/node": "^20.14.9",
"@types/serialize-javascript": "^5.0.4",
"copy-webpack-plugin": "^13.0.1",
"copy-webpack-plugin": "^14.0.0",
"cross-env": "^7.0.3",
"cspell": "^8.13.1",
"cspell": "^9.7.0",
"css-loader": "^7.1.2",
"del-cli": "^6.0.0",
"del-cli": "^7.0.0",
"eslint": "^9.31.0",
"eslint-config-prettier": "^10.1.8",
"eslint-config-webpack": "^4.4.2",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jest": "^29.0.1",
"eslint-plugin-jsdoc": "^53.0.1",
"eslint-plugin-n": "^17.21.0",
"eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-unicorn": "^60.0.0",
"eslint-plugin-jest": "^29.15.0",
"file-loader": "^6.2.0",
"file-type": "^16.5.4",
"globals": "^16.3.0",
"husky": "^9.1.4",
"image-size": "^2.0.2",
"imagemin": "^9.0.0",
Expand All @@ -102,7 +92,7 @@
"imagemin-svgo": "^12.0.0",
"imagemin-webp": "^8.0.0",
"jest": "^30.0.0",
"lint-staged": "^15.2.8",
"lint-staged": "^16.3.1",
"memfs": "^4.11.1",
"mini-css-extract-plugin": "^2.9.0",
"npm-run-all": "^4.1.5",
Expand All @@ -114,7 +104,6 @@
"svgo": "^4.0.0",
"tempy": "^3.1.0",
"typescript": "^5.5.3",
"typescript-eslint": "^8.38.0",
"url-loader": "^4.1.1",
"webpack": "^5.92.1"
},
Expand All @@ -136,6 +125,6 @@
}
},
"engines": {
"node": ">= 18.12.0"
"node": ">= 20.9.0"
}
}
20 changes: 10 additions & 10 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ const worker = require("./worker");
/** @typedef {import("./utils.js").imageminMinify} ImageminMinifyFunction */
/** @typedef {import("./utils.js").squooshMinify} SquooshMinifyFunction */

// eslint-disable-next-line jsdoc/reject-any-type
/** @typedef {any} EXPECTED_ANY */

/** @typedef {RegExp | string} Rule */
/** @typedef {Rule[] | Rule} Rules */

Expand All @@ -48,8 +51,8 @@ const worker = require("./worker");
* @typedef {object} WorkerResult
* @property {string} filename filename
* @property {Buffer} data data buffer
* @property {Array<Error>} warnings warnings
* @property {Array<Error>} errors errors
* @property {Error[]} warnings warnings
* @property {Error[]} errors errors
* @property {AssetInfo & { [worker.isFilenameProcessed]?: boolean }} info asset info
*/

Expand All @@ -64,9 +67,8 @@ const worker = require("./worker");
* @property {Transformer<T> | Transformer<T>[]} transformer transformer
*/

// eslint-disable-next-line jsdoc/no-restricted-syntax
/**
* @typedef {{ [key: string]: any }} CustomOptions
* @typedef {{ [key: string]: EXPECTED_ANY }} CustomOptions
*/

/**
Expand Down Expand Up @@ -156,8 +158,8 @@ const worker = require("./worker");
* @property {Rule=} test test to match files against
* @property {Rule=} include files to include
* @property {Rule=} exclude files to exclude
* @property {T extends any[] ? { [P in keyof T]: Minimizer<T[P]> } : Minimizer<T> | Minimizer<T>[]=} minimizer allows to set the minimizer
* @property {G extends any[] ? { [P in keyof G]: Generator<G[P]> } : Generator<G>[]=} generator allows to set the generator
* @property {T extends EXPECTED_ANY[] ? { [P in keyof T]: Minimizer<T[P]> } : Minimizer<T> | Minimizer<T>[]=} minimizer allows to set the minimizer
* @property {G extends EXPECTED_ANY[] ? { [P in keyof G]: Generator<G[P]> } : Generator<G>[]=} generator allows to set the generator
* @property {boolean=} loader automatically adding `image-loader`.
* @property {number=} concurrency maximum number of concurrency optimization processes in one time
* @property {string=} severityError allows to choose how errors are displayed
Expand Down Expand Up @@ -267,7 +269,7 @@ class ImageMinimizerPlugin {

/**
* @template Z
* @param {Transformer<Z> | Array<Transformer<Z>>} transformer transformer
* @param {Transformer<Z> | Transformer<Z>[]} transformer transformer
* @returns {Promise<Task<Z>>} generated task
*/
const getFromCache = async (transformer) => {
Expand Down Expand Up @@ -318,10 +320,8 @@ class ImageMinimizerPlugin {
const limit = Math.max(
1,
this.options.concurrency ||
// eslint-disable-next-line n/no-unsupported-features/node-builtins
(typeof os.availableParallelism === "function"
? {
// eslint-disable-next-line n/no-unsupported-features/node-builtins
length: os.availableParallelism(),
}
: os.cpus() || {
Expand Down Expand Up @@ -511,7 +511,7 @@ class ImageMinimizerPlugin {

generatorForLoader =
importGenerators.length > 0
? /** @type {G extends any[] ? { [P in keyof G]: Generator<G[P]>; } : Generator<G>[]} */
? /** @type {G extends EXPECTED_ANY[] ? { [P in keyof G]: Generator<G[P]> } : Generator<G>[]} */
(importGenerators)
: undefined;
}
Expand Down
2 changes: 1 addition & 1 deletion src/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function processSizeQuery(transformers, widthQuery, heightQuery, unitQuery) {
const minimizer = { ...transformer };

const minimizerOptions = {
.../** @type {{ options: import("./index").BasicTransformerOptions<T> & { resize?: import("./index").ResizeOptions }}} */
.../** @type {{ options: import("./index").BasicTransformerOptions<T> & { resize?: import("./index").ResizeOptions } }} */
(minimizer).options,
};

Expand Down
26 changes: 11 additions & 15 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ const path = require("node:path");

/** @typedef {import("./index").WorkerResult} WorkerResult */
/** @typedef {import("./index").CustomOptions} CustomOptions */
/** @typedef {import("./index").EXPECTED_ANY} EXPECTED_ANY */
/** @typedef {import("webpack").WebpackError} WebpackError */
/** @typedef {import("webpack").Module} Module */
/** @typedef {import("webpack").AssetInfo} AssetInfo */

// eslint-disable-next-line jsdoc/no-restricted-syntax
/** @typedef {any} EXPECTED_ANY */

/**
* @template T
* @typedef {() => Promise<T>} Task
Expand Down Expand Up @@ -127,7 +125,7 @@ const stringToBytes = (string) =>

/**
* @param {ArrayBuffer | ArrayLike<number>} input input buffer
* @returns {{ext: string, mime: string} | undefined} file type info
* @returns {{ ext: string, mime: string } | undefined} file type info
*/
function fileTypeFromBuffer(input) {
if (
Expand All @@ -150,7 +148,7 @@ function fileTypeFromBuffer(input) {

/**
* @param {number[]} header header bytes
* @param {{offset: number, mask?: number[]}=} options options
* @param {{ offset: number, mask?: number[] }=} options options
* @returns {boolean} whether the header matches
*/
const check = (header, options) => {
Expand All @@ -174,7 +172,7 @@ function fileTypeFromBuffer(input) {

/**
* @param {string} header header string
* @param {{offset: number, mask?: number[]}=} options options
* @param {{ offset: number, mask?: number[] }=} options options
* @returns {boolean} whether the header matches
*/
const checkString = (header, options) =>
Expand Down Expand Up @@ -492,8 +490,8 @@ function memoize(fn) {

/**
* @typedef {object} MetaData
* @property {Array<Error>} warnings warnings
* @property {Array<Error>} errors errors
* @property {Error[]} warnings warnings
* @property {Error[]} errors errors
*/

class InvalidConfigError extends Error {
Expand Down Expand Up @@ -718,8 +716,8 @@ async function imageminMinify(original, options) {
* @typedef {object} SquooshImage
* @property {(options: Record<string, unknown>) => Promise<void>} preprocess preprocess
* @property {(options: Record<string, unknown>) => Promise<void>} encode encode
* @property {Record<string, {binary: Uint8Array, extension: string}>} encodedWith encoded with
* @property {{ bitmap: {width: number, height: number}} } decoded decoded
* @property {Record<string, { binary: Uint8Array, extension: string }>} encodedWith encoded with
* @property {{ bitmap: { width: number, height: number } }} decoded decoded
*/

/**
Expand Down Expand Up @@ -788,9 +786,8 @@ async function squooshImagePoolTeardown() {
* @returns {Promise<WorkerResult | null>} generated result
*/
async function squooshGenerate(original, options) {
// eslint-disable-next-line jsdoc/no-restricted-syntax
/**
* @typedef {{ [key: string]: any }} SquooshOptions
* @typedef {{ [key: string]: EXPECTED_ANY }} SquooshOptions
*/

const squoosh = require("@squoosh/lib");
Expand Down Expand Up @@ -886,9 +883,8 @@ squooshGenerate.teardown = squooshImagePoolTeardown;
* @returns {Promise<WorkerResult | null>} minified result
*/
async function squooshMinify(original, options) {
// eslint-disable-next-line jsdoc/no-restricted-syntax
/**
* @typedef {{ [key: string]: any }} SquooshOptions
* @typedef {{ [key: string]: EXPECTED_ANY }} SquooshOptions
*/

const squoosh = require("@squoosh/lib");
Expand Down Expand Up @@ -1194,7 +1190,7 @@ function sharpGenerate(original, options) {
/**
* @typedef {object} SharpOptions
* @property {ResizeOptions=} resize resize options
* @property {number | 'auto'=} rotate rotate options
* @property {number | "auto"=} rotate rotate options
* @property {SizeSuffix=} sizeSuffix size suffix
* @property {SharpEncodeOptions=} encodeOptions encode options
*/
Expand Down
2 changes: 2 additions & 0 deletions test/ImageminPlugin.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1150,6 +1150,7 @@ describe("imagemin plugin", () => {
});

it("should throw an error on empty minimizer", async () => {
// eslint-disable-next-line jest/no-unneeded-async-expect-function
await expect(async () => {
await runWebpack({
emitPlugin: true,
Expand All @@ -1161,6 +1162,7 @@ describe("imagemin plugin", () => {
});

it("should throw an error on empty generator", async () => {
// eslint-disable-next-line jest/no-unneeded-async-expect-function
await expect(async () => {
await runWebpack({
emitPlugin: true,
Expand Down
2 changes: 1 addition & 1 deletion test/__snapshots__/ImageminPlugin.test.js.snap
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing

exports[`imagemin plugin should work with mini-css-extract-plugin (svgoMinify): main.css 1`] = `
"a {
Expand Down
2 changes: 1 addition & 1 deletion test/__snapshots__/validate-loader-options.test.js.snap
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing

exports[`validate loader options should throw an error on the "generator" option with "[]" value 1`] = `
"Invalid options object. Image Minimizer Plugin Loader has been initialized using an options object that does not match the API schema.
Expand Down
2 changes: 1 addition & 1 deletion test/__snapshots__/validate-plugin-options.test.js.snap
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing

exports[`validate plugin options should work 1`] = `
"Invalid options object. Image Minimizer Plugin has been initialized using an options object that does not match the API schema.
Expand Down
14 changes: 9 additions & 5 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ declare namespace ImageMinimizerPlugin {
PathData,
ImageminMinifyFunction,
SquooshMinifyFunction,
EXPECTED_ANY,
Rule,
Rules,
FilterFn,
Expand Down Expand Up @@ -99,6 +100,7 @@ type TemplatePath = import("webpack").TemplatePath;
type PathData = import("webpack").PathData;
type ImageminMinifyFunction = typeof imageminMinify;
type SquooshMinifyFunction = typeof squooshMinify;
type EXPECTED_ANY = any;
type Rule = RegExp | string;
type Rules = Rule[] | Rule;
type FilterFn = (source: Buffer, sourcePath: string) => boolean;
Expand All @@ -115,11 +117,11 @@ type WorkerResult = {
/**
* warnings
*/
warnings: Array<Error>;
warnings: Error[];
/**
* errors
*/
errors: Array<Error>;
errors: Error[];
/**
* asset info
*/
Expand Down Expand Up @@ -158,7 +160,7 @@ type Task<T> = {
transformer: Transformer<T> | Transformer<T>[];
};
type CustomOptions = {
[key: string]: any;
[key: string]: EXPECTED_ANY;
};
type InferDefaultType<T> = T extends infer U ? U : CustomOptions;
type BasicTransformerOptions<T> = InferDefaultType<T> | undefined;
Expand Down Expand Up @@ -272,15 +274,17 @@ type PluginOptions<T, G> = {
* allows to set the minimizer
*/
minimizer?:
| (T extends any[]
| (T extends EXPECTED_ANY[]
? { [P in keyof T]: Minimizer<T[P]> }
: Minimizer<T> | Minimizer<T>[])
| undefined;
/**
* allows to set the generator
*/
generator?:
| (G extends any[] ? { [P in keyof G]: Generator<G[P]> } : Generator<G>[])
| (G extends EXPECTED_ANY[]
? { [P in keyof G]: Generator<G[P]> }
: Generator<G>[])
| undefined;
/**
* automatically adding `image-loader`.
Expand Down
Loading
Loading