Skip to content

Commit

Permalink
feat: added swcMinifyFragment to minify HTML fragments
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akait committed Oct 13, 2022
1 parent afaf453 commit 021c752
Show file tree
Hide file tree
Showing 8 changed files with 254 additions and 8 deletions.
64 changes: 63 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,24 @@ module.exports = {

// For `@swc/html`:
//
// HTML documents - HTML documents with `Doctype` and `<html>/`<head>`/`<body>` tags
//
// Options - https://github.com/swc-project/bindings/blob/main/packages/html/index.ts#L5
//
// new HtmlMinimizerPlugin({
// minify: HtmlMinimizerPlugin.swcMinify,
// minimizerOptions: {}
// })
//
//
// HTML fragments - HTML fragments, i.e. HTML files without doctype or used in `<template>` tags or HTML parts which injects into another HTML parts
//
// Options - https://github.com/swc-project/bindings/blob/main/packages/html/index.ts#L38
//
// new HtmlMinimizerPlugin({
// minify: HtmlMinimizerPlugin.swcMinifyFragment,
// minimizerOptions: {}
// })
],
},
};
Expand Down Expand Up @@ -297,7 +309,13 @@ By default, plugin uses [html-minifier-terser](https://github.com/terser/html-mi
We currently support:

- `HtmlMinimizerPlugin.htmlMinifierTerser`
- `HtmlMinimizerPlugin.swcMinify`
- `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)

> **Note**
>
> The difference between `swcMinify` and `swcMinifyFragment` is the error reporting.
> You will get errors (invalid or broken syntax) if you have them in your HTML document or fragment. Which allows you to see all the errors and problems at the build stage.
Useful for using and testing unpublished versions or forks.

Expand Down Expand Up @@ -454,6 +472,8 @@ module.exports = {

Available [`options`](https://github.com/swc-project/bindings/blob/main/packages/html/index.ts#L5).

HTML Documents:

```js
const HtmlMinimizerPlugin = require("html-minimizer-webpack-plugin");
const CopyPlugin = require("copy-webpack-plugin");
Expand Down Expand Up @@ -491,6 +511,48 @@ module.exports = {
};
```

HTML Framgents:

```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({
test: /\.template\.html$/i,
minify: HtmlMinimizerPlugin.swcMinifyFragment,
minimizerOptions: {
// Options - https://github.com/swc-project/bindings/blob/main/packages/html/index.ts#L38
},
}),
],
},
};
```

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

## Contributing

Please take a moment to read our contributing guidelines if you haven't yet done so.
Expand Down
12 changes: 9 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ const { Worker } = require("jest-worker");

const schema = require("./options.json");

const { throttleAll, htmlMinifierTerser, swcMinify } = require("./utils");
const { minify } = require("./minify");
const {
throttleAll,
htmlMinifierTerser,
swcMinify,
swcMinifyFragment,
} = require("./utils");
const { minify: minifyInternal } = require("./minify");

/** @typedef {import("schema-utils/declarations/validate").Schema} Schema */
/** @typedef {import("webpack").Compiler} Compiler */
Expand Down Expand Up @@ -378,7 +383,7 @@ class HtmlMinimizerPlugin {
try {
output = await (getWorker
? getWorker().transform(serialize(options))
: minify(options));
: minifyInternal(options));
} catch (error) {
compilation.errors.push(
/** @type {WebpackError} */
Expand Down Expand Up @@ -475,5 +480,6 @@ class HtmlMinimizerPlugin {

HtmlMinimizerPlugin.htmlMinifierTerser = htmlMinifierTerser;
HtmlMinimizerPlugin.swcMinify = swcMinify;
HtmlMinimizerPlugin.swcMinifyFragment = swcMinifyFragment;

module.exports = HtmlMinimizerPlugin;
43 changes: 40 additions & 3 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,14 +112,46 @@ async function htmlMinifierTerser(input, minimizerOptions = {}) {
async function swcMinify(input, minimizerOptions = {}) {
// eslint-disable-next-line global-require, import/no-extraneous-dependencies, import/no-unresolved
const swcMinifier = require("@swc/html");

const [[, code]] = Object.entries(input);
// TODO `import("@swc/html").Options`
const options = /** @type {*} */ ({
...minimizerOptions,
});
const result = await swcMinifier.minify(Buffer.from(code), options);

let errors;

if (typeof result.errors !== "undefined") {
errors = result.errors.map((diagnostic) => {
const error = new Error(diagnostic.message);

// @ts-ignore
error.span = diagnostic.span;
// @ts-ignore
error.level = diagnostic.level;

return error;
});
}

return { code: result.code, errors };
}

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

let errors;

Expand All @@ -139,4 +171,9 @@ async function swcMinify(input, minimizerOptions = {}) {
return { code: result.code, errors };
}

module.exports = { throttleAll, htmlMinifierTerser, swcMinify };
module.exports = {
throttleAll,
htmlMinifierTerser,
swcMinify,
swcMinifyFragment,
};
75 changes: 75 additions & 0 deletions test/__snapshots__/minify-option.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,78 @@ exports[`"minify" option should work with 'swcMinify': assets 1`] = `
exports[`"minify" option should work with 'swcMinify': errors 1`] = `[]`;

exports[`"minify" option should work with 'swcMinify': warnings 1`] = `[]`;

exports[`"minify" option should work with 'swcMinifyFragment' and options: assets 1`] = `
{
"template.html": "<div> <ul class="bar baz foo"> <li>test</li> <li>test</li> <li>test</li> </ul> </div> <span>test</span> ",
}
`;

exports[`"minify" option should work with 'swcMinifyFragment' and options: errors 1`] = `[]`;

exports[`"minify" option should work with 'swcMinifyFragment' and options: warnings 1`] = `[]`;

exports[`"minify" option should work with 'swcMinifyFragment' and throw errors: assets 1`] = `
{
"broken-html-syntax.html": "Text &lt; img src="image.png" >
Text &lt;<img src=image.png>
Text ><img src=image.png>
<a foo><bar></bar></a><boo>boohay
&lt;&lt;&lt;&lt;>foo
>>&lt;</boo>",
}
`;

exports[`"minify" option should work with 'swcMinifyFragment' and throw errors: errors 1`] = `
[
"Error: broken-html-syntax.html from Html Minimizer plugin
Abrupt closing of empty comment",
"Error: broken-html-syntax.html from Html Minimizer plugin
Cdata in html content",
"Error: broken-html-syntax.html from Html Minimizer plugin
End tag "a" violates nesting rules",
"Error: broken-html-syntax.html from Html Minimizer plugin
Eof in tag",
"Error: broken-html-syntax.html from Html Minimizer plugin
Incorrectly opened comment",
"Error: broken-html-syntax.html from Html Minimizer plugin
Invalid first character of tag name",
"Error: broken-html-syntax.html from Html Minimizer plugin
Invalid first character of tag name",
"Error: broken-html-syntax.html from Html Minimizer plugin
Invalid first character of tag name",
"Error: broken-html-syntax.html from Html Minimizer plugin
Invalid first character of tag name",
"Error: broken-html-syntax.html from Html Minimizer plugin
Invalid first character of tag name",
"Error: broken-html-syntax.html from Html Minimizer plugin
Invalid first character of tag name",
"Error: broken-html-syntax.html from Html Minimizer plugin
Invalid first character of tag name",
"Error: broken-html-syntax.html from Html Minimizer plugin
Non void html element start tag with trailing solidus",
"Error: broken-html-syntax.html from Html Minimizer plugin
Unexpected question mark instead of tag name",
]
`;

exports[`"minify" option should work with 'swcMinifyFragment' and throw errors: warnings 1`] = `[]`;

exports[`"minify" option should work with 'swcMinifyFragment': assets 1`] = `
{
"template.html": "<div>
<ul class="bar baz foo">
<li>test</li>
<li>test</li>
<li>test</li>
</ul>
</div>
<span>test</span>
",
}
`;

exports[`"minify" option should work with 'swcMinifyFragment': errors 1`] = `[]`;

exports[`"minify" option should work with 'swcMinifyFragment': warnings 1`] = `[]`;
8 changes: 8 additions & 0 deletions test/fixtures/template.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<div>
<ul class=" foo bar baz ">
<li>test</li>
<li>test</li>
<li>test</li>
</ul>
</div>
<span>test</span>
48 changes: 48 additions & 0 deletions test/minify-option.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,4 +238,52 @@ describe('"minify" option', () => {
expect(getErrors(stats)).toMatchSnapshot("errors");
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});

it("should work with 'swcMinifyFragment'", async () => {
const testHtmlId = "./template.html";
const compiler = getCompiler(testHtmlId);

new HtmlMinimizerPlugin({
minify: HtmlMinimizerPlugin.swcMinifyFragment,
}).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 'swcMinifyFragment' and throw errors", async () => {
const testHtmlId = "./broken-html-syntax.html";
const compiler = getCompiler(testHtmlId);

new HtmlMinimizerPlugin({
minify: HtmlMinimizerPlugin.swcMinifyFragment,
}).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 'swcMinifyFragment' and options", async () => {
const testHtmlId = "./template.html";
const compiler = getCompiler(testHtmlId);

new HtmlMinimizerPlugin({
minimizerOptions: {
collapseWhitespaces: "advanced-conservative",
},
minify: HtmlMinimizerPlugin.swcMinifyFragment,
}).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");
});
});
3 changes: 2 additions & 1 deletion types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ declare namespace HtmlMinimizerPlugin {
export {
htmlMinifierTerser,
swcMinify,
swcMinifyFragment,
Schema,
Compiler,
Compilation,
Expand Down Expand Up @@ -191,6 +192,7 @@ type DefinedDefaultMinimizerAndOptions<T> =
};
import { htmlMinifierTerser } from "./utils";
import { swcMinify } from "./utils";
import { swcMinifyFragment } from "./utils";
type Schema = import("schema-utils/declarations/validate").Schema;
type Compilation = import("webpack").Compilation;
type WebpackError = import("webpack").WebpackError;
Expand Down Expand Up @@ -246,5 +248,4 @@ type InternalPluginOptions<T> = BasePluginOptions & {
: never
: Minimizer<T>;
};
import { minify } from "./minify";
import { Worker } from "jest-worker";
9 changes: 9 additions & 0 deletions types/utils.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,12 @@ export function swcMinify(
input: Input,
minimizerOptions?: CustomOptions | undefined
): Promise<MinimizedResult>;
/**
* @param {Input} input
* @param {CustomOptions | undefined} [minimizerOptions]
* @returns {Promise<MinimizedResult>}
*/
export function swcMinifyFragment(
input: Input,
minimizerOptions?: CustomOptions | undefined
): Promise<MinimizedResult>;

0 comments on commit 021c752

Please sign in to comment.