html-minimizer-webpack-plugin
Advanced tools
Comparing version 4.3.0 to 4.4.0
"use strict"; | ||
const os = require("os"); | ||
const { | ||
validate | ||
} = require("schema-utils"); | ||
const serialize = require("serialize-javascript"); | ||
const { | ||
Worker | ||
} = require("jest-worker"); | ||
const schema = require("./options.json"); | ||
const { | ||
throttleAll, | ||
htmlMinifierTerser, | ||
swcMinify, | ||
swcMinifyFragment | ||
swcMinifyFragment, | ||
minifyHtmlNode | ||
} = require("./utils"); | ||
const schema = require("./options.json"); | ||
const { | ||
minify: minifyInternal | ||
} = require("./minify"); | ||
/** @typedef {import("schema-utils/declarations/validate").Schema} Schema */ | ||
/** @typedef {import("webpack").Compiler} Compiler */ | ||
/** @typedef {import("webpack").Compilation} Compilation */ | ||
/** @typedef {import("webpack").WebpackError} WebpackError */ | ||
/** @typedef {import("webpack").Asset} Asset */ | ||
/** @typedef {import("jest-worker").Worker} JestWorker */ | ||
/** @typedef {import("./utils.js").HtmlMinifierTerserOptions} HtmlMinifierTerserOptions */ | ||
/** @typedef {RegExp | string} Rule */ | ||
/** @typedef {Rule[] | Rule} Rules */ | ||
@@ -111,5 +96,5 @@ | ||
* @typedef {Object} BasePluginOptions | ||
* @property {Rules} [test] | ||
* @property {Rules} [include] | ||
* @property {Rules} [exclude] | ||
* @property {Rule} [test] | ||
* @property {Rule} [include] | ||
* @property {Rule} [exclude] | ||
* @property {Parallel} [parallel] | ||
@@ -133,6 +118,30 @@ */ | ||
/** | ||
* @template T | ||
* @param fn {(function(): any) | undefined} | ||
* @returns {function(): T} | ||
*/ | ||
const memoize = fn => { | ||
let cache = false; | ||
/** @type {T} */ | ||
let result; | ||
return () => { | ||
if (cache) { | ||
return result; | ||
} | ||
result = /** @type {function(): any} */fn(); | ||
cache = true; | ||
// Allow to clean up memory for fn | ||
// and all dependent resources | ||
// eslint-disable-next-line no-undefined, no-param-reassign | ||
fn = undefined; | ||
return result; | ||
}; | ||
}; | ||
const getSerializeJavascript = memoize(() => | ||
// eslint-disable-next-line global-require | ||
require("serialize-javascript")); | ||
/** | ||
* @template [T=HtmlMinifierTerserOptions] | ||
*/ | ||
class HtmlMinimizerPlugin { | ||
@@ -143,5 +152,3 @@ /** | ||
constructor(options) { | ||
validate( | ||
/** @type {Schema} */ | ||
schema, options || {}, { | ||
validate( /** @type {Schema} */schema, options || {}, { | ||
name: "Html Minimizer Plugin", | ||
@@ -158,10 +165,8 @@ baseDataPath: "options" | ||
} = options || {}; | ||
/** @type {T extends any[] ? { [P in keyof T]: Minimizer<T[P]>; } : Minimizer<T>} */ | ||
let minimizer; | ||
if (Array.isArray(minify)) { | ||
// @ts-ignore | ||
minimizer = | ||
/** @type {MinimizerImplementation<T>[]} */ | ||
minimizer = /** @type {MinimizerImplementation<T>[]} */ | ||
minify.map( | ||
@@ -180,4 +185,3 @@ /** | ||
} else { | ||
minimizer = | ||
/** @type {T extends any[] ? { [P in keyof T]: Minimizer<T[P]>; } : Minimizer<T>} */ | ||
minimizer = /** @type {T extends any[] ? { [P in keyof T]: Minimizer<T[P]>; } : Minimizer<T>} */ | ||
{ | ||
@@ -188,2 +192,3 @@ implementation: minify, | ||
} | ||
/** | ||
@@ -193,4 +198,2 @@ * @private | ||
*/ | ||
this.options = { | ||
@@ -204,2 +207,3 @@ test, | ||
} | ||
/** | ||
@@ -211,4 +215,2 @@ * @private | ||
*/ | ||
static buildWarning(warning, file) { | ||
@@ -224,2 +226,3 @@ /** | ||
} | ||
/** | ||
@@ -231,4 +234,2 @@ * @private | ||
*/ | ||
static buildError(error, file) { | ||
@@ -239,3 +240,2 @@ /** | ||
let builtError; | ||
if (typeof error === "string") { | ||
@@ -246,3 +246,2 @@ builtError = new Error(`${file} from Html Minimizer plugin\n${error}`); | ||
} | ||
if (error.stack) { | ||
@@ -254,3 +253,2 @@ // @ts-ignore | ||
} | ||
builtError = new Error(`${file} from Html Minimizer plugin\n${error.message}`); | ||
@@ -260,2 +258,3 @@ builtError.file = file; | ||
} | ||
/** | ||
@@ -266,4 +265,2 @@ * @private | ||
*/ | ||
static getAvailableNumberOfCores(parallel) { | ||
@@ -277,2 +274,3 @@ // In some cases cpus() returns undefined | ||
} | ||
/** | ||
@@ -286,4 +284,2 @@ * @private | ||
*/ | ||
async optimize(compiler, compilation, assets, optimizeOptions) { | ||
@@ -295,15 +291,13 @@ const cache = compilation.getCache("HtmlMinimizerWebpackPlugin"); | ||
info | ||
} = | ||
/** @type {Asset} */ | ||
compilation.getAsset(name); // Skip double minimize assets from child compilation | ||
} = /** @type {Asset} */compilation.getAsset(name); | ||
// Skip double minimize assets from child compilation | ||
if (info.minimized) { | ||
return false; | ||
} | ||
if (!compiler.webpack.ModuleFilenameHelpers.matchObject.bind( // eslint-disable-next-line no-undefined | ||
if (!compiler.webpack.ModuleFilenameHelpers.matchObject.bind( | ||
// eslint-disable-next-line no-undefined | ||
undefined, this.options)(name)) { | ||
return false; | ||
} | ||
return true; | ||
@@ -314,4 +308,3 @@ }).map(async name => { | ||
source | ||
} = | ||
/** @type {Asset} */ | ||
} = /** @type {Asset} */ | ||
compilation.getAsset(name); | ||
@@ -321,7 +314,5 @@ const eTag = cache.getLazyHashedEtag(source); | ||
const output = await cacheItem.getPromise(); | ||
if (!output) { | ||
numberOfAssets += 1; | ||
} | ||
return { | ||
@@ -335,21 +326,16 @@ name, | ||
})); | ||
if (assetsForMinify.length === 0) { | ||
return; | ||
} | ||
/** @type {undefined | (() => MinimizerWorker<T>)} */ | ||
let getWorker; | ||
/** @type {undefined | MinimizerWorker<T>} */ | ||
let initializedWorker; | ||
/** @type {undefined | number} */ | ||
let numberOfWorkers; | ||
if (optimizeOptions.availableNumberOfCores > 0) { | ||
// Do not create unnecessary workers when the number of files is less than the available cores, it saves memory | ||
numberOfWorkers = Math.min(numberOfAssets, optimizeOptions.availableNumberOfCores); // eslint-disable-next-line consistent-return | ||
numberOfWorkers = Math.min(numberOfAssets, optimizeOptions.availableNumberOfCores); | ||
// eslint-disable-next-line consistent-return | ||
getWorker = () => { | ||
@@ -360,25 +346,25 @@ if (initializedWorker) { | ||
initializedWorker = | ||
/** @type {MinimizerWorker<T>} */ | ||
// eslint-disable-next-line global-require | ||
const { | ||
Worker | ||
} = require("jest-worker"); | ||
initializedWorker = /** @type {MinimizerWorker<T>} */ | ||
new Worker(require.resolve("./minify"), { | ||
numWorkers: numberOfWorkers, | ||
enableWorkerThreads: true | ||
}); // https://github.com/facebook/jest/issues/8872#issuecomment-524822081 | ||
}); | ||
// https://github.com/facebook/jest/issues/8872#issuecomment-524822081 | ||
const workerStdout = initializedWorker.getStdout(); | ||
if (workerStdout) { | ||
workerStdout.on("data", chunk => process.stdout.write(chunk)); | ||
} | ||
const workerStderr = initializedWorker.getStderr(); | ||
if (workerStderr) { | ||
workerStderr.on("data", chunk => process.stderr.write(chunk)); | ||
} | ||
return initializedWorker; | ||
}; | ||
} | ||
const { | ||
@@ -388,3 +374,2 @@ RawSource | ||
const scheduledTasks = []; | ||
for (const asset of assetsForMinify) { | ||
@@ -402,14 +387,11 @@ scheduledTasks.push(async () => { | ||
const sourceFromInputSource = inputSource.source(); | ||
if (!output) { | ||
input = sourceFromInputSource; | ||
if (Buffer.isBuffer(input)) { | ||
input = input.toString(); | ||
} | ||
/** | ||
* @type {InternalOptions<T>} | ||
*/ | ||
const options = { | ||
@@ -420,12 +402,9 @@ name, | ||
}; | ||
try { | ||
output = await (getWorker ? getWorker().transform(serialize(options)) : minifyInternal(options)); | ||
output = await (getWorker ? getWorker().transform(getSerializeJavascript()(options)) : minifyInternal(options)); | ||
} catch (error) { | ||
compilation.errors.push( | ||
/** @type {WebpackError} */ | ||
compilation.errors.push( /** @type {WebpackError} */ | ||
HtmlMinimizerPlugin.buildError(error, name)); | ||
return; | ||
} | ||
output.source = new RawSource(output.code); | ||
@@ -438,32 +417,22 @@ await cacheItem.storePromise({ | ||
} | ||
const newInfo = { | ||
minimized: true | ||
}; | ||
if (output.warnings && output.warnings.length > 0) { | ||
for (const warning of output.warnings) { | ||
compilation.warnings.push( | ||
/** @type {WebpackError} */ | ||
compilation.warnings.push( /** @type {WebpackError} */ | ||
HtmlMinimizerPlugin.buildWarning(warning, name)); | ||
} | ||
} | ||
if (output.errors && output.errors.length > 0) { | ||
for (const error of output.errors) { | ||
compilation.errors.push( | ||
/** @type {WebpackError} */ | ||
compilation.errors.push( /** @type {WebpackError} */ | ||
HtmlMinimizerPlugin.buildError(error, name)); | ||
} | ||
} | ||
compilation.updateAsset(name, output.source, newInfo); | ||
}); | ||
} | ||
const limit = getWorker && numberOfAssets > 0 ? | ||
/** @type {number} */ | ||
numberOfWorkers : scheduledTasks.length; | ||
const limit = getWorker && numberOfAssets > 0 ? /** @type {number} */numberOfWorkers : scheduledTasks.length; | ||
await throttleAll(limit, scheduledTasks); | ||
if (initializedWorker) { | ||
@@ -473,2 +442,3 @@ await initializedWorker.end(); | ||
} | ||
/** | ||
@@ -478,4 +448,2 @@ * @param {Compiler} compiler | ||
*/ | ||
apply(compiler) { | ||
@@ -496,16 +464,11 @@ const pluginName = this.constructor.name; | ||
formatFlag | ||
}) => minimized ? | ||
/** @type {Function} */ | ||
green( | ||
/** @type {Function} */ | ||
formatFlag("minimized")) : ""); | ||
}) => minimized ? /** @type {Function} */green( /** @type {Function} */formatFlag("minimized")) : ""); | ||
}); | ||
}); | ||
} | ||
} | ||
HtmlMinimizerPlugin.htmlMinifierTerser = htmlMinifierTerser; | ||
HtmlMinimizerPlugin.swcMinify = swcMinify; | ||
HtmlMinimizerPlugin.swcMinifyFragment = swcMinifyFragment; | ||
HtmlMinimizerPlugin.minifyHtmlNode = minifyHtmlNode; | ||
module.exports = HtmlMinimizerPlugin; |
"use strict"; | ||
/** @typedef {import("./index.js").MinimizedResult} MinimizedResult */ | ||
/** @typedef {import("./index.js").InternalResult} InternalResult */ | ||
@@ -20,12 +19,10 @@ | ||
const transformers = Array.isArray(options.minimizer) ? options.minimizer : [options.minimizer]; | ||
for (let i = 0; i <= transformers.length - 1; i++) { | ||
const { | ||
implementation | ||
} = transformers[i]; // eslint-disable-next-line no-await-in-loop | ||
} = transformers[i]; | ||
// eslint-disable-next-line no-await-in-loop | ||
const minifyResult = await implementation({ | ||
[options.name]: result.code | ||
}, transformers[i].options); | ||
if (Object.prototype.toString.call(minifyResult) === "[object Object]" && minifyResult !== null && "code" in minifyResult) { | ||
@@ -40,5 +37,5 @@ result.code = minifyResult.code; | ||
} | ||
return result; | ||
}; | ||
/** | ||
@@ -48,4 +45,2 @@ * @param {string} options | ||
*/ | ||
async function transform(options) { | ||
@@ -58,3 +53,2 @@ // 'use strict' => this === undefined (Clean Scope) | ||
} | ||
module.exports = { | ||
@@ -61,0 +55,0 @@ minify, |
"use strict"; | ||
/** @typedef {import("./index.js").MinimizedResult} MinimizedResult */ | ||
/** @typedef {import("./index.js").CustomOptions} CustomOptions */ | ||
/** @typedef {import("./index.js").Input} Input */ | ||
/** @typedef {import("html-minifier-terser").Options} HtmlMinifierTerserOptions */ | ||
/** @typedef {import("html-minifier-terser").Options} HtmlMinifierTerserOptions */ | ||
const notSettled = Symbol(`not-settled`); | ||
/** | ||
@@ -17,3 +16,3 @@ * @template T | ||
/** | ||
* Run tasks with limited concurency. | ||
* Run tasks with limited concurrency. | ||
* @template T | ||
@@ -24,3 +23,2 @@ * @param {number} limit - Limit of tasks that run at once. | ||
*/ | ||
function throttleAll(limit, tasks) { | ||
@@ -30,11 +28,8 @@ if (!Number.isInteger(limit) || limit < 1) { | ||
} | ||
if (!Array.isArray(tasks) || !tasks.every(task => typeof task === `function`)) { | ||
throw new TypeError(`Expected \`tasks\` to be a list of functions returning a promise`); | ||
} | ||
return new Promise((resolve, reject) => { | ||
const result = Array(tasks.length).fill(notSettled); | ||
const entries = tasks.entries(); | ||
const next = () => { | ||
@@ -45,16 +40,12 @@ const { | ||
} = entries.next(); | ||
if (done) { | ||
const isLast = !result.includes(notSettled); | ||
if (isLast) resolve( | ||
/** @type{T[]} **/ | ||
result); | ||
if (isLast) resolve( /** @type{T[]} **/result); | ||
return; | ||
} | ||
const [index, task] = value; | ||
const [index, task] = value; | ||
/** | ||
* @param {T} x | ||
*/ | ||
const onFulfilled = x => { | ||
@@ -64,9 +55,8 @@ result[index] = x; | ||
}; | ||
task().then(onFulfilled, reject); | ||
}; | ||
Array(limit).fill(0).forEach(next); | ||
}); | ||
} | ||
/** | ||
@@ -77,13 +67,8 @@ * @param {Input} input | ||
*/ | ||
/* istanbul ignore next */ | ||
async function htmlMinifierTerser(input, minimizerOptions = {}) { | ||
// eslint-disable-next-line global-require, import/no-extraneous-dependencies | ||
const htmlMinifier = require("html-minifier-terser"); | ||
const [[, code]] = Object.entries(input); | ||
/** @type {HtmlMinifierTerserOptions} */ | ||
const defaultMinimizerOptions = { | ||
@@ -104,6 +89,8 @@ caseSensitive: true, | ||
removeScriptTypeAttributes: true, | ||
removeStyleLinkTypeAttributes: true // `useShortDoctype` is not safe for XHTML | ||
removeStyleLinkTypeAttributes: true | ||
// `useShortDoctype` is not safe for XHTML | ||
}; | ||
}; | ||
const result = await htmlMinifier.minify(code, { ...defaultMinimizerOptions, | ||
const result = await htmlMinifier.minify(code, { | ||
...defaultMinimizerOptions, | ||
...minimizerOptions | ||
@@ -115,2 +102,3 @@ }); | ||
} | ||
/** | ||
@@ -121,35 +109,46 @@ * @param {Input} input | ||
*/ | ||
/* 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] | ||
* @returns {Promise<MinimizedResult>} | ||
*/ | ||
/* istanbul ignore next */ | ||
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 [[, code]] = Object.entries(input); | ||
const options = /** @type {import("@swc/html").Options} */{ | ||
...minimizerOptions | ||
}; | ||
const result = await swcMinifier.minify(Buffer.from(code), options); | ||
let errors; | ||
return { | ||
code: result.code, | ||
errors: result.errors ? result.errors.map(diagnostic => { | ||
const error = new Error(diagnostic.message); | ||
if (typeof result.errors !== "undefined") { | ||
errors = result.errors.map(diagnostic => { | ||
const error = new Error(diagnostic.message); // @ts-ignore | ||
error.span = diagnostic.span; // @ts-ignore | ||
// @ts-ignore | ||
error.span = diagnostic.span; | ||
// @ts-ignore | ||
error.level = diagnostic.level; | ||
return error; | ||
}); | ||
} | ||
return { | ||
code: result.code, | ||
errors | ||
}) : | ||
// eslint-disable-next-line no-undefined | ||
undefined | ||
}; | ||
} | ||
/** | ||
@@ -160,36 +159,26 @@ * @param {Input} input | ||
*/ | ||
/* 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); // TODO `import("@swc/html").Options` | ||
const options = | ||
/** @type {*} */ | ||
{ ...minimizerOptions | ||
const [[, code]] = Object.entries(input); | ||
const options = /** @type {import("@swc/html").FragmentOptions} */{ | ||
...minimizerOptions | ||
}; | ||
const result = await swcMinifier.minifyFragment(Buffer.from(code), options); | ||
let errors; | ||
return { | ||
code: result.code, | ||
errors: result.errors ? result.errors.map(diagnostic => { | ||
const error = new Error(diagnostic.message); | ||
if (typeof result.errors !== "undefined") { | ||
errors = result.errors.map(diagnostic => { | ||
const error = new Error(diagnostic.message); // @ts-ignore | ||
error.span = diagnostic.span; // @ts-ignore | ||
// @ts-ignore | ||
error.span = diagnostic.span; | ||
// @ts-ignore | ||
error.level = diagnostic.level; | ||
return error; | ||
}); | ||
} | ||
return { | ||
code: result.code, | ||
errors | ||
}) : | ||
// eslint-disable-next-line no-undefined | ||
undefined | ||
}; | ||
} | ||
module.exports = { | ||
@@ -199,3 +188,4 @@ throttleAll, | ||
swcMinify, | ||
swcMinifyFragment | ||
swcMinifyFragment, | ||
minifyHtmlNode | ||
}; |
{ | ||
"name": "html-minimizer-webpack-plugin", | ||
"version": "4.3.0", | ||
"version": "4.4.0", | ||
"description": "html minimizer plugin for Webpack", | ||
@@ -30,4 +30,8 @@ "license": "MIT", | ||
"lint:js": "eslint --cache .", | ||
"lint:spelling": "cspell \"**/*.*\"", | ||
"lint:types": "tsc --pretty --noEmit", | ||
"lint": "npm-run-all -l -p \"lint:**\"", | ||
"fix:js": "npm run lint:js -- --fix", | ||
"fix:prettier": "npm run lint:prettier -- --write", | ||
"fix": "npm-run-all -l fix:js fix:prettier", | ||
"test:only": "cross-env NODE_ENV=test jest", | ||
@@ -55,33 +59,36 @@ "test:watch": "npm run test:only -- --watch", | ||
"@types/html-minifier-terser": "^7.0.0", | ||
"html-minifier-terser": "^7.0.0", | ||
"jest-worker": "^29.1.2", | ||
"schema-utils": "^4.0.0", | ||
"serialize-javascript": "^6.0.0" | ||
"html-minifier-terser": "^7.2.0", | ||
"jest-worker": "^29.5.0", | ||
"schema-utils": "^4.0.1", | ||
"serialize-javascript": "^6.0.1" | ||
}, | ||
"devDependencies": { | ||
"@babel/cli": "^7.18.9", | ||
"@babel/core": "^7.18.13", | ||
"@babel/preset-env": "^7.18.9", | ||
"@commitlint/cli": "^17.1.2", | ||
"@commitlint/config-conventional": "^17.1.0", | ||
"@swc/html": "^0.0.17", | ||
"@babel/cli": "^7.21.5", | ||
"@babel/core": "^7.22.1", | ||
"@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", | ||
"@webpack-contrib/eslint-config-webpack": "^3.0.0", | ||
"babel-jest": "^29.1.2", | ||
"babel-jest": "^29.5.0", | ||
"copy-webpack-plugin": "^9.0.1", | ||
"cross-env": "^7.0.3", | ||
"cspell": "^6.31.1", | ||
"del": "^6.1.1", | ||
"del-cli": "^5.0.0", | ||
"eslint": "^8.23.0", | ||
"eslint-config-prettier": "^8.5.0", | ||
"eslint-plugin-import": "^2.26.0", | ||
"husky": "^8.0.1", | ||
"jest": "^29.1.2", | ||
"lint-staged": "^13.0.3", | ||
"memfs": "^3.4.7", | ||
"eslint": "^8.41.0", | ||
"eslint-config-prettier": "^8.8.0", | ||
"eslint-plugin-import": "^2.27.5", | ||
"husky": "^8.0.3", | ||
"jest": "^29.5.0", | ||
"lint-staged": "^13.2.2", | ||
"memfs": "^3.5.1", | ||
"npm-run-all": "^4.1.5", | ||
"prettier": "^2.7.1", | ||
"prettier": "^2.8.8", | ||
"standard-version": "^9.3.1", | ||
"typescript": "^4.7.4", | ||
"webpack": "^5.74.0" | ||
"typescript": "^5.1.3", | ||
"webpack": "^5.85.0" | ||
}, | ||
@@ -88,0 +95,0 @@ "keywords": [ |
@@ -11,3 +11,3 @@ <div align="center"> | ||
[![cover][cover]][cover-url] | ||
[![chat][chat]][chat-url] | ||
[![discussion][discussion]][discussion-url] | ||
[![size][size]][size-url] | ||
@@ -17,6 +17,7 @@ | ||
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/imagemin/imagemin) (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. | ||
@@ -61,2 +62,20 @@ ## Getting Started | ||
**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: | ||
@@ -133,4 +152,5 @@ | ||
> | ||
> - `@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) | ||
> - `@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.) | ||
> - `@minify-html/node` - please read documentation https://github.com/wilsonzlin/minify-html#whitespace | ||
@@ -315,5 +335,6 @@ ## Options | ||
- `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` | ||
@@ -519,3 +540,3 @@ > **Note** | ||
HTML Framgents: | ||
HTML Fragments: | ||
@@ -560,2 +581,45 @@ ```js | ||
### `@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. | ||
@@ -581,5 +645,5 @@ | ||
[cover-url]: https://codecov.io/gh/webpack-contrib/html-minimizer-webpack-plugin | ||
[chat]: https://img.shields.io/badge/gitter-webpack%2Fwebpack-brightgreen.svg | ||
[chat-url]: https://gitter.im/webpack/webpack | ||
[discussion]: https://img.shields.io/github/discussions/webpack/webpack | ||
[discussion-url]: https://github.com/webpack/webpack/discussions | ||
[size]: https://packagephobia.now.sh/badge?p=html-minimizer-webpack-plugin | ||
[size-url]: https://packagephobia.now.sh/result?p=html-minimizer-webpack-plugin |
export = HtmlMinimizerPlugin; | ||
/** @typedef {import("schema-utils/declarations/validate").Schema} Schema */ | ||
/** @typedef {import("webpack").Compiler} Compiler */ | ||
/** @typedef {import("webpack").Compilation} Compilation */ | ||
/** @typedef {import("webpack").WebpackError} WebpackError */ | ||
/** @typedef {import("webpack").Asset} Asset */ | ||
/** @typedef {import("jest-worker").Worker} JestWorker */ | ||
/** @typedef {import("./utils.js").HtmlMinifierTerserOptions} HtmlMinifierTerserOptions */ | ||
/** @typedef {RegExp | string} Rule */ | ||
/** @typedef {Rule[] | Rule} Rules */ | ||
/** | ||
* @typedef {Object} MinimizedResult | ||
* @property {string} code | ||
* @property {Array<unknown>} [errors] | ||
* @property {Array<unknown>} [warnings] | ||
*/ | ||
/** | ||
* @typedef {{ [file: string]: string }} Input | ||
*/ | ||
/** | ||
* @typedef {{ [key: string]: any }} CustomOptions | ||
*/ | ||
/** | ||
* @template T | ||
* @typedef {T extends infer U ? U : CustomOptions} InferDefaultType | ||
*/ | ||
/** | ||
* @template T | ||
* @typedef {InferDefaultType<T> | undefined} MinimizerOptions | ||
*/ | ||
/** | ||
* @template T | ||
* @callback MinimizerImplementation | ||
* @param {Input} input | ||
* @param {MinimizerOptions<T>} [minimizerOptions] | ||
* @returns {Promise<MinimizedResult>} | ||
*/ | ||
/** | ||
* @template T | ||
* @typedef {Object} Minimizer | ||
* @property {MinimizerImplementation<T>} implementation | ||
* @property {MinimizerOptions<T> | undefined} [options] | ||
*/ | ||
/** | ||
* @template T | ||
* @typedef {Object} InternalOptions | ||
* @property {string} name | ||
* @property {string} input | ||
* @property {T extends any[] ? { [P in keyof T]: Minimizer<T[P]>; } : Minimizer<T>} minimizer | ||
*/ | ||
/** | ||
* @typedef InternalResult | ||
* @property {string} code | ||
* @property {Array<any>} warnings | ||
* @property {Array<any>} errors | ||
*/ | ||
/** | ||
* @template T | ||
* @typedef {JestWorker & { transform: (options: string) => InternalResult, minify: (options: InternalOptions<T>) => InternalResult }} MinimizerWorker | ||
*/ | ||
/** | ||
* @typedef {undefined | boolean | number} Parallel | ||
*/ | ||
/** | ||
* @typedef {Object} BasePluginOptions | ||
* @property {Rules} [test] | ||
* @property {Rules} [include] | ||
* @property {Rules} [exclude] | ||
* @property {Parallel} [parallel] | ||
*/ | ||
/** | ||
* @template T | ||
* @typedef {BasePluginOptions & { minimizer: T extends any[] ? { [P in keyof T]: Minimizer<T[P]> } : Minimizer<T> }} InternalPluginOptions | ||
*/ | ||
/** | ||
* @template T | ||
* @typedef {T extends HtmlMinifierTerserOptions | ||
* ? { minify?: MinimizerImplementation<T> | undefined, minimizerOptions?: MinimizerOptions<T> | undefined } | ||
* : T extends any[] | ||
* ? { minify: { [P in keyof T]: MinimizerImplementation<T[P]>; }, minimizerOptions?: { [P in keyof T]?: MinimizerOptions<T[P]> | undefined; } | undefined } | ||
* : { minify: MinimizerImplementation<T>, minimizerOptions?: MinimizerOptions<T> | undefined }} DefinedDefaultMinimizerAndOptions | ||
*/ | ||
/** | ||
* @template [T=HtmlMinifierTerserOptions] | ||
@@ -140,2 +59,3 @@ */ | ||
swcMinifyFragment, | ||
minifyHtmlNode, | ||
Schema, | ||
@@ -168,5 +88,5 @@ Compiler, | ||
type BasePluginOptions = { | ||
test?: Rules | undefined; | ||
include?: Rules | undefined; | ||
exclude?: Rules | undefined; | ||
test?: Rule | undefined; | ||
include?: Rule | undefined; | ||
exclude?: Rule | undefined; | ||
parallel?: Parallel; | ||
@@ -182,9 +102,5 @@ }; | ||
? { | ||
minify: T extends infer T_1 | ||
? { [P in keyof T_1]: MinimizerImplementation<T[P]> } | ||
: never; | ||
minify: { [P in keyof T]: MinimizerImplementation<T[P]> }; | ||
minimizerOptions?: | ||
| (T extends infer T_2 | ||
? { [P_1 in keyof T_2]?: MinimizerOptions<T[P_1]> } | ||
: never) | ||
| { [P_1 in keyof T]?: MinimizerOptions<T[P_1]> } | ||
| undefined; | ||
@@ -199,2 +115,3 @@ } | ||
import { swcMinifyFragment } from "./utils"; | ||
import { minifyHtmlNode } from "./utils"; | ||
type Schema = import("schema-utils/declarations/validate").Schema; | ||
@@ -233,5 +150,3 @@ type Compilation = import("webpack").Compilation; | ||
minimizer: T extends any[] | ||
? T extends infer T_1 | ||
? { [P in keyof T_1]: Minimizer<T[P]> } | ||
: never | ||
? { [P in keyof T]: Minimizer<T[P]> } | ||
: Minimizer<T>; | ||
@@ -244,3 +159,3 @@ }; | ||
}; | ||
type MinimizerWorker<T> = Worker & { | ||
type MinimizerWorker<T> = import("jest-worker").Worker & { | ||
transform: (options: string) => InternalResult; | ||
@@ -252,7 +167,4 @@ minify: (options: InternalOptions<T>) => InternalResult; | ||
minimizer: T extends any[] | ||
? T extends infer T_1 | ||
? { [P in keyof T_1]: Minimizer<T[P]> } | ||
: never | ||
? { [P in keyof T]: Minimizer<T[P]> } | ||
: Minimizer<T>; | ||
}; | ||
import { Worker } from "jest-worker"; |
@@ -11,3 +11,3 @@ export type Task<T> = () => Promise<T>; | ||
/** | ||
* Run tasks with limited concurency. | ||
* Run tasks with limited concurrency. | ||
* @template T | ||
@@ -46,1 +46,10 @@ * @param {number} limit - Limit of tasks that run at once. | ||
): Promise<MinimizedResult>; | ||
/** | ||
* @param {Input} input | ||
* @param {CustomOptions | undefined} [minimizerOptions] | ||
* @returns {Promise<MinimizedResult>} | ||
*/ | ||
export function minifyHtmlNode( | ||
input: Input, | ||
minimizerOptions?: CustomOptions | undefined | ||
): Promise<MinimizedResult>; |
49355
642
28
976
Updatedhtml-minifier-terser@^7.2.0
Updatedjest-worker@^29.5.0
Updatedschema-utils@^4.0.1
Updatedserialize-javascript@^6.0.1