@rsbuild/plugin-image-compress 1.0.0 → 1.0.2-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023-present Bytedance, Inc. and its affiliates.
3
+ Copyright (c) 2024 Rspack Contrib
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,19 +1,78 @@
1
- <p align="center">
2
- <a href="https://rsbuild.dev" target="blank"><img src="https://github.com/web-infra-dev/rsbuild/assets/7237365/84abc13e-b620-468f-a90b-dbf28e7e9427" alt="Rsbuild Logo" /></a>
1
+ # @rsbuild/plugin-image-compress
2
+
3
+ An Rsbuild plugin to compress images via [@napi-rs/image](https://www.npmjs.com/package/@napi-rs/image) and [SVGO](https://www.npmjs.com/package/svgo).
4
+
5
+ With the image compression plugin, image assets used in the project can be compressed to reduce the output size without affecting the visual appearance of the image.
6
+
7
+ <p>
8
+ <a href="https://npmjs.com/package/@rsbuild/plugin-image-compress">
9
+ <img src="https://img.shields.io/npm/v/@rsbuild/plugin-image-compress?style=flat-square&colorA=564341&colorB=EDED91" alt="npm version" />
10
+ </a>
11
+ <img src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square&colorA=564341&colorB=EDED91" alt="license" />
3
12
  </p>
4
13
 
5
- # Rsbuild
14
+ ## Usage
15
+
16
+ Install:
17
+
18
+ ```bash
19
+ npm add @rsbuild/plugin-image-compress -D
20
+ ```
21
+
22
+ Add plugin to your `rsbuild.config.ts`:
23
+
24
+ ```ts
25
+ // rsbuild.config.ts
26
+ import { pluginImageCompress } from "@rsbuild/plugin-image-compress";
27
+
28
+ export default {
29
+ plugins: [pluginImageCompress()],
30
+ };
31
+ ```
32
+
33
+ ## Options
34
+
35
+ The plugin accepts an array of compressor configuration options, each of which can be either a string or an object. The string can be the name of a built-in compressor and its default configuration enabled.
36
+ Or use the object format configuration and specify the compressor in the `use` field. The remaining fields of the object will be used as compressor configuration options.
37
+
38
+ By default, the plugin will enable `jpeg`, `png`, `ico` image compressors, which are equivalent to the following two examples:
39
+
40
+ ```js
41
+ pluginImageCompress(["jpeg", "png", "ico"]);
42
+ ```
43
+
44
+ ```js
45
+ pluginImageCompress([{ use: "jpeg" }, { use: "png" }, { use: "ico" }]);
46
+ ```
47
+
48
+ The default configuration can be overridden by specifying a configuration option.
49
+ For example, to allow the jpeg compressor to recognize new extension name and to set the quality of the png compressor.
50
+
51
+ ```js
52
+ pluginImageCompress([
53
+ { use: "jpeg", test: /\.(?:jpg|jpeg|jpe)$/ },
54
+ { use: "png", minQuality: 50 },
55
+ "ico",
56
+ ]);
57
+ ```
58
+
59
+ The default `png` compressor is lossy.
60
+ If you want to replace it with a lossless compressor, you can use the following configuration.
6
61
 
7
- Unleash the power of Rspack with the out-of-the-box build tool.
62
+ ```js
63
+ pluginImageCompress(["jpeg", "pngLossless", "ico"]);
64
+ ```
8
65
 
9
- ## Documentation
66
+ The list of configuration options will eventually be converted to the corresponding bundler loader configuration, so compressors follow the same bottom-to-top matching rule.
10
67
 
11
- https://rsbuild.dev/
68
+ For example, the `png` compressor will take precedence over the `pngLossless` compressor for the following configuration:
12
69
 
13
- ## Contributing
70
+ ```js
71
+ pluginImageCompress(["jpeg", "pngLossless", "ico", "png"]);
72
+ ```
14
73
 
15
- Please read the [Contributing Guide](https://github.com/web-infra-dev/rsbuild/blob/main/CONTRIBUTING.md).
74
+ For more information on compressors, please visit [@napi-rs/image](https://image.napi.rs/docs).
16
75
 
17
76
  ## License
18
77
 
19
- Rsbuild is [MIT licensed](https://github.com/web-infra-dev/rsbuild/blob/main/LICENSE).
78
+ [MIT](./LICENSE).
@@ -1,37 +1,60 @@
1
- // ../../node_modules/.pnpm/@modern-js+module-tools@2.40.0_typescript@5.3.2/node_modules/@modern-js/module-tools/shims/esm.js
2
- import { fileURLToPath } from "url";
3
- import path from "path";
4
-
5
- // ../../scripts/require_shims.js
6
- import { createRequire } from "module";
7
- global.require = createRequire(import.meta.url);
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
8
29
 
9
30
  // src/index.ts
10
- import assert2 from "assert";
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ DEFAULT_OPTIONS: () => DEFAULT_OPTIONS,
34
+ PLUGIN_IMAGE_COMPRESS_NAME: () => PLUGIN_IMAGE_COMPRESS_NAME,
35
+ pluginImageCompress: () => pluginImageCompress
36
+ });
37
+ module.exports = __toCommonJS(src_exports);
38
+ var import_node_assert2 = __toESM(require("assert"), 1);
11
39
 
12
40
  // src/minimizer.ts
13
- import { Buffer as Buffer2 } from "buffer";
41
+ var import_node_buffer2 = require("buffer");
14
42
 
15
43
  // src/shared/codecs.ts
16
- import { Buffer } from "buffer";
17
- import {
18
- compressJpeg,
19
- losslessCompressPng,
20
- pngQuantize,
21
- Transformer
22
- } from "@napi-rs/image";
23
- import svgo from "svgo";
44
+ var import_node_buffer = require("buffer");
45
+ var import_image = require("@napi-rs/image");
46
+ var import_svgo = __toESM(require("svgo"), 1);
24
47
  var jpegCodec = {
25
48
  handler(buf, options) {
26
- return compressJpeg(buf, options);
49
+ return (0, import_image.compressJpeg)(buf, options);
27
50
  },
28
51
  defaultOptions: {
29
- test: /\.(jpg|jpeg)$/
52
+ test: /\.(?:jpg|jpeg)$/
30
53
  }
31
54
  };
32
55
  var pngCodec = {
33
56
  handler(buf, options) {
34
- return pngQuantize(buf, options);
57
+ return (0, import_image.pngQuantize)(buf, options);
35
58
  },
36
59
  defaultOptions: {
37
60
  test: /\.png$/
@@ -39,7 +62,7 @@ var pngCodec = {
39
62
  };
40
63
  var pngLosslessCodec = {
41
64
  handler(buf, options) {
42
- return losslessCompressPng(buf, options);
65
+ return (0, import_image.losslessCompressPng)(buf, options);
43
66
  },
44
67
  defaultOptions: {
45
68
  test: /\.png$/
@@ -47,16 +70,16 @@ var pngLosslessCodec = {
47
70
  };
48
71
  var icoCodec = {
49
72
  handler(buf) {
50
- return new Transformer(buf).ico();
73
+ return new import_image.Transformer(buf).ico();
51
74
  },
52
75
  defaultOptions: {
53
- test: /\.(ico|icon)$/
76
+ test: /\.(?:ico|icon)$/
54
77
  }
55
78
  };
56
79
  var svgCodec = {
57
80
  async handler(buf, options) {
58
- const result = svgo.optimize(buf.toString(), options);
59
- return Buffer.from(result.data);
81
+ const result = import_svgo.default.optimize(buf.toString(), options);
82
+ return import_node_buffer.Buffer.from(result.data);
60
83
  },
61
84
  defaultOptions: {
62
85
  test: /\.svg$/
@@ -72,22 +95,24 @@ var codecs = {
72
95
  var codecs_default = codecs;
73
96
 
74
97
  // src/minimizer.ts
75
- var MODERN_JS_IMAGE_MINIMIZER_PLUGIN_NAME = "@rsbuild/plugin-image-compress/minimizer";
76
- var ModernJsImageMinimizerPlugin = class {
98
+ var IMAGE_MINIMIZER_PLUGIN_NAME = "@rsbuild/plugin-image-compress/minimizer";
99
+ var ImageMinimizerPlugin = class {
77
100
  constructor(options) {
78
- this.name = MODERN_JS_IMAGE_MINIMIZER_PLUGIN_NAME;
101
+ this.name = IMAGE_MINIMIZER_PLUGIN_NAME;
79
102
  this.options = options;
80
103
  }
81
104
  async optimize(compiler, compilation, assets) {
82
- const cache = compilation.getCache(MODERN_JS_IMAGE_MINIMIZER_PLUGIN_NAME);
105
+ const cache = compilation.getCache(IMAGE_MINIMIZER_PLUGIN_NAME);
83
106
  const { RawSource } = compiler.webpack.sources;
84
107
  const { matchObject } = compiler.webpack.ModuleFilenameHelpers;
85
108
  const buildError = (error, file, context) => {
86
109
  const cause = error instanceof Error ? error : new Error();
87
- const message = file && context ? `"${file}" in "${context}" from Modern.js Image Minimizer:
110
+ const message = file && context ? `"${file}" in "${context}" from Image Minimizer:
88
111
  ${cause.message}` : cause.message;
89
112
  const ret = new compiler.webpack.WebpackError(message);
90
- error instanceof Error && (ret.error = error);
113
+ if (error instanceof Error) {
114
+ ret.error = error;
115
+ }
91
116
  return ret;
92
117
  };
93
118
  const codec = codecs_default[this.options.use];
@@ -99,17 +124,22 @@ ${cause.message}` : cause.message;
99
124
  const opts = { ...codec.defaultOptions, ...this.options };
100
125
  const handleAsset = async (name) => {
101
126
  const info = compilation.getAsset(name)?.info;
102
- if (info?.minimized || !matchObject(opts, name)) {
127
+ const fileName = name.split("?")[0];
128
+ if (info?.minimized || !matchObject(opts, fileName)) {
129
+ return;
130
+ }
131
+ const asset = compilation.getAsset(name);
132
+ if (!asset) {
103
133
  return;
104
134
  }
105
- const { source: inputSource } = compilation.getAsset(name);
135
+ const { source: inputSource } = asset;
106
136
  const eTag = cache.getLazyHashedEtag(inputSource);
107
137
  const cacheItem = cache.getItemCache(name, eTag);
108
138
  let result = await cacheItem.getPromise();
109
139
  try {
110
140
  if (!result) {
111
141
  const input = inputSource.source();
112
- const buf = await codec.handler(Buffer2.from(input), opts);
142
+ const buf = await codec.handler(import_node_buffer2.Buffer.from(input), opts);
113
143
  result = { source: new RawSource(buf) };
114
144
  await cacheItem.storePromise(result);
115
145
  }
@@ -127,6 +157,7 @@ ${cause.message}` : cause.message;
127
157
  {
128
158
  name: this.name,
129
159
  stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE,
160
+ // @ts-expect-error unsupported by Rspack
130
161
  additionalAssets: true
131
162
  },
132
163
  (assets) => this.optimize(compiler, compilation, assets)
@@ -143,12 +174,12 @@ ${cause.message}` : cause.message;
143
174
  };
144
175
 
145
176
  // src/shared/utils.ts
146
- import assert from "assert";
177
+ var import_node_assert = __toESM(require("assert"), 1);
147
178
  var withDefaultOptions = (opt) => {
148
179
  const options = typeof opt === "string" ? { use: opt } : opt;
149
180
  const { defaultOptions } = codecs_default[options.use];
150
181
  const ret = { ...defaultOptions, ...options };
151
- assert("test" in ret);
182
+ (0, import_node_assert.default)("test" in ret);
152
183
  return ret;
153
184
  };
154
185
 
@@ -161,7 +192,7 @@ var castOptions = (args) => {
161
192
  }
162
193
  const ret = [];
163
194
  for (const arg of args) {
164
- assert2(!Array.isArray(arg));
195
+ (0, import_node_assert2.default)(!Array.isArray(arg));
165
196
  ret.push(arg);
166
197
  }
167
198
  return ret;
@@ -171,22 +202,25 @@ var normalizeOptions = (options) => {
171
202
  const normalized = opts.map((opt) => withDefaultOptions(opt));
172
203
  return normalized;
173
204
  };
205
+ var PLUGIN_IMAGE_COMPRESS_NAME = "rsbuild:image-compress";
174
206
  var pluginImageCompress = (...args) => ({
175
- name: "rsbuild:image-compress",
207
+ name: PLUGIN_IMAGE_COMPRESS_NAME,
176
208
  setup(api) {
177
209
  const opts = normalizeOptions(castOptions(args));
178
- api.modifyBundlerChain((chain, { env }) => {
179
- if (env !== "production") {
210
+ api.modifyBundlerChain((chain, { isDev }) => {
211
+ if (isDev) {
180
212
  return;
181
213
  }
182
214
  chain.optimization.minimize(true);
183
215
  for (const opt of opts) {
184
- chain.optimization.minimizer(`image-compress-${opt.use}`).use(ModernJsImageMinimizerPlugin, [opt]);
216
+ chain.optimization.minimizer(`image-compress-${opt.use}`).use(ImageMinimizerPlugin, [opt]);
185
217
  }
186
218
  });
187
219
  }
188
220
  });
189
- export {
221
+ // Annotate the CommonJS export names for ESM import in node:
222
+ 0 && (module.exports = {
190
223
  DEFAULT_OPTIONS,
224
+ PLUGIN_IMAGE_COMPRESS_NAME,
191
225
  pluginImageCompress
192
- };
226
+ });
@@ -0,0 +1,38 @@
1
+ import { RsbuildPlugin } from '@rsbuild/core';
2
+ import { JpegCompressOptions, PngQuantOptions, PNGLosslessOptions } from '@napi-rs/image';
3
+ import { Config } from 'svgo';
4
+
5
+ type OneOrMany<T> = T | T[];
6
+ interface CodecBaseOptions {
7
+ jpeg: JpegCompressOptions;
8
+ png: PngQuantOptions;
9
+ pngLossless: PNGLosslessOptions;
10
+ ico: Record<string, unknown>;
11
+ svg: Config;
12
+ }
13
+ interface BaseCompressOptions<T extends Codecs> {
14
+ use: T;
15
+ test?: OneOrMany<RegExp>;
16
+ include?: OneOrMany<RegExp>;
17
+ exclude?: OneOrMany<RegExp>;
18
+ }
19
+ type FinalOptionCollection = {
20
+ [K in Codecs]: BaseCompressOptions<K> & CodecBaseOptions[K];
21
+ };
22
+ type Codecs = keyof CodecBaseOptions;
23
+ type OptionCollection = {
24
+ [K in Codecs]: K | FinalOptionCollection[K];
25
+ };
26
+ type Options = OptionCollection[Codecs];
27
+
28
+ type PluginImageCompressOptions = Options[];
29
+ declare const DEFAULT_OPTIONS: Codecs[];
30
+ interface IPluginImageCompress {
31
+ (...options: Options[]): RsbuildPlugin;
32
+ (options: Options[]): RsbuildPlugin;
33
+ }
34
+ declare const PLUGIN_IMAGE_COMPRESS_NAME = "rsbuild:image-compress";
35
+ /** Options enable by default: {@link DEFAULT_OPTIONS} */
36
+ declare const pluginImageCompress: IPluginImageCompress;
37
+
38
+ export { DEFAULT_OPTIONS, type IPluginImageCompress, PLUGIN_IMAGE_COMPRESS_NAME, type PluginImageCompressOptions, pluginImageCompress };
package/dist/index.d.ts CHANGED
@@ -1,20 +1,20 @@
1
- import { RsbuildPlugin } from '@rsbuild/webpack';
1
+ import { RsbuildPlugin } from '@rsbuild/core';
2
2
  import { JpegCompressOptions, PngQuantOptions, PNGLosslessOptions } from '@napi-rs/image';
3
3
  import { Config } from 'svgo';
4
4
 
5
- type ArrayOrNot<T> = T | T[];
5
+ type OneOrMany<T> = T | T[];
6
6
  interface CodecBaseOptions {
7
7
  jpeg: JpegCompressOptions;
8
8
  png: PngQuantOptions;
9
9
  pngLossless: PNGLosslessOptions;
10
- ico: {};
10
+ ico: Record<string, unknown>;
11
11
  svg: Config;
12
12
  }
13
13
  interface BaseCompressOptions<T extends Codecs> {
14
14
  use: T;
15
- test?: ArrayOrNot<RegExp>;
16
- include?: ArrayOrNot<RegExp>;
17
- exclude?: ArrayOrNot<RegExp>;
15
+ test?: OneOrMany<RegExp>;
16
+ include?: OneOrMany<RegExp>;
17
+ exclude?: OneOrMany<RegExp>;
18
18
  }
19
19
  type FinalOptionCollection = {
20
20
  [K in Codecs]: BaseCompressOptions<K> & CodecBaseOptions[K];
@@ -31,7 +31,8 @@ interface IPluginImageCompress {
31
31
  (...options: Options[]): RsbuildPlugin;
32
32
  (options: Options[]): RsbuildPlugin;
33
33
  }
34
+ declare const PLUGIN_IMAGE_COMPRESS_NAME = "rsbuild:image-compress";
34
35
  /** Options enable by default: {@link DEFAULT_OPTIONS} */
35
36
  declare const pluginImageCompress: IPluginImageCompress;
36
37
 
37
- export { DEFAULT_OPTIONS, IPluginImageCompress, PluginImageCompressOptions, pluginImageCompress };
38
+ export { DEFAULT_OPTIONS, type IPluginImageCompress, PLUGIN_IMAGE_COMPRESS_NAME, type PluginImageCompressOptions, pluginImageCompress };
package/dist/index.js CHANGED
@@ -1,59 +1,29 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
19
- };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
-
30
1
  // src/index.ts
31
- var src_exports = {};
32
- __export(src_exports, {
33
- DEFAULT_OPTIONS: () => DEFAULT_OPTIONS,
34
- pluginImageCompress: () => pluginImageCompress
35
- });
36
- module.exports = __toCommonJS(src_exports);
37
- var import_assert2 = __toESM(require("assert"));
2
+ import assert2 from "node:assert";
38
3
 
39
4
  // src/minimizer.ts
40
- var import_buffer2 = require("buffer");
5
+ import { Buffer as Buffer2 } from "node:buffer";
41
6
 
42
7
  // src/shared/codecs.ts
43
- var import_buffer = require("buffer");
44
- var import_image = require("@napi-rs/image");
45
- var import_svgo = __toESM(require("svgo"));
8
+ import { Buffer } from "node:buffer";
9
+ import {
10
+ Transformer,
11
+ compressJpeg,
12
+ losslessCompressPng,
13
+ pngQuantize
14
+ } from "@napi-rs/image";
15
+ import svgo from "svgo";
46
16
  var jpegCodec = {
47
17
  handler(buf, options) {
48
- return (0, import_image.compressJpeg)(buf, options);
18
+ return compressJpeg(buf, options);
49
19
  },
50
20
  defaultOptions: {
51
- test: /\.(jpg|jpeg)$/
21
+ test: /\.(?:jpg|jpeg)$/
52
22
  }
53
23
  };
54
24
  var pngCodec = {
55
25
  handler(buf, options) {
56
- return (0, import_image.pngQuantize)(buf, options);
26
+ return pngQuantize(buf, options);
57
27
  },
58
28
  defaultOptions: {
59
29
  test: /\.png$/
@@ -61,7 +31,7 @@ var pngCodec = {
61
31
  };
62
32
  var pngLosslessCodec = {
63
33
  handler(buf, options) {
64
- return (0, import_image.losslessCompressPng)(buf, options);
34
+ return losslessCompressPng(buf, options);
65
35
  },
66
36
  defaultOptions: {
67
37
  test: /\.png$/
@@ -69,16 +39,16 @@ var pngLosslessCodec = {
69
39
  };
70
40
  var icoCodec = {
71
41
  handler(buf) {
72
- return new import_image.Transformer(buf).ico();
42
+ return new Transformer(buf).ico();
73
43
  },
74
44
  defaultOptions: {
75
- test: /\.(ico|icon)$/
45
+ test: /\.(?:ico|icon)$/
76
46
  }
77
47
  };
78
48
  var svgCodec = {
79
49
  async handler(buf, options) {
80
- const result = import_svgo.default.optimize(buf.toString(), options);
81
- return import_buffer.Buffer.from(result.data);
50
+ const result = svgo.optimize(buf.toString(), options);
51
+ return Buffer.from(result.data);
82
52
  },
83
53
  defaultOptions: {
84
54
  test: /\.svg$/
@@ -94,22 +64,24 @@ var codecs = {
94
64
  var codecs_default = codecs;
95
65
 
96
66
  // src/minimizer.ts
97
- var MODERN_JS_IMAGE_MINIMIZER_PLUGIN_NAME = "@rsbuild/plugin-image-compress/minimizer";
98
- var ModernJsImageMinimizerPlugin = class {
67
+ var IMAGE_MINIMIZER_PLUGIN_NAME = "@rsbuild/plugin-image-compress/minimizer";
68
+ var ImageMinimizerPlugin = class {
99
69
  constructor(options) {
100
- this.name = MODERN_JS_IMAGE_MINIMIZER_PLUGIN_NAME;
70
+ this.name = IMAGE_MINIMIZER_PLUGIN_NAME;
101
71
  this.options = options;
102
72
  }
103
73
  async optimize(compiler, compilation, assets) {
104
- const cache = compilation.getCache(MODERN_JS_IMAGE_MINIMIZER_PLUGIN_NAME);
74
+ const cache = compilation.getCache(IMAGE_MINIMIZER_PLUGIN_NAME);
105
75
  const { RawSource } = compiler.webpack.sources;
106
76
  const { matchObject } = compiler.webpack.ModuleFilenameHelpers;
107
77
  const buildError = (error, file, context) => {
108
78
  const cause = error instanceof Error ? error : new Error();
109
- const message = file && context ? `"${file}" in "${context}" from Modern.js Image Minimizer:
79
+ const message = file && context ? `"${file}" in "${context}" from Image Minimizer:
110
80
  ${cause.message}` : cause.message;
111
81
  const ret = new compiler.webpack.WebpackError(message);
112
- error instanceof Error && (ret.error = error);
82
+ if (error instanceof Error) {
83
+ ret.error = error;
84
+ }
113
85
  return ret;
114
86
  };
115
87
  const codec = codecs_default[this.options.use];
@@ -120,19 +92,23 @@ ${cause.message}` : cause.message;
120
92
  }
121
93
  const opts = { ...codec.defaultOptions, ...this.options };
122
94
  const handleAsset = async (name) => {
123
- var _a;
124
- const info = (_a = compilation.getAsset(name)) == null ? void 0 : _a.info;
125
- if ((info == null ? void 0 : info.minimized) || !matchObject(opts, name)) {
95
+ const info = compilation.getAsset(name)?.info;
96
+ const fileName = name.split("?")[0];
97
+ if (info?.minimized || !matchObject(opts, fileName)) {
98
+ return;
99
+ }
100
+ const asset = compilation.getAsset(name);
101
+ if (!asset) {
126
102
  return;
127
103
  }
128
- const { source: inputSource } = compilation.getAsset(name);
104
+ const { source: inputSource } = asset;
129
105
  const eTag = cache.getLazyHashedEtag(inputSource);
130
106
  const cacheItem = cache.getItemCache(name, eTag);
131
107
  let result = await cacheItem.getPromise();
132
108
  try {
133
109
  if (!result) {
134
110
  const input = inputSource.source();
135
- const buf = await codec.handler(import_buffer2.Buffer.from(input), opts);
111
+ const buf = await codec.handler(Buffer2.from(input), opts);
136
112
  result = { source: new RawSource(buf) };
137
113
  await cacheItem.storePromise(result);
138
114
  }
@@ -150,6 +126,7 @@ ${cause.message}` : cause.message;
150
126
  {
151
127
  name: this.name,
152
128
  stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE,
129
+ // @ts-expect-error unsupported by Rspack
153
130
  additionalAssets: true
154
131
  },
155
132
  (assets) => this.optimize(compiler, compilation, assets)
@@ -166,12 +143,12 @@ ${cause.message}` : cause.message;
166
143
  };
167
144
 
168
145
  // src/shared/utils.ts
169
- var import_assert = __toESM(require("assert"));
146
+ import assert from "node:assert";
170
147
  var withDefaultOptions = (opt) => {
171
148
  const options = typeof opt === "string" ? { use: opt } : opt;
172
149
  const { defaultOptions } = codecs_default[options.use];
173
150
  const ret = { ...defaultOptions, ...options };
174
- (0, import_assert.default)("test" in ret);
151
+ assert("test" in ret);
175
152
  return ret;
176
153
  };
177
154
 
@@ -184,7 +161,7 @@ var castOptions = (args) => {
184
161
  }
185
162
  const ret = [];
186
163
  for (const arg of args) {
187
- (0, import_assert2.default)(!Array.isArray(arg));
164
+ assert2(!Array.isArray(arg));
188
165
  ret.push(arg);
189
166
  }
190
167
  return ret;
@@ -194,23 +171,24 @@ var normalizeOptions = (options) => {
194
171
  const normalized = opts.map((opt) => withDefaultOptions(opt));
195
172
  return normalized;
196
173
  };
174
+ var PLUGIN_IMAGE_COMPRESS_NAME = "rsbuild:image-compress";
197
175
  var pluginImageCompress = (...args) => ({
198
- name: "rsbuild:image-compress",
176
+ name: PLUGIN_IMAGE_COMPRESS_NAME,
199
177
  setup(api) {
200
178
  const opts = normalizeOptions(castOptions(args));
201
- api.modifyBundlerChain((chain, { env }) => {
202
- if (env !== "production") {
179
+ api.modifyBundlerChain((chain, { isDev }) => {
180
+ if (isDev) {
203
181
  return;
204
182
  }
205
183
  chain.optimization.minimize(true);
206
184
  for (const opt of opts) {
207
- chain.optimization.minimizer(`image-compress-${opt.use}`).use(ModernJsImageMinimizerPlugin, [opt]);
185
+ chain.optimization.minimizer(`image-compress-${opt.use}`).use(ImageMinimizerPlugin, [opt]);
208
186
  }
209
187
  });
210
188
  }
211
189
  });
212
- // Annotate the CommonJS export names for ESM import in node:
213
- 0 && (module.exports = {
190
+ export {
214
191
  DEFAULT_OPTIONS,
192
+ PLUGIN_IMAGE_COMPRESS_NAME,
215
193
  pluginImageCompress
216
- });
194
+ };
package/package.json CHANGED
@@ -1,46 +1,62 @@
1
1
  {
2
2
  "name": "@rsbuild/plugin-image-compress",
3
- "version": "1.0.0",
4
- "description": "Image compress plugin for Rsbuild",
5
- "homepage": "https://rsbuild.dev",
6
- "repository": {
7
- "type": "git",
8
- "url": "https://github.com/web-infra-dev/rsbuild",
9
- "directory": "packages/plugin-image-compress"
10
- },
3
+ "version": "1.0.2-beta.0",
4
+ "repository": "https://github.com/rspack-contrib/rsbuild-plugin-image-compress",
11
5
  "license": "MIT",
6
+ "type": "module",
12
7
  "exports": {
13
8
  ".": {
14
9
  "types": "./dist/index.d.ts",
15
- "import": "./dist/index.mjs",
16
- "default": "./dist/index.js"
10
+ "import": "./dist/index.js",
11
+ "require": "./dist/index.cjs"
17
12
  }
18
13
  },
19
14
  "main": "./dist/index.js",
15
+ "module": "./dist/index.mjs",
20
16
  "types": "./dist/index.d.ts",
21
17
  "files": [
22
18
  "dist"
23
19
  ],
20
+ "simple-git-hooks": {
21
+ "pre-commit": "npx nano-staged"
22
+ },
23
+ "nano-staged": {
24
+ "*.{js,jsx,ts,tsx,mjs,cjs}": [
25
+ "biome check --write --no-errors-on-unmatched"
26
+ ]
27
+ },
24
28
  "dependencies": {
25
- "@napi-rs/image": "^1.7.0",
26
- "svgo": "^3.0.2",
27
- "@rsbuild/shared": "1.0.0"
29
+ "@napi-rs/image": "^1.9.2",
30
+ "svgo": "^3.3.2"
28
31
  },
29
32
  "devDependencies": {
30
- "@types/node": "^16",
31
- "typescript": "^5.3.0",
32
- "webpack": "^5.89.0",
33
- "@rsbuild/core": "1.0.0",
34
- "@rsbuild/test-helper": "1.0.0",
35
- "@rsbuild/webpack": "1.0.0"
33
+ "@biomejs/biome": "^1.8.3",
34
+ "@playwright/test": "^1.44.1",
35
+ "@rsbuild/core": "1.0.0-alpha.9",
36
+ "@types/node": "^20.14.1",
37
+ "nano-staged": "^0.8.0",
38
+ "playwright": "^1.44.1",
39
+ "simple-git-hooks": "^2.11.1",
40
+ "tsup": "^8.0.2",
41
+ "typescript": "^5.5.2"
42
+ },
43
+ "peerDependencies": {
44
+ "@rsbuild/core": "1.x || 1.0.0-0"
45
+ },
46
+ "peerDependenciesMeta": {
47
+ "@rsbuild/core": {
48
+ "optional": true
49
+ }
36
50
  },
37
51
  "publishConfig": {
38
52
  "access": "public",
39
- "provenance": true,
40
53
  "registry": "https://registry.npmjs.org/"
41
54
  },
42
55
  "scripts": {
43
- "build": "modern build",
44
- "dev": "modern build --watch"
56
+ "build": "tsup",
57
+ "dev": "tsup --watch",
58
+ "lint": "biome check .",
59
+ "lint:write": "biome check . --write",
60
+ "test": "playwright test"
45
61
  }
46
62
  }