Skip to content

Commit 29ac915

Browse files
feat: added ability to minimizer JSON using jsonMinify (#657)
1 parent e505dee commit 29ac915

File tree

11 files changed

+182
-1
lines changed

11 files changed

+182
-1
lines changed

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,31 @@ module.exports = {
815815
};
816816
```
817817

818+
### JSON
819+
820+
Uses `JSON.stringify()` to minify your JSON files during the build process.
821+
822+
**webpack.config.js**
823+
824+
```js
825+
module.exports = {
826+
optimization: {
827+
minimize: true,
828+
minimizer: [
829+
// Keeps original terser plugin to minify JS files
830+
"...",
831+
// Will minify JSON files (they can come from copy-webpack-plugin or when you are using asset modules)
832+
new TerserPlugin({
833+
test: /\.json$/,
834+
minify: TerserPlugin.jsonMinify,
835+
// We are supporting `space` and `replacer` options, you can set them below
836+
terserOptions: {},
837+
}),
838+
],
839+
},
840+
};
841+
```
842+
818843
### Custom Minify Function
819844

820845
Override the default minify function - use `uglify-js` for minification.

src/index.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const { minify } = require("./minify");
77
const schema = require("./options.json");
88
const {
99
esbuildMinify,
10+
jsonMinify,
1011
memoize,
1112
swcMinify,
1213
terserMinify,
@@ -117,6 +118,7 @@ const {
117118
* @typedef {object} MinimizeFunctionHelpers
118119
* @property {() => string | undefined=} getMinimizerVersion function that returns version of minimizer
119120
* @property {() => boolean | undefined=} supportsWorkerThreads true when minimizer support worker threads, otherwise false
121+
* @property {() => boolean | undefined=} supportsWorker true when minimizer support worker, otherwise false
120122
*/
121123

122124
/**
@@ -178,6 +180,7 @@ class TerserPlugin {
178180
baseDataPath: "options",
179181
});
180182

183+
// TODO handle json and etc in the next major release
181184
// TODO make `minimizer` option instead `minify` and `terserOptions` in the next major release, also rename `terserMinify` to `terserMinimize`
182185
const {
183186
minify = /** @type {MinimizerImplementation<T>} */ (terserMinify),
@@ -407,7 +410,15 @@ class TerserPlugin {
407410
/** @type {undefined | number} */
408411
let numberOfWorkers;
409412

410-
if (optimizeOptions.availableNumberOfCores > 0) {
413+
const needCreateWorker =
414+
optimizeOptions.availableNumberOfCores > 0 &&
415+
(typeof this.options.minimizer.implementation.supportsWorker ===
416+
"undefined" ||
417+
(typeof this.options.minimizer.implementation.supportsWorker ===
418+
"function" &&
419+
this.options.minimizer.implementation.supportsWorker()));
420+
421+
if (needCreateWorker) {
411422
// Do not create unnecessary workers when the number of files is less than the available cores, it saves memory
412423
numberOfWorkers = Math.min(
413424
numberOfAssets,
@@ -908,5 +919,6 @@ TerserPlugin.terserMinify = terserMinify;
908919
TerserPlugin.uglifyJsMinify = uglifyJsMinify;
909920
TerserPlugin.swcMinify = swcMinify;
910921
TerserPlugin.esbuildMinify = esbuildMinify;
922+
TerserPlugin.jsonMinify = jsonMinify;
911923

912924
module.exports = TerserPlugin;

src/utils.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,32 @@ esbuildMinify.getMinimizerVersion = () => {
775775
*/
776776
esbuildMinify.supportsWorkerThreads = () => false;
777777

778+
/* istanbul ignore next */
779+
/**
780+
* @param {Input} input input
781+
* @param {RawSourceMap=} sourceMap source map
782+
* @param {CustomOptions=} minimizerOptions options
783+
* @returns {Promise<MinimizedResult>} minimized result
784+
*/
785+
async function jsonMinify(input, sourceMap, minimizerOptions) {
786+
const options =
787+
/** @type {{ replacer?: Parameters<typeof JSON.stringify>[1], space?: Parameters<typeof JSON.stringify>[2] }} */
788+
(minimizerOptions);
789+
790+
const [[, code]] = Object.entries(input);
791+
const result = JSON.stringify(
792+
JSON.parse(code),
793+
options.replacer,
794+
options.space,
795+
);
796+
797+
return { code: result };
798+
}
799+
800+
jsonMinify.getMinimizerVersion = () => "1.0.0";
801+
jsonMinify.supportsWorker = () => false;
802+
jsonMinify.supportsWorkerThreads = () => false;
803+
778804
/**
779805
* @template T
780806
* @typedef {() => T} FunctionReturning
@@ -806,6 +832,7 @@ function memoize(fn) {
806832

807833
module.exports = {
808834
esbuildMinify,
835+
jsonMinify,
809836
memoize,
810837
swcMinify,
811838
terserMinify,

test/__snapshots__/minify-option.test.js.snap

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,35 @@ exports[`minify option should work using when the \`minify\` option is \`esbuild
148148
149149
exports[`minify option should work using when the \`minify\` option is \`esbuildMinify\`: warnings 1`] = `Array []`;
150150
151+
exports[`minify option should work using when the \`minify\` option is \`jsonMinify\` and allows to set \`JSON.stringify\` options: assets 1`] = `
152+
Object {
153+
"71be2c5d019f14d29bdd.json": "{
154+
\\"foo\\": \\"bar\\",
155+
\\"bar\\": [
156+
\\"baz\\"
157+
]
158+
}",
159+
"main.js": "(()=>{var t={437(t,r,e){\\"use strict\\";t.exports=e.p+\\"71be2c5d019f14d29bdd.json\\"}},r={};function e(o){var n=r[o];if(void 0!==n)return n.exports;var c=r[o]={exports:{}};return t[o](c,c.exports,e),c.exports}e.m=t,e.g=function(){if(\\"object\\"==typeof globalThis)return globalThis;try{return this||new Function(\\"return this\\")()}catch(t){if(\\"object\\"==typeof window)return window}}(),e.o=(t,r)=>Object.prototype.hasOwnProperty.call(t,r),(()=>{var t;e.g.importScripts&&(t=e.g.location+\\"\\");var r=e.g.document;if(!t&&r&&(r.currentScript&&\\"SCRIPT\\"===r.currentScript.tagName.toUpperCase()&&(t=r.currentScript.src),!t)){var o=r.getElementsByTagName(\\"script\\");if(o.length)for(var n=o.length-1;n>-1&&(!t||!/^http(s?):/.test(t));)t=o[n--].src}if(!t)throw new Error(\\"Automatic publicPath is not supported in this browser\\");t=t.replace(/^blob:/,\\"\\").replace(/#.*$/,\\"\\").replace(/\\\\?.*$/,\\"\\").replace(/\\\\/[^\\\\/]+$/,\\"/\\"),e.p=t})(),e.b=\\"undefined\\"!=typeof document&&document.baseURI||self.location.href,console.log(new URL(e(437),e.b))})();",
160+
}
161+
`;
162+
163+
exports[`minify option should work using when the \`minify\` option is \`jsonMinify\` and allows to set \`JSON.stringify\` options: errors 1`] = `Array []`;
164+
165+
exports[`minify option should work using when the \`minify\` option is \`jsonMinify\` and allows to set \`JSON.stringify\` options: warnings 1`] = `Array []`;
166+
167+
exports[`minify option should work using when the \`minify\` option is \`jsonMinify\` and output errors: warnings 1`] = `Array []`;
168+
169+
exports[`minify option should work using when the \`minify\` option is \`jsonMinify\`: assets 1`] = `
170+
Object {
171+
"60491a50c1e5dd7216b6.json": "{\\"foo\\":\\"bar\\",\\"bar\\":[\\"baz\\"]}",
172+
"main.js": "(()=>{var t={437(t,r,e){\\"use strict\\";t.exports=e.p+\\"60491a50c1e5dd7216b6.json\\"}},r={};function e(o){var n=r[o];if(void 0!==n)return n.exports;var c=r[o]={exports:{}};return t[o](c,c.exports,e),c.exports}e.m=t,e.g=function(){if(\\"object\\"==typeof globalThis)return globalThis;try{return this||new Function(\\"return this\\")()}catch(t){if(\\"object\\"==typeof window)return window}}(),e.o=(t,r)=>Object.prototype.hasOwnProperty.call(t,r),(()=>{var t;e.g.importScripts&&(t=e.g.location+\\"\\");var r=e.g.document;if(!t&&r&&(r.currentScript&&\\"SCRIPT\\"===r.currentScript.tagName.toUpperCase()&&(t=r.currentScript.src),!t)){var o=r.getElementsByTagName(\\"script\\");if(o.length)for(var n=o.length-1;n>-1&&(!t||!/^http(s?):/.test(t));)t=o[n--].src}if(!t)throw new Error(\\"Automatic publicPath is not supported in this browser\\");t=t.replace(/^blob:/,\\"\\").replace(/#.*$/,\\"\\").replace(/\\\\?.*$/,\\"\\").replace(/\\\\/[^\\\\/]+$/,\\"/\\"),e.p=t})(),e.b=\\"undefined\\"!=typeof document&&document.baseURI||self.location.href,console.log(new URL(e(437),e.b))})();",
173+
}
174+
`;
175+
176+
exports[`minify option should work using when the \`minify\` option is \`jsonMinify\`: errors 1`] = `Array []`;
177+
178+
exports[`minify option should work using when the \`minify\` option is \`jsonMinify\`: warnings 1`] = `Array []`;
179+
151180
exports[`minify option should work using when the \`minify\` option is \`swcMinify\` and ECMA modules output: assets 1`] = `
152181
Object {
153182
"main.js": "var e={};e.d=(o,t)=>{for(var r in t)e.o(t,r)&&!e.o(o,r)&&Object.defineProperty(o,r,{enumerable:!0,get:t[r]})},e.o=(e,o)=>Object.prototype.hasOwnProperty.call(e,o);var o={};function t(){console.log(11)}e.d(o,{A:()=>r}),t();let r=t,a=o.A;export{a as default};",

test/fixtures/file-error.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"foo":
3+
}

test/fixtures/file.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"foo":
3+
4+
"bar",
5+
6+
"bar": [ "baz" ]
7+
}

test/fixtures/json-error.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
console.log(new URL("./file-error.json", import.meta.url));

test/fixtures/json.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
console.log(new URL("./file.json", import.meta.url));

test/minify-option.test.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,60 @@ describe("minify option", () => {
819819
expect(getWarnings(stats)).toMatchSnapshot("warnings");
820820
});
821821

822+
it("should work using when the `minify` option is `jsonMinify`", async () => {
823+
const compiler = getCompiler({
824+
entry: path.resolve(__dirname, "./fixtures/json.js"),
825+
});
826+
827+
new TerserPlugin().apply(compiler);
828+
new TerserPlugin({
829+
test: /\.json$/i,
830+
minify: TerserPlugin.jsonMinify,
831+
}).apply(compiler);
832+
833+
const stats = await compile(compiler);
834+
835+
expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
836+
expect(getErrors(stats)).toMatchSnapshot("errors");
837+
expect(getWarnings(stats)).toMatchSnapshot("warnings");
838+
});
839+
840+
it("should work using when the `minify` option is `jsonMinify` and allows to set `JSON.stringify` options", async () => {
841+
const compiler = getCompiler({
842+
entry: path.resolve(__dirname, "./fixtures/json.js"),
843+
});
844+
845+
new TerserPlugin().apply(compiler);
846+
new TerserPlugin({
847+
test: /\.json$/i,
848+
minify: TerserPlugin.jsonMinify,
849+
terserOptions: { space: 4, replacer: null },
850+
}).apply(compiler);
851+
852+
const stats = await compile(compiler);
853+
854+
expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
855+
expect(getErrors(stats)).toMatchSnapshot("errors");
856+
expect(getWarnings(stats)).toMatchSnapshot("warnings");
857+
});
858+
859+
it("should work using when the `minify` option is `jsonMinify` and output errors", async () => {
860+
const compiler = getCompiler({
861+
entry: path.resolve(__dirname, "./fixtures/json-error.js"),
862+
});
863+
864+
new TerserPlugin().apply(compiler);
865+
new TerserPlugin({
866+
test: /\.json$/i,
867+
minify: TerserPlugin.jsonMinify,
868+
}).apply(compiler);
869+
870+
const stats = await compile(compiler);
871+
872+
expect(stats.compilation.errors[0].message).toContain("Unexpected token");
873+
expect(getWarnings(stats)).toMatchSnapshot("warnings");
874+
});
875+
822876
// Due `esbuild` doesn't support extract comments we keep legal comments by default
823877
it("should work using when the `minify` option is `esbuildMinify` and keep legal comments when extract comments is disabled", async () => {
824878
const compiler = getCompiler({

types/index.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ declare namespace TerserPlugin {
7171
uglifyJsMinify,
7272
swcMinify,
7373
esbuildMinify,
74+
jsonMinify,
7475
Schema,
7576
Compiler,
7677
Compilation,
@@ -112,6 +113,7 @@ import { terserMinify } from "./utils";
112113
import { uglifyJsMinify } from "./utils";
113114
import { swcMinify } from "./utils";
114115
import { esbuildMinify } from "./utils";
116+
import { jsonMinify } from "./utils";
115117
type Schema = import("schema-utils/declarations/validate").Schema;
116118
type Compiler = import("webpack").Compiler;
117119
type Compilation = import("webpack").Compilation;
@@ -250,6 +252,10 @@ type MinimizeFunctionHelpers = {
250252
* true when minimizer support worker threads, otherwise false
251253
*/
252254
supportsWorkerThreads?: (() => boolean | undefined) | undefined;
255+
/**
256+
* true when minimizer support worker, otherwise false
257+
*/
258+
supportsWorker?: (() => boolean | undefined) | undefined;
253259
};
254260
type MinimizerImplementation<T> = BasicMinimizerImplementation<T> &
255261
MinimizeFunctionHelpers;

0 commit comments

Comments
 (0)