Skip to content

Commit 5461421

Browse files
feat: added swc minimizer (#197)
1 parent 4947cd9 commit 5461421

File tree

9 files changed

+1799
-1130
lines changed

9 files changed

+1799
-1130
lines changed

README.md

+21
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,27 @@ module.exports = {
579579
};
580580
```
581581

582+
### Using custom minifier [swc](https://github.com/swc-project/swc)
583+
584+
**webpack.config.js**
585+
586+
```js
587+
module.exports = {
588+
// Uncomment if you need source maps
589+
// devtool: "source-map",
590+
optimization: {
591+
minimize: true,
592+
minimizer: [
593+
new CssMinimizerPlugin({
594+
minify: CssMinimizerPlugin.swcMinify,
595+
// Uncomment this line for options
596+
// minimizerOptions: {},
597+
}),
598+
],
599+
},
600+
};
601+
```
602+
582603
## Contributing
583604

584605
Please take a moment to read our contributing guidelines if you haven't yet done so.

package-lock.json

+1,530-1,128
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+4
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@
5959
},
6060
"lightningcss": {
6161
"optional": true
62+
},
63+
"@swc/css": {
64+
"optional": true
6265
}
6366
},
6467
"dependencies": {
@@ -76,6 +79,7 @@
7679
"@commitlint/cli": "^17.0.0",
7780
"@commitlint/config-conventional": "^17.0.0",
7881
"@parcel/css": "^1.8.3",
82+
"@swc/css": "^0.0.16",
7983
"@types/clean-css": "^4.2.5",
8084
"@types/csso": "^5.0.0",
8185
"@types/serialize-javascript": "^5.0.2",

src/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const {
1313
esbuildMinify,
1414
parcelCssMinify,
1515
lightningCssMinify,
16+
swcMinify,
1617
} = require("./utils");
1718

1819
const schema = require("./options.json");
@@ -723,5 +724,6 @@ CssMinimizerPlugin.cleanCssMinify = cleanCssMinify;
723724
CssMinimizerPlugin.esbuildMinify = esbuildMinify;
724725
CssMinimizerPlugin.parcelCssMinify = parcelCssMinify;
725726
CssMinimizerPlugin.lightningCssMinify = lightningCssMinify;
727+
CssMinimizerPlugin.swcMinify = swcMinify;
726728

727729
module.exports = CssMinimizerPlugin;

src/utils.js

+44-2
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ async function parcelCssMinify(input, sourceMap, minimizerOptions) {
375375
// eslint-disable-next-line import/no-extraneous-dependencies, global-require
376376
const parcelCss = require("@parcel/css");
377377

378-
// Copy `esbuild` options
378+
// Copy `parcel-css` options
379379
const parcelCssOptions = buildParcelCssOptions(minimizerOptions);
380380

381381
// Let `esbuild` generate a SourceMap
@@ -419,7 +419,7 @@ async function lightningCssMinify(input, sourceMap, minimizerOptions) {
419419
// eslint-disable-next-line import/no-extraneous-dependencies, global-require
420420
const lightningCss = require("lightningcss");
421421

422-
// Copy `esbuild` options
422+
// Copy `lightningCss` options
423423
const lightningCssOptions = buildLightningCssOptions(minimizerOptions);
424424

425425
// Let `esbuild` generate a SourceMap
@@ -436,6 +436,47 @@ async function lightningCssMinify(input, sourceMap, minimizerOptions) {
436436
};
437437
}
438438

439+
/* istanbul ignore next */
440+
/**
441+
* @param {Input} input
442+
* @param {RawSourceMap | undefined} sourceMap
443+
* @param {CustomOptions} minimizerOptions
444+
* @return {Promise<MinimizedResult>}
445+
*/
446+
async function swcMinify(input, sourceMap, minimizerOptions) {
447+
const [[filename, code]] = Object.entries(input);
448+
/**
449+
* @param {*} [swcOptions={}]
450+
* @returns {*}
451+
*/
452+
const buildSwcOptions = (swcOptions = {}) => {
453+
// Need deep copy objects to avoid https://github.com/terser/terser/issues/366
454+
return {
455+
...swcOptions,
456+
filename,
457+
};
458+
};
459+
460+
// eslint-disable-next-line import/no-extraneous-dependencies, global-require
461+
const swc = require("@swc/css");
462+
463+
// Copy `swc` options
464+
const swcOptions = buildSwcOptions(minimizerOptions);
465+
466+
// Let `swc` generate a SourceMap
467+
if (sourceMap) {
468+
swcOptions.sourceMap = true;
469+
}
470+
471+
const result = await swc.minify(Buffer.from(code), swcOptions);
472+
473+
return {
474+
code: result.code.toString(),
475+
// eslint-disable-next-line no-undefined
476+
map: result.map ? JSON.parse(result.map.toString()) : undefined,
477+
};
478+
}
479+
439480
module.exports = {
440481
throttleAll,
441482
cssnanoMinify,
@@ -444,4 +485,5 @@ module.exports = {
444485
esbuildMinify,
445486
parcelCssMinify,
446487
lightningCssMinify,
488+
swcMinify,
447489
};

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

+44
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,50 @@ exports[`"minify" option should work with "CssMinimizerPlugin.parcelCssMinify" m
403403

404404
exports[`"minify" option should work with "CssMinimizerPlugin.parcelCssMinify" minifier: warning 1`] = `[]`;
405405

406+
exports[`"minify" option should work with "CssMinimizerPlugin.swcMinify" minifier and generate source maps #2: assets 1`] = `
407+
{
408+
"foo/foo.css": "body{font-weight:700}body{color:red}body a{text-align:center}
409+
/*# sourceMappingURL=foo.css.map*/",
410+
"foo/foo.css.map": "{"version":3,"file":"foo/foo.css","mappings":"AAAA,IAAI,CAAE,WACO,CAAE,GAAI,CCCnB,IAAI,CAAE,KACC,CAAE,GAAG,CAGZ,IAFE,aACY,CAAE,MAAM","sources":["webpack:///./sourcemap/bar.scss","webpack:///./sourcemap/foo.scss"],"sourcesContent":["body {\\n font-weight: bold;\\n}","@import 'bar';\\n\\nbody {\\n color: red;\\n a {\\n text-align: center;\\n }\\n}"],"names":[],"sourceRoot":""}",
411+
}
412+
`;
413+
414+
exports[`"minify" option should work with "CssMinimizerPlugin.swcMinify" minifier and generate source maps #2: error 1`] = `[]`;
415+
416+
exports[`"minify" option should work with "CssMinimizerPlugin.swcMinify" minifier and generate source maps #2: warning 1`] = `[]`;
417+
418+
exports[`"minify" option should work with "CssMinimizerPlugin.swcMinify" minifier and generate source maps: assets 1`] = `
419+
{
420+
"foo.css": "body{font-weight:700}body{color:red}body a{text-align:center}
421+
/*# sourceMappingURL=foo.css.map*/",
422+
"foo.css.map": "{"version":3,"file":"foo.css","mappings":"AAAA,IAAI,CAAE,WACO,CAAE,GAAI,CCCnB,IAAI,CAAE,KACC,CAAE,GAAG,CAGZ,IAFE,aACY,CAAE,MAAM","sources":["webpack:///./sourcemap/bar.scss","webpack:///./sourcemap/foo.scss"],"sourcesContent":["body {\\n font-weight: bold;\\n}","@import 'bar';\\n\\nbody {\\n color: red;\\n a {\\n text-align: center;\\n }\\n}"],"names":[],"sourceRoot":""}",
423+
}
424+
`;
425+
426+
exports[`"minify" option should work with "CssMinimizerPlugin.swcMinify" minifier and generate source maps: error 1`] = `[]`;
427+
428+
exports[`"minify" option should work with "CssMinimizerPlugin.swcMinify" minifier and generate source maps: warning 1`] = `[]`;
429+
430+
exports[`"minify" option should work with "CssMinimizerPlugin.swcMinify" minifier and options for "swcMinify": assets 1`] = `
431+
{
432+
"foo.css": "table.colortable{& td{text-align:center;&.c{text-transform:uppercase}&:first-child,&:first-child+td{border:1px solid black}}& th{text-align:center;background:black;color:white}}.example{display:grid;transition:all.5s;user-select:none;background:linear-gradient(to bottom,white,black)}",
433+
}
434+
`;
435+
436+
exports[`"minify" option should work with "CssMinimizerPlugin.swcMinify" minifier and options for "swcMinify": error 1`] = `[]`;
437+
438+
exports[`"minify" option should work with "CssMinimizerPlugin.swcMinify" minifier and options for "swcMinify": warning 1`] = `[]`;
439+
440+
exports[`"minify" option should work with "CssMinimizerPlugin.swcMinify" minifier: assets 1`] = `
441+
{
442+
"foo.css": "body{font-weight:700}body{color:red}body a{text-align:center}",
443+
}
444+
`;
445+
446+
exports[`"minify" option should work with "CssMinimizerPlugin.swcMinify" minifier: error 1`] = `[]`;
447+
448+
exports[`"minify" option should work with "CssMinimizerPlugin.swcMinify" minifier: warning 1`] = `[]`;
449+
406450
exports[`"minify" option should work with "clean-css" minifier: assets 1`] = `
407451
{
408452
"foo.css": "body{color:red}a{color:#00f}",

test/minify-option.test.js

+141
Original file line numberDiff line numberDiff line change
@@ -1059,6 +1059,147 @@ describe('"minify" option', () => {
10591059
expect(getWarnings(stats)).toMatchSnapshot("warning");
10601060
});
10611061

1062+
it('should work with "CssMinimizerPlugin.swcMinify" minifier', async () => {
1063+
const compiler = getCompiler({
1064+
entry: {
1065+
foo: `${__dirname}/fixtures/sourcemap/foo.scss`,
1066+
},
1067+
module: {
1068+
rules: [
1069+
{
1070+
test: /.s?css$/i,
1071+
use: [
1072+
MiniCssExtractPlugin.loader,
1073+
{ loader: "css-loader", options: { sourceMap: true } },
1074+
{ loader: "sass-loader", options: { sourceMap: true } },
1075+
],
1076+
},
1077+
],
1078+
},
1079+
});
1080+
1081+
new CssMinimizerPlugin({
1082+
minimizerOptions: {},
1083+
minify: [CssMinimizerPlugin.swcMinify],
1084+
}).apply(compiler);
1085+
1086+
const stats = await compile(compiler);
1087+
1088+
expect(readAssets(compiler, stats, /\.css(\.map)?$/)).toMatchSnapshot(
1089+
"assets"
1090+
);
1091+
expect(getErrors(stats)).toMatchSnapshot("error");
1092+
expect(getWarnings(stats)).toMatchSnapshot("warning");
1093+
});
1094+
1095+
// Need improve package for source map generation
1096+
it('should work with "CssMinimizerPlugin.swcMinify" minifier and generate source maps', async () => {
1097+
const compiler = getCompiler({
1098+
devtool: "source-map",
1099+
entry: {
1100+
foo: `${__dirname}/fixtures/sourcemap/foo.scss`,
1101+
},
1102+
module: {
1103+
rules: [
1104+
{
1105+
test: /.s?css$/i,
1106+
use: [
1107+
MiniCssExtractPlugin.loader,
1108+
{ loader: "css-loader", options: { sourceMap: true } },
1109+
{ loader: "sass-loader", options: { sourceMap: true } },
1110+
],
1111+
},
1112+
],
1113+
},
1114+
});
1115+
1116+
new CssMinimizerPlugin({
1117+
minimizerOptions: {},
1118+
minify: [CssMinimizerPlugin.swcMinify],
1119+
}).apply(compiler);
1120+
1121+
const stats = await compile(compiler);
1122+
1123+
expect(readAssets(compiler, stats, /\.css(\.map)?$/)).toMatchSnapshot(
1124+
"assets"
1125+
);
1126+
expect(getErrors(stats)).toMatchSnapshot("error");
1127+
expect(getWarnings(stats)).toMatchSnapshot("warning");
1128+
});
1129+
1130+
it('should work with "CssMinimizerPlugin.swcMinify" minifier and generate source maps #2', async () => {
1131+
const compiler = getCompiler({
1132+
devtool: "source-map",
1133+
entry: {
1134+
foo: `${__dirname}/fixtures/sourcemap/foo.scss`,
1135+
},
1136+
module: {
1137+
rules: [
1138+
{
1139+
test: /.s?css$/i,
1140+
use: [
1141+
MiniCssExtractPlugin.loader,
1142+
{ loader: "css-loader", options: { sourceMap: true } },
1143+
{ loader: "sass-loader", options: { sourceMap: true } },
1144+
],
1145+
},
1146+
],
1147+
},
1148+
plugins: [
1149+
new MiniCssExtractPlugin({
1150+
filename: "foo/[name].css",
1151+
chunkFilename: "foo/[id].[name].css",
1152+
}),
1153+
],
1154+
});
1155+
1156+
new CssMinimizerPlugin({
1157+
minimizerOptions: {},
1158+
minify: [CssMinimizerPlugin.swcMinify],
1159+
}).apply(compiler);
1160+
1161+
const stats = await compile(compiler);
1162+
1163+
expect(readAssets(compiler, stats, /\.css(\.map)?$/)).toMatchSnapshot(
1164+
"assets"
1165+
);
1166+
expect(getErrors(stats)).toMatchSnapshot("error");
1167+
expect(getWarnings(stats)).toMatchSnapshot("warning");
1168+
});
1169+
1170+
// TODO improve test when options will come
1171+
it('should work with "CssMinimizerPlugin.swcMinify" minifier and options for "swcMinify"', async () => {
1172+
const compiler = getCompiler({
1173+
entry: {
1174+
foo: `${__dirname}/fixtures/nesting.css`,
1175+
},
1176+
module: {
1177+
rules: [
1178+
{
1179+
test: /\.css$/i,
1180+
use: [
1181+
MiniCssExtractPlugin.loader,
1182+
{ loader: "css-loader", options: { sourceMap: true } },
1183+
],
1184+
},
1185+
],
1186+
},
1187+
});
1188+
1189+
new CssMinimizerPlugin({
1190+
minimizerOptions: {},
1191+
minify: [CssMinimizerPlugin.swcMinify],
1192+
}).apply(compiler);
1193+
1194+
const stats = await compile(compiler);
1195+
1196+
expect(readAssets(compiler, stats, /\.css(\.map)?$/)).toMatchSnapshot(
1197+
"assets"
1198+
);
1199+
expect(getErrors(stats)).toMatchSnapshot("error");
1200+
expect(getWarnings(stats)).toMatchSnapshot("warning");
1201+
});
1202+
10621203
it("should work throw an error if minimizer function doesn't return", async () => {
10631204
const compiler = getCompiler({
10641205
entry: {

types/index.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ declare namespace CssMinimizerPlugin {
7070
esbuildMinify,
7171
parcelCssMinify,
7272
lightningCssMinify,
73+
swcMinify,
7374
Schema,
7475
Compiler,
7576
Compilation,
@@ -132,6 +133,7 @@ import { cleanCssMinify } from "./utils";
132133
import { esbuildMinify } from "./utils";
133134
import { parcelCssMinify } from "./utils";
134135
import { lightningCssMinify } from "./utils";
136+
import { swcMinify } from "./utils";
135137
type Schema = import("schema-utils/declarations/validate").Schema;
136138
type Compilation = import("webpack").Compilation;
137139
type WebpackError = import("webpack").WebpackError;

types/utils.d.ts

+11
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,14 @@ export function lightningCssMinify(
8484
sourceMap: RawSourceMap | undefined,
8585
minimizerOptions: CustomOptions
8686
): Promise<MinimizedResult>;
87+
/**
88+
* @param {Input} input
89+
* @param {RawSourceMap | undefined} sourceMap
90+
* @param {CustomOptions} minimizerOptions
91+
* @return {Promise<MinimizedResult>}
92+
*/
93+
export function swcMinify(
94+
input: Input,
95+
sourceMap: RawSourceMap | undefined,
96+
minimizerOptions: CustomOptions
97+
): Promise<MinimizedResult>;

0 commit comments

Comments
 (0)