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
29 changes: 23 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,12 @@ can also be an array. Each element is passed to the minimizer at the same
index in the `minify` array. If a single object is provided instead, it is
reused for every minimizer.

Two keys are filled in before a minimizer sees them, and only when the options
do not already set them: `ecma`, from
[`output.environment`](https://webpack.js.org/configuration/output/#outputenvironment),
and `module`, from the asset's own `javascriptModule` info or its `.mjs` /
`.cjs` extension. Setting either yourself wins, including `module: false`.

> **Note**
>
> `terserOptions` is kept as a deprecated alias of `minimizerOptions` for
Expand Down Expand Up @@ -626,11 +632,16 @@ module.exports = {
import webp from "./image.jpg?as=webp";
```

Under [`cache.type: "filesystem"`](https://webpack.js.org/configuration/cache/#cachetype)
a module is restored from the pack rather than rebuilt across runs. That
restored result is the generator's, so changing `generate` or
`generatorOptions` has to invalidate the pack, and the plugin adds their
identity to
In watch mode the rename is carried on the module rather than reapplied each
build, so a rebuild that does not touch the image keeps pointing at the
generated name without running the generator again. Changing the image does
run it again, since its answer is cached under the bytes.

The same holds across runs under
[`cache.type: "filesystem"`](https://webpack.js.org/configuration/cache/#cachetype),
where a module is restored from the pack rather than rebuilt. That restored
result is the generator's, so changing `generate` or `generatorOptions` has to
invalidate the pack, and the plugin adds their identity to
[`cache.version`](https://webpack.js.org/configuration/cache/#cacheversion) so
it does. This needs the plugin to be in the config — `plugins` or
`optimization.minimizer` — since webpack builds the cache while it applies
Expand All @@ -656,7 +667,13 @@ Default: `{}`

Options for [`generate`](#generate), exactly as
[`minimizerOptions`](#minimizeroptions) is for [`minify`](#minify): one object
for one generator, or an array positionally matching an array of generators.
for one generator, or an array positionally matching an array of generators. A
single object handed an array of generators is reused for every one of them.

`ecma` is filled in from
[`output.environment`](https://webpack.js.org/configuration/output/#outputenvironment)
unless the options set it, the same way it is for
[`minimizerOptions`](#minimizeroptions).

```js
const MinimizerPlugin = require("minimizer-webpack-plugin");
Expand Down
103 changes: 103 additions & 0 deletions test/generate-option.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,109 @@ describe("generate option", () => {
});
});

describe("generatorOptions", () => {
/**
* Records the options it was handed and rewrites nothing, so a test can read
* back what reached it.
* @param {{ [file: string]: string | Buffer }} input input
* @param {undefined} sourceMap source map
* @param {Record<string, EXPECTED_ANY>} generatorOptions the options under test
* @returns {{ code: string | Buffer }} the input, unchanged
*/
function records(input, sourceMap, generatorOptions) {
const [[, code]] = Object.entries(input);

records.seen.push(generatorOptions);

return { code };
}

records.supportsBinary = () => true;
records.supportsWorker = () => false;

beforeEach(() => {
records.seen = [];
});

/**
* @param {object} options plugin options beyond `test` and `generate`
* @param {EXPECTED_ANY} generate the generator, or an array of them
* @returns {Promise<import("webpack").Stats>} the stats of the build
*/
async function build(options, generate) {
const compiler = getCompiler({
entry: path.resolve(__dirname, "./fixtures/images.js"),
module: { rules: IMAGE_RULES },
});

new MinimizerPlugin({ test: /\.jpe?g$/i, generate, ...options }).apply(
compiler,
);

return compile(compiler);
}

it("should hand one object to the generator", async () => {
const stats = await build(
{ generatorOptions: { encodeOptions: { webp: { quality: 90 } } } },
records,
);

if (reportedNoAwait(stats)) {
return;
}

expect(records.seen).toHaveLength(1);
expect(records.seen[0]).toMatchObject({
encodeOptions: { webp: { quality: 90 } },
});
});

it("should default to an empty object", async () => {
const stats = await build({}, records);

if (reportedNoAwait(stats)) {
return;
}

expect(records.seen).toHaveLength(1);
// `module` and `ecma` are overlaid onto a generator's options the same way
// they are onto a minimizer's, so an absent `generatorOptions` is not bare.
expect(Object.keys(records.seen[0]).sort()).toEqual(["ecma", "module"]);
});

it("should match an array of options to an array of generators", async () => {
const stats = await build(
{ generatorOptions: [{ first: true }, { second: true }] },
[records, records],
);

if (reportedNoAwait(stats)) {
return;
}

expect(records.seen).toHaveLength(2);
expect(records.seen[0]).toMatchObject({ first: true });
expect(records.seen[1]).toMatchObject({ second: true });
expect(records.seen[0]).not.toHaveProperty("second");
});

it("should share one object across an array of generators", async () => {
const stats = await build({ generatorOptions: { shared: true } }, [
records,
records,
]);

if (reportedNoAwait(stats)) {
return;
}

expect(records.seen).toHaveLength(2);
expect(records.seen[0]).toMatchObject({ shared: true });
expect(records.seen[1]).toMatchObject({ shared: true });
});
});

describe("sharpGenerate target format", () => {
it("should report when no target format was asked for", async () => {
const result = await MinimizerPlugin.sharpGenerate(
Expand Down
Loading
Loading