Skip to content

Commit

Permalink
feat: added @minify-html/node support
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akait committed Jun 10, 2023
1 parent 057a43d commit f6364cb
Show file tree
Hide file tree
Showing 8 changed files with 181 additions and 5 deletions.
74 changes: 69 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@

# html-minimizer-webpack-plugin

This plugin can use 2 tools to optimize and minify your HTML:
This plugin can use 3 tools to optimize and minify your HTML:

- [`html-minifier-terser`](https://github.com/terser/html-minifier-terser) (by default) - JavaScript-based HTML minifier.
- [`swc`](https://github.com/swc-project/swc) - very fast Rust-based platform for the Web.
- [`html-minifier-terser`](https://github.com/terser/html-minifier-terser) (by default) - JavaScript-based HTML minifier.
- [`@minify-html/node`](https://github.com/wilsonzlin/minify-html) - A Rust HTML minifier meticulously optimised for speed and effectiveness, with bindings for other languages.

## Getting Started

Expand Down Expand Up @@ -56,6 +57,24 @@ or
pnpm add -D @swc/html
```

**Additional step**: If you want to use `@minify-html/node` you need to install it:

```console
npm install @minify-html/node --save-dev
```

or

```console
yarn add -D @minify-html/node
```

or

```console
pnpm add -D @minify-html/node
```

Then add the plugin to your `webpack` configuration. For example:

**webpack.config.js**
Expand Down Expand Up @@ -127,8 +146,9 @@ And run `webpack` via your preferred method.
>
> Removing and collapsing spaces in the tools differ (by default).
>
> - `html-minifier-terser` - always collapse multiple whitespaces to 1 space (never remove it entirely), but you can change it using [`options`](https://github.com/terser/html-minifier-terser#options-quick-reference)
> - `@swc/html` - remove and collapse whitespaces only in safe places (for example - around `html` and `body` elements, inside the `head` element and between metadata elements - `<meta>`/`script`/`link`/etc.)
> - `html-minifier-terser` - always collapse multiple whitespaces to 1 space (never remove it entirely), but you can change it using [`options`](https://github.com/terser/html-minifier-terser#options-quick-reference)
> - `@minify-html/node` - please read documentation https://github.com/wilsonzlin/minify-html#whitespace
## Options

Expand Down Expand Up @@ -308,9 +328,10 @@ By default, plugin uses [html-minifier-terser](https://github.com/terser/html-mi

We currently support:

- `HtmlMinimizerPlugin.htmlMinifierTerser`
- `HtmlMinimizerPlugin.swcMinify` (used to compress HTML documents, i.e. with HTML doctype and `<html>`/`<body>`/`<head>` tags)
- `HtmlMinimizerPlugin.swcMinifyFragment` (used to compress HTML fragments, i.e. when you have part of HTML which will be inserted into another HTML parts)
- `HtmlMinimizerPlugin.htmlMinifierTerser`
- `HtmlMinimizerPlugin.minifyHtmlNode`

> **Note**
>
Expand Down Expand Up @@ -511,7 +532,7 @@ module.exports = {
};
```

HTML Framgents:
HTML Fragments:

```js
const HtmlMinimizerPlugin = require("html-minimizer-webpack-plugin");
Expand Down Expand Up @@ -551,6 +572,49 @@ module.exports = {
};
```

### `@minify-html/node`

Available [`options`](https://github.com/wilsonzlin/minify-html#minification).

HTML Documents:

```js
const HtmlMinimizerPlugin = require("html-minimizer-webpack-plugin");
const CopyPlugin = require("copy-webpack-plugin");

module.exports = {
module: {
rules: [
{
test: /\.html$/i,
type: "asset/resource",
},
],
},
plugins: [
new CopyPlugin({
patterns: [
{
context: path.resolve(__dirname, "dist"),
from: "./src/*.html",
},
],
}),
],
optimization: {
minimize: true,
minimizer: [
new HtmlMinimizerPlugin({
minify: HtmlMinimizerPlugin.minifyHtmlNode,
minimizerOptions: {
// Options - https://github.com/wilsonzlin/minify-html#minification
},
}),
],
},
};
```

You can use multiple `HtmlMinimizerPlugin` plugins to compress different files with the different `minify` function.

## Contributing
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"@babel/preset-env": "^7.22.4",
"@commitlint/cli": "^17.6.5",
"@commitlint/config-conventional": "^17.6.5",
"@minify-html/node": "^0.11.1",
"@swc/html": "^0.0.18",
"@types/node": "^18.16.3",
"@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 @@ -11,6 +11,7 @@ const {
htmlMinifierTerser,
swcMinify,
swcMinifyFragment,
minifyHtmlNode,
} = require("./utils");
const { minify: minifyInternal } = require("./minify");

Expand Down Expand Up @@ -480,5 +481,6 @@ class HtmlMinimizerPlugin {
HtmlMinimizerPlugin.htmlMinifierTerser = htmlMinifierTerser;
HtmlMinimizerPlugin.swcMinify = swcMinify;
HtmlMinimizerPlugin.swcMinifyFragment = swcMinifyFragment;
HtmlMinimizerPlugin.minifyHtmlNode = minifyHtmlNode;

module.exports = HtmlMinimizerPlugin;
20 changes: 20 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,25 @@ async function htmlMinifierTerser(input, minimizerOptions = {}) {
return { code: result };
}

/**
* @param {Input} input
* @param {CustomOptions | undefined} [minimizerOptions]
* @returns {Promise<MinimizedResult>}
*/
/* istanbul ignore next */
async function minifyHtmlNode(input, minimizerOptions = {}) {
// eslint-disable-next-line global-require, import/no-extraneous-dependencies, import/no-unresolved
const minifyHtmlPkg = require("@minify-html/node");
const [[, code]] = Object.entries(input);
const options =
/** @type {Parameters<import("@minify-html/node").minify>[1]} */ ({
...minimizerOptions,
});
const result = await minifyHtmlPkg.minify(Buffer.from(code), options);

return { code: result.toString() };
}

/**
* @param {Input} input
* @param {CustomOptions | undefined} [minimizerOptions]
Expand Down Expand Up @@ -174,4 +193,5 @@ module.exports = {
htmlMinifierTerser,
swcMinify,
swcMinifyFragment,
minifyHtmlNode,
};
30 changes: 30 additions & 0 deletions test/__snapshots__/minify-option.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,36 @@ exports[`"minify" option should work minify function: errors 1`] = `[]`;

exports[`"minify" option should work minify function: warnings 1`] = `[]`;

exports[`"minify" option should work with '@minify-html/node' and broken syntax: assets 1`] = `
{
"broken-html-syntax.html": "Text < img src="image.png" > Text <<img src=image.png> Text ><img src=image.png><a foo><bar><![CDATA[]]]><?foo?><!bar><boo>boohay <<<<>foo ",
}
`;

exports[`"minify" option should work with '@minify-html/node' and broken syntax: errors 1`] = `[]`;

exports[`"minify" option should work with '@minify-html/node' and broken syntax: warnings 1`] = `[]`;

exports[`"minify" option should work with '@minify-html/node' and options: assets 1`] = `
{
"simple.html": "<!doctype html><html lang=en><meta charset=UTF-8><meta content=width=device-width,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0 name=viewport><meta content=ie=edge http-equiv=X-UA-Compatible><title>Document</title><body><h1>My First Heading</h1><p>My first paragraph.<h2>An Unordered HTML List</h2><ul><li>Coffee<li>Tea<li>Milk</ul><h2>An Ordered HTML List</h2><ol><li>Coffee<li>Tea<li>Milk</ol>",
}
`;

exports[`"minify" option should work with '@minify-html/node' and options: errors 1`] = `[]`;

exports[`"minify" option should work with '@minify-html/node' and options: warnings 1`] = `[]`;

exports[`"minify" option should work with '@minify-html/node': assets 1`] = `
{
"simple.html": "<!doctypehtml><html lang=en><meta charset=UTF-8><meta content=width=device-width,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0 name=viewport><meta content=ie=edge http-equiv=X-UA-Compatible><title>Document</title><body><h1>My First Heading</h1><p>My first paragraph.<h2>An Unordered HTML List</h2><ul><li>Coffee<li>Tea<li>Milk</ul><h2>An Ordered HTML List</h2><ol><li>Coffee<li>Tea<li>Milk</ol>",
}
`;

exports[`"minify" option should work with '@minify-html/node': errors 1`] = `[]`;

exports[`"minify" option should work with '@minify-html/node': warnings 1`] = `[]`;

exports[`"minify" option should work with 'swcMinify' and options: assets 1`] = `
{
"simple.html": "<!doctype html><html lang=en><meta charset=UTF-8><meta name=viewport content="width=device-width,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0"><meta http-equiv=X-UA-Compatible content="ie=edge"><title>Document</title><h1>My First Heading</h1> <p>My first paragraph.</p> <h2>An Unordered HTML List</h2> <ul> <li>Coffee</li> <li>Tea</li> <li>Milk</li> </ul> <h2>An Ordered HTML List</h2> <ol> <li>Coffee</li> <li>Tea</li> <li>Milk</li> </ol>",
Expand Down
48 changes: 48 additions & 0 deletions test/minify-option.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,4 +286,52 @@ describe('"minify" option', () => {
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});

it("should work with '@minify-html/node'", async () => {
const testHtmlId = "./simple.html";
const compiler = getCompiler(testHtmlId);

new HtmlMinimizerPlugin({
minify: HtmlMinimizerPlugin.minifyHtmlNode,
}).apply(compiler);

const stats = await compile(compiler);

expect(readAssets(compiler, stats, /\.html$/i)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});

it("should work with '@minify-html/node' and broken syntax", async () => {
const testHtmlId = "./broken-html-syntax.html";
const compiler = getCompiler(testHtmlId);

new HtmlMinimizerPlugin({
minify: HtmlMinimizerPlugin.minifyHtmlNode,
}).apply(compiler);

const stats = await compile(compiler);

expect(readAssets(compiler, stats, /\.html$/i)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});

it("should work with '@minify-html/node' and options", async () => {
const testHtmlId = "./simple.html";
const compiler = getCompiler(testHtmlId);

new HtmlMinimizerPlugin({
minimizerOptions: {
do_not_minify_doctype: true,
},
minify: HtmlMinimizerPlugin.minifyHtmlNode,
}).apply(compiler);

const stats = await compile(compiler);

expect(readAssets(compiler, stats, /\.html$/i)).toMatchSnapshot("assets");
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});
});
2 changes: 2 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ declare namespace HtmlMinimizerPlugin {
htmlMinifierTerser,
swcMinify,
swcMinifyFragment,
minifyHtmlNode,
Schema,
Compiler,
Compilation,
Expand Down Expand Up @@ -193,6 +194,7 @@ type DefinedDefaultMinimizerAndOptions<T> =
import { htmlMinifierTerser } from "./utils";
import { swcMinify } from "./utils";
import { swcMinifyFragment } from "./utils";
import { minifyHtmlNode } from "./utils";
type Schema = import("schema-utils/declarations/validate").Schema;
type Compilation = import("webpack").Compilation;
type WebpackError = import("webpack").WebpackError;
Expand Down
9 changes: 9 additions & 0 deletions types/utils.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,12 @@ export function swcMinifyFragment(
input: Input,
minimizerOptions?: CustomOptions | undefined
): Promise<MinimizedResult>;
/**
* @param {Input} input
* @param {CustomOptions | undefined} [minimizerOptions]
* @returns {Promise<MinimizedResult>}
*/
export function minifyHtmlNode(
input: Input,
minimizerOptions?: CustomOptions | undefined
): Promise<MinimizedResult>;

0 comments on commit f6364cb

Please sign in to comment.