Skip to content

Commit

Permalink
feat: added swc minimizer (#197)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akait authored Sep 29, 2022
1 parent 4947cd9 commit 5461421
Show file tree
Hide file tree
Showing 9 changed files with 1,799 additions and 1,130 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,27 @@ module.exports = {
};
```

### Using custom minifier [swc](https://github.com/swc-project/swc)

**webpack.config.js**

```js
module.exports = {
// Uncomment if you need source maps
// devtool: "source-map",
optimization: {
minimize: true,
minimizer: [
new CssMinimizerPlugin({
minify: CssMinimizerPlugin.swcMinify,
// Uncomment this line for options
// minimizerOptions: {},
}),
],
},
};
```

## Contributing

Please take a moment to read our contributing guidelines if you haven't yet done so.
Expand Down
2,658 changes: 1,530 additions & 1,128 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@
},
"lightningcss": {
"optional": true
},
"@swc/css": {
"optional": true
}
},
"dependencies": {
Expand All @@ -76,6 +79,7 @@
"@commitlint/cli": "^17.0.0",
"@commitlint/config-conventional": "^17.0.0",
"@parcel/css": "^1.8.3",
"@swc/css": "^0.0.16",
"@types/clean-css": "^4.2.5",
"@types/csso": "^5.0.0",
"@types/serialize-javascript": "^5.0.2",
Expand Down
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const {
esbuildMinify,
parcelCssMinify,
lightningCssMinify,
swcMinify,
} = require("./utils");

const schema = require("./options.json");
Expand Down Expand Up @@ -723,5 +724,6 @@ CssMinimizerPlugin.cleanCssMinify = cleanCssMinify;
CssMinimizerPlugin.esbuildMinify = esbuildMinify;
CssMinimizerPlugin.parcelCssMinify = parcelCssMinify;
CssMinimizerPlugin.lightningCssMinify = lightningCssMinify;
CssMinimizerPlugin.swcMinify = swcMinify;

module.exports = CssMinimizerPlugin;
46 changes: 44 additions & 2 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ async function parcelCssMinify(input, sourceMap, minimizerOptions) {
// eslint-disable-next-line import/no-extraneous-dependencies, global-require
const parcelCss = require("@parcel/css");

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

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

// Copy `esbuild` options
// Copy `lightningCss` options
const lightningCssOptions = buildLightningCssOptions(minimizerOptions);

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

/* istanbul ignore next */
/**
* @param {Input} input
* @param {RawSourceMap | undefined} sourceMap
* @param {CustomOptions} minimizerOptions
* @return {Promise<MinimizedResult>}
*/
async function swcMinify(input, sourceMap, minimizerOptions) {
const [[filename, code]] = Object.entries(input);
/**
* @param {*} [swcOptions={}]
* @returns {*}
*/
const buildSwcOptions = (swcOptions = {}) => {
// Need deep copy objects to avoid https://github.com/terser/terser/issues/366
return {
...swcOptions,
filename,
};
};

// eslint-disable-next-line import/no-extraneous-dependencies, global-require
const swc = require("@swc/css");

// Copy `swc` options
const swcOptions = buildSwcOptions(minimizerOptions);

// Let `swc` generate a SourceMap
if (sourceMap) {
swcOptions.sourceMap = true;
}

const result = await swc.minify(Buffer.from(code), swcOptions);

return {
code: result.code.toString(),
// eslint-disable-next-line no-undefined
map: result.map ? JSON.parse(result.map.toString()) : undefined,
};
}

module.exports = {
throttleAll,
cssnanoMinify,
Expand All @@ -444,4 +485,5 @@ module.exports = {
esbuildMinify,
parcelCssMinify,
lightningCssMinify,
swcMinify,
};
44 changes: 44 additions & 0 deletions test/__snapshots__/minify-option.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,50 @@ exports[`"minify" option should work with "CssMinimizerPlugin.parcelCssMinify" m

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

exports[`"minify" option should work with "CssMinimizerPlugin.swcMinify" minifier and generate source maps #2: assets 1`] = `
{
"foo/foo.css": "body{font-weight:700}body{color:red}body a{text-align:center}
/*# sourceMappingURL=foo.css.map*/",
"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":""}",
}
`;

exports[`"minify" option should work with "CssMinimizerPlugin.swcMinify" minifier and generate source maps #2: error 1`] = `[]`;

exports[`"minify" option should work with "CssMinimizerPlugin.swcMinify" minifier and generate source maps #2: warning 1`] = `[]`;

exports[`"minify" option should work with "CssMinimizerPlugin.swcMinify" minifier and generate source maps: assets 1`] = `
{
"foo.css": "body{font-weight:700}body{color:red}body a{text-align:center}
/*# sourceMappingURL=foo.css.map*/",
"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":""}",
}
`;

exports[`"minify" option should work with "CssMinimizerPlugin.swcMinify" minifier and generate source maps: error 1`] = `[]`;

exports[`"minify" option should work with "CssMinimizerPlugin.swcMinify" minifier and generate source maps: warning 1`] = `[]`;

exports[`"minify" option should work with "CssMinimizerPlugin.swcMinify" minifier and options for "swcMinify": assets 1`] = `
{
"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)}",
}
`;

exports[`"minify" option should work with "CssMinimizerPlugin.swcMinify" minifier and options for "swcMinify": error 1`] = `[]`;

exports[`"minify" option should work with "CssMinimizerPlugin.swcMinify" minifier and options for "swcMinify": warning 1`] = `[]`;

exports[`"minify" option should work with "CssMinimizerPlugin.swcMinify" minifier: assets 1`] = `
{
"foo.css": "body{font-weight:700}body{color:red}body a{text-align:center}",
}
`;

exports[`"minify" option should work with "CssMinimizerPlugin.swcMinify" minifier: error 1`] = `[]`;

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

exports[`"minify" option should work with "clean-css" minifier: assets 1`] = `
{
"foo.css": "body{color:red}a{color:#00f}",
Expand Down
141 changes: 141 additions & 0 deletions test/minify-option.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,147 @@ describe('"minify" option', () => {
expect(getWarnings(stats)).toMatchSnapshot("warning");
});

it('should work with "CssMinimizerPlugin.swcMinify" minifier', async () => {
const compiler = getCompiler({
entry: {
foo: `${__dirname}/fixtures/sourcemap/foo.scss`,
},
module: {
rules: [
{
test: /.s?css$/i,
use: [
MiniCssExtractPlugin.loader,
{ loader: "css-loader", options: { sourceMap: true } },
{ loader: "sass-loader", options: { sourceMap: true } },
],
},
],
},
});

new CssMinimizerPlugin({
minimizerOptions: {},
minify: [CssMinimizerPlugin.swcMinify],
}).apply(compiler);

const stats = await compile(compiler);

expect(readAssets(compiler, stats, /\.css(\.map)?$/)).toMatchSnapshot(
"assets"
);
expect(getErrors(stats)).toMatchSnapshot("error");
expect(getWarnings(stats)).toMatchSnapshot("warning");
});

// Need improve package for source map generation
it('should work with "CssMinimizerPlugin.swcMinify" minifier and generate source maps', async () => {
const compiler = getCompiler({
devtool: "source-map",
entry: {
foo: `${__dirname}/fixtures/sourcemap/foo.scss`,
},
module: {
rules: [
{
test: /.s?css$/i,
use: [
MiniCssExtractPlugin.loader,
{ loader: "css-loader", options: { sourceMap: true } },
{ loader: "sass-loader", options: { sourceMap: true } },
],
},
],
},
});

new CssMinimizerPlugin({
minimizerOptions: {},
minify: [CssMinimizerPlugin.swcMinify],
}).apply(compiler);

const stats = await compile(compiler);

expect(readAssets(compiler, stats, /\.css(\.map)?$/)).toMatchSnapshot(
"assets"
);
expect(getErrors(stats)).toMatchSnapshot("error");
expect(getWarnings(stats)).toMatchSnapshot("warning");
});

it('should work with "CssMinimizerPlugin.swcMinify" minifier and generate source maps #2', async () => {
const compiler = getCompiler({
devtool: "source-map",
entry: {
foo: `${__dirname}/fixtures/sourcemap/foo.scss`,
},
module: {
rules: [
{
test: /.s?css$/i,
use: [
MiniCssExtractPlugin.loader,
{ loader: "css-loader", options: { sourceMap: true } },
{ loader: "sass-loader", options: { sourceMap: true } },
],
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: "foo/[name].css",
chunkFilename: "foo/[id].[name].css",
}),
],
});

new CssMinimizerPlugin({
minimizerOptions: {},
minify: [CssMinimizerPlugin.swcMinify],
}).apply(compiler);

const stats = await compile(compiler);

expect(readAssets(compiler, stats, /\.css(\.map)?$/)).toMatchSnapshot(
"assets"
);
expect(getErrors(stats)).toMatchSnapshot("error");
expect(getWarnings(stats)).toMatchSnapshot("warning");
});

// TODO improve test when options will come
it('should work with "CssMinimizerPlugin.swcMinify" minifier and options for "swcMinify"', async () => {
const compiler = getCompiler({
entry: {
foo: `${__dirname}/fixtures/nesting.css`,
},
module: {
rules: [
{
test: /\.css$/i,
use: [
MiniCssExtractPlugin.loader,
{ loader: "css-loader", options: { sourceMap: true } },
],
},
],
},
});

new CssMinimizerPlugin({
minimizerOptions: {},
minify: [CssMinimizerPlugin.swcMinify],
}).apply(compiler);

const stats = await compile(compiler);

expect(readAssets(compiler, stats, /\.css(\.map)?$/)).toMatchSnapshot(
"assets"
);
expect(getErrors(stats)).toMatchSnapshot("error");
expect(getWarnings(stats)).toMatchSnapshot("warning");
});

it("should work throw an error if minimizer function doesn't return", async () => {
const compiler = getCompiler({
entry: {
Expand Down
2 changes: 2 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ declare namespace CssMinimizerPlugin {
esbuildMinify,
parcelCssMinify,
lightningCssMinify,
swcMinify,
Schema,
Compiler,
Compilation,
Expand Down Expand Up @@ -132,6 +133,7 @@ import { cleanCssMinify } from "./utils";
import { esbuildMinify } from "./utils";
import { parcelCssMinify } from "./utils";
import { lightningCssMinify } from "./utils";
import { swcMinify } from "./utils";
type Schema = import("schema-utils/declarations/validate").Schema;
type Compilation = import("webpack").Compilation;
type WebpackError = import("webpack").WebpackError;
Expand Down
11 changes: 11 additions & 0 deletions types/utils.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,14 @@ export function lightningCssMinify(
sourceMap: RawSourceMap | undefined,
minimizerOptions: CustomOptions
): Promise<MinimizedResult>;
/**
* @param {Input} input
* @param {RawSourceMap | undefined} sourceMap
* @param {CustomOptions} minimizerOptions
* @return {Promise<MinimizedResult>}
*/
export function swcMinify(
input: Input,
sourceMap: RawSourceMap | undefined,
minimizerOptions: CustomOptions
): Promise<MinimizedResult>;

0 comments on commit 5461421

Please sign in to comment.