html-minifier-next 4.15.1 → 4.15.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -30
- package/dist/htmlminifier.cjs +34 -0
- package/dist/htmlminifier.esm.bundle.js +34 -0
- package/dist/types/lib/options.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/lib/options.js +34 -0
package/README.md
CHANGED
|
@@ -352,42 +352,48 @@ Available options:
|
|
|
352
352
|
|
|
353
353
|
## Minification comparison
|
|
354
354
|
|
|
355
|
-
How does HTML Minifier Next compare to other minifiers? (All minification with the most aggressive settings—though without [hyper-optimization](https://meiert.com/blog/the-ways-of-writing-html/#toc-hyper-optimized)—and against a wide range of pages.
|
|
355
|
+
How does HTML Minifier Next compare to other minifiers? (All minification with the most aggressive settings—though without [hyper-optimization](https://meiert.com/blog/the-ways-of-writing-html/#toc-hyper-optimized)—and against a wide range of pages.)
|
|
356
356
|
|
|
357
357
|
<!-- Auto-generated benchmarks, don’t edit -->
|
|
358
|
-
| Site | Original Size (KB) | [HTML Minifier Next](https://github.com/j9t/html-minifier-next) ([config](https://github.com/j9t/html-minifier-next/blob/main/benchmarks/html-minifier.json))<br>[](https://socket.dev/npm/package/html-minifier-next) | [
|
|
359
|
-
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
360
|
-
| [A List Apart](https://alistapart.com/) | 59 | **50** |
|
|
361
|
-
| [Apple](https://www.apple.com/) | 266 | **206** |
|
|
362
|
-
| [BBC](https://www.bbc.co.uk/) |
|
|
363
|
-
| [CERN](https://home.cern/) | 152 | **83** |
|
|
364
|
-
| [CSS-Tricks](https://css-tricks.com/) | 162 | **119** |
|
|
365
|
-
| [ECMAScript](https://tc39.es/ecma262/) | 7250 | 6401
|
|
366
|
-
| [EDRi](https://edri.org/) | 80 | **59** |
|
|
367
|
-
| [EFF](https://www.eff.org/) | 55 | **46** |
|
|
368
|
-
| [European Alternatives](https://european-alternatives.eu/) | 48 | **30** |
|
|
369
|
-
| [FAZ](https://www.faz.net/aktuell/) |
|
|
370
|
-
| [French Tech](https://lafrenchtech.gouv.fr/) |
|
|
371
|
-
| [Frontend Dogma](https://frontenddogma.com/) | 225 | **217** |
|
|
372
|
-
| [Google](https://www.google.com/) |
|
|
373
|
-
| [Ground News](https://ground.news/) |
|
|
374
|
-
| [HTML Living Standard](https://html.spec.whatwg.org/multipage/) | 149 | 148 |
|
|
375
|
-
| [Igalia](https://www.igalia.com/) | 50 | **33** |
|
|
376
|
-
| [Leanpub](https://leanpub.com/) |
|
|
377
|
-
| [Mastodon](https://mastodon.social/explore) | 37 | **28** |
|
|
378
|
-
| [MDN](https://developer.mozilla.org/en-US/) | 109 | **62** |
|
|
379
|
-
| [Middle East Eye](https://www.middleeasteye.net/) | 223 | **196** |
|
|
380
|
-
| [
|
|
381
|
-
| [
|
|
382
|
-
| [
|
|
383
|
-
| [
|
|
384
|
-
| [
|
|
385
|
-
| [
|
|
386
|
-
|
|
|
358
|
+
| Site | Original Size (KB) | [HTML Minifier Next](https://github.com/j9t/html-minifier-next) ([config](https://github.com/j9t/html-minifier-next/blob/main/benchmarks/html-minifier.json))<br>[](https://socket.dev/npm/package/html-minifier-next) | [htmlnano](https://github.com/posthtml/htmlnano)<br>[](https://socket.dev/npm/package/htmlnano) | [@swc/html](https://github.com/swc-project/swc)<br>[](https://socket.dev/npm/package/@swc/html) | [minify-html](https://github.com/wilsonzlin/minify-html)<br>[](https://socket.dev/npm/package/@minify-html/node) | [minimize](https://github.com/Swaagie/minimize)<br>[](https://socket.dev/npm/package/minimize) | [htmlcompressor.com](https://htmlcompressor.com/) |
|
|
359
|
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
360
|
+
| [A List Apart](https://alistapart.com/) | 59 | **50** | 51 | 52 | 51 | 54 | 52 |
|
|
361
|
+
| [Apple](https://www.apple.com/) | 266 | **206** | 236 | 239 | 240 | 242 | 243 |
|
|
362
|
+
| [BBC](https://www.bbc.co.uk/) | 643 | **584** | 603 | 604 | 605 | 638 | n/a |
|
|
363
|
+
| [CERN](https://home.cern/) | 152 | **83** | 91 | 91 | 91 | 93 | 96 |
|
|
364
|
+
| [CSS-Tricks](https://css-tricks.com/) | 162 | **119** | 127 | 143 | 143 | 148 | 144 |
|
|
365
|
+
| [ECMAScript](https://tc39.es/ecma262/) | 7250 | **6401** | 6573 | 6455 | 6578 | 6626 | n/a |
|
|
366
|
+
| [EDRi](https://edri.org/) | 80 | **59** | 70 | 70 | 71 | 75 | 73 |
|
|
367
|
+
| [EFF](https://www.eff.org/) | 55 | **46** | 49 | 48 | 49 | 50 | 50 |
|
|
368
|
+
| [European Alternatives](https://european-alternatives.eu/) | 48 | **30** | 32 | 32 | 32 | 32 | 32 |
|
|
369
|
+
| [FAZ](https://www.faz.net/aktuell/) | 1564 | 1457 | **1404** | 1489 | 1500 | 1511 | n/a |
|
|
370
|
+
| [French Tech](https://lafrenchtech.gouv.fr/) | 153 | **122** | 126 | 126 | 126 | 132 | 127 |
|
|
371
|
+
| [Frontend Dogma](https://frontenddogma.com/) | 225 | **217** | 238 | 223 | 225 | 243 | 224 |
|
|
372
|
+
| [Google](https://www.google.com/) | 76 | **71** | n/a | 72 | 73 | 75 | 75 |
|
|
373
|
+
| [Ground News](https://ground.news/) | 2373 | **2089** | 2185 | 2211 | 2213 | 2360 | n/a |
|
|
374
|
+
| [HTML Living Standard](https://html.spec.whatwg.org/multipage/) | 149 | 148 | 153 | **147** | 149 | 155 | 149 |
|
|
375
|
+
| [Igalia](https://www.igalia.com/) | 50 | **33** | 36 | 36 | 36 | 37 | 36 |
|
|
376
|
+
| [Leanpub](https://leanpub.com/) | 229 | **199** | 214 | 213 | 214 | 224 | 226 |
|
|
377
|
+
| [Mastodon](https://mastodon.social/explore) | 37 | **28** | 32 | 35 | 35 | 36 | 36 |
|
|
378
|
+
| [MDN](https://developer.mozilla.org/en-US/) | 109 | **62** | 64 | 65 | 65 | 68 | 68 |
|
|
379
|
+
| [Middle East Eye](https://www.middleeasteye.net/) | 223 | **196** | 203 | 201 | 200 | 202 | 203 |
|
|
380
|
+
| [Mistral AI](https://mistral.ai/) | 360 | **319** | 323 | 326 | 327 | 357 | n/a |
|
|
381
|
+
| [Mozilla](https://www.mozilla.org/) | 45 | **31** | 34 | 34 | 34 | 35 | 35 |
|
|
382
|
+
| [Nielsen Norman Group](https://www.nngroup.com/) | 86 | 68 | **55** | 74 | 75 | 77 | 76 |
|
|
383
|
+
| [SitePoint](https://www.sitepoint.com/) | 491 | **360** | 431 | 465 | 470 | 488 | n/a |
|
|
384
|
+
| [Startup-Verband](https://startupverband.de/) | 42 | **29** | 30 | 30 | 30 | 31 | 30 |
|
|
385
|
+
| [TetraLogical](https://tetralogical.com/) | 44 | 39 | **35** | 38 | 39 | 39 | 39 |
|
|
386
|
+
| [TPGi](https://www.tpgi.com/) | 175 | **159** | 160 | 164 | 166 | 172 | 172 |
|
|
387
|
+
| [United Nations](https://www.un.org/en/) | 151 | **112** | 121 | 125 | 125 | 130 | 123 |
|
|
388
|
+
| [Vivaldi](https://vivaldi.com/) | 92 | **74** | n/a | 79 | 81 | 83 | 81 |
|
|
389
|
+
| [W3C](https://www.w3.org/) | 50 | **36** | 39 | 38 | 38 | 41 | 39 |
|
|
390
|
+
| **Average processing time** | | 121 ms (30/30) | 648 ms (28/30) | 50 ms (30/30) | **14 ms (30/30)** | 275 ms (30/30) | 1409 ms (24/30) |
|
|
387
391
|
|
|
388
392
|
(Last updated: Dec 23, 2025)
|
|
389
393
|
<!-- End auto-generated -->
|
|
390
394
|
|
|
395
|
+
Notes: htmlnano runs in an isolated process for crash protection, adding ~50–100ms overhead per test. Minimize does not minify CSS and JS. [HTML Minifier Terser](https://github.com/terser/html-minifier-terser) is currently not included due to issues around whitespace collapsing and removal of code using modern CSS features, issues which appeared to distort the data.
|
|
396
|
+
|
|
391
397
|
## Examples
|
|
392
398
|
|
|
393
399
|
### CLI
|
package/dist/htmlminifier.cjs
CHANGED
|
@@ -1581,6 +1581,31 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, cssM
|
|
|
1581
1581
|
minifySVG: null
|
|
1582
1582
|
};
|
|
1583
1583
|
|
|
1584
|
+
// Helper to convert string patterns to RegExp (for JSON config support)
|
|
1585
|
+
const parseRegExp = (value) => {
|
|
1586
|
+
if (typeof value === 'string') {
|
|
1587
|
+
return new RegExp(value.replace(/^\/(.*)\/$/, '$1'));
|
|
1588
|
+
}
|
|
1589
|
+
return value; // Already a RegExp or other type
|
|
1590
|
+
};
|
|
1591
|
+
|
|
1592
|
+
const parseRegExpArray = (arr) => {
|
|
1593
|
+
return Array.isArray(arr) ? arr.map(parseRegExp) : arr;
|
|
1594
|
+
};
|
|
1595
|
+
|
|
1596
|
+
// Helper for nested arrays (e.g., `customAttrSurround: [[start, end], …]`)
|
|
1597
|
+
const parseNestedRegExpArray = (arr) => {
|
|
1598
|
+
if (!Array.isArray(arr)) return arr;
|
|
1599
|
+
return arr.map(item => {
|
|
1600
|
+
// If item is an array (a pair), recursively convert each element
|
|
1601
|
+
if (Array.isArray(item)) {
|
|
1602
|
+
return item.map(parseRegExp);
|
|
1603
|
+
}
|
|
1604
|
+
// Otherwise, convert single item
|
|
1605
|
+
return parseRegExp(item);
|
|
1606
|
+
});
|
|
1607
|
+
};
|
|
1608
|
+
|
|
1584
1609
|
Object.keys(inputOptions).forEach(function (key) {
|
|
1585
1610
|
const option = inputOptions[key];
|
|
1586
1611
|
|
|
@@ -1816,6 +1841,15 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, cssM
|
|
|
1816
1841
|
// Unlike minifyCSS/minifyJS, this is a simple options object, not a function
|
|
1817
1842
|
// The actual minification is applied inline during attribute processing
|
|
1818
1843
|
options.minifySVG = getSVGMinifierOptions(option);
|
|
1844
|
+
} else if (key === 'customAttrCollapse') {
|
|
1845
|
+
// Single RegExp pattern
|
|
1846
|
+
options[key] = parseRegExp(option);
|
|
1847
|
+
} else if (key === 'customAttrSurround') {
|
|
1848
|
+
// Nested array of RegExp pairs: `[[openRegExp, closeRegExp], …]`
|
|
1849
|
+
options[key] = parseNestedRegExpArray(option);
|
|
1850
|
+
} else if (['customAttrAssign', 'customEventAttributes', 'ignoreCustomComments', 'ignoreCustomFragments'].includes(key)) {
|
|
1851
|
+
// Array of RegExp patterns
|
|
1852
|
+
options[key] = parseRegExpArray(option);
|
|
1819
1853
|
} else {
|
|
1820
1854
|
options[key] = option;
|
|
1821
1855
|
}
|
|
@@ -6723,6 +6723,31 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, cssM
|
|
|
6723
6723
|
minifySVG: null
|
|
6724
6724
|
};
|
|
6725
6725
|
|
|
6726
|
+
// Helper to convert string patterns to RegExp (for JSON config support)
|
|
6727
|
+
const parseRegExp = (value) => {
|
|
6728
|
+
if (typeof value === 'string') {
|
|
6729
|
+
return new RegExp(value.replace(/^\/(.*)\/$/, '$1'));
|
|
6730
|
+
}
|
|
6731
|
+
return value; // Already a RegExp or other type
|
|
6732
|
+
};
|
|
6733
|
+
|
|
6734
|
+
const parseRegExpArray = (arr) => {
|
|
6735
|
+
return Array.isArray(arr) ? arr.map(parseRegExp) : arr;
|
|
6736
|
+
};
|
|
6737
|
+
|
|
6738
|
+
// Helper for nested arrays (e.g., `customAttrSurround: [[start, end], …]`)
|
|
6739
|
+
const parseNestedRegExpArray = (arr) => {
|
|
6740
|
+
if (!Array.isArray(arr)) return arr;
|
|
6741
|
+
return arr.map(item => {
|
|
6742
|
+
// If item is an array (a pair), recursively convert each element
|
|
6743
|
+
if (Array.isArray(item)) {
|
|
6744
|
+
return item.map(parseRegExp);
|
|
6745
|
+
}
|
|
6746
|
+
// Otherwise, convert single item
|
|
6747
|
+
return parseRegExp(item);
|
|
6748
|
+
});
|
|
6749
|
+
};
|
|
6750
|
+
|
|
6726
6751
|
Object.keys(inputOptions).forEach(function (key) {
|
|
6727
6752
|
const option = inputOptions[key];
|
|
6728
6753
|
|
|
@@ -6958,6 +6983,15 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, cssM
|
|
|
6958
6983
|
// Unlike minifyCSS/minifyJS, this is a simple options object, not a function
|
|
6959
6984
|
// The actual minification is applied inline during attribute processing
|
|
6960
6985
|
options.minifySVG = getSVGMinifierOptions(option);
|
|
6986
|
+
} else if (key === 'customAttrCollapse') {
|
|
6987
|
+
// Single RegExp pattern
|
|
6988
|
+
options[key] = parseRegExp(option);
|
|
6989
|
+
} else if (key === 'customAttrSurround') {
|
|
6990
|
+
// Nested array of RegExp pairs: `[[openRegExp, closeRegExp], …]`
|
|
6991
|
+
options[key] = parseNestedRegExpArray(option);
|
|
6992
|
+
} else if (['customAttrAssign', 'customEventAttributes', 'ignoreCustomComments', 'ignoreCustomFragments'].includes(key)) {
|
|
6993
|
+
// Array of RegExp patterns
|
|
6994
|
+
options[key] = parseRegExpArray(option);
|
|
6961
6995
|
} else {
|
|
6962
6996
|
options[key] = option;
|
|
6963
6997
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../../src/lib/options.js"],"names":[],"mappings":"AAWA,6DAUC;AAID;;;;;;;;;GASG;AACH,6CATW,OAAO,CAAC,eAAe,CAAC,0EAEhC;IAAuB,eAAe;IACf,SAAS;IACT,MAAM;CAA2B,GAG9C,eAAe,
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../../src/lib/options.js"],"names":[],"mappings":"AAWA,6DAUC;AAID;;;;;;;;;GASG;AACH,6CATW,OAAO,CAAC,eAAe,CAAC,0EAEhC;IAAuB,eAAe;IACf,SAAS;IACT,MAAM;CAA2B,GAG9C,eAAe,CA6S3B"}
|
package/package.json
CHANGED
package/src/lib/options.js
CHANGED
|
@@ -58,6 +58,31 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, cssM
|
|
|
58
58
|
minifySVG: null
|
|
59
59
|
};
|
|
60
60
|
|
|
61
|
+
// Helper to convert string patterns to RegExp (for JSON config support)
|
|
62
|
+
const parseRegExp = (value) => {
|
|
63
|
+
if (typeof value === 'string') {
|
|
64
|
+
return new RegExp(value.replace(/^\/(.*)\/$/, '$1'));
|
|
65
|
+
}
|
|
66
|
+
return value; // Already a RegExp or other type
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const parseRegExpArray = (arr) => {
|
|
70
|
+
return Array.isArray(arr) ? arr.map(parseRegExp) : arr;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Helper for nested arrays (e.g., `customAttrSurround: [[start, end], …]`)
|
|
74
|
+
const parseNestedRegExpArray = (arr) => {
|
|
75
|
+
if (!Array.isArray(arr)) return arr;
|
|
76
|
+
return arr.map(item => {
|
|
77
|
+
// If item is an array (a pair), recursively convert each element
|
|
78
|
+
if (Array.isArray(item)) {
|
|
79
|
+
return item.map(parseRegExp);
|
|
80
|
+
}
|
|
81
|
+
// Otherwise, convert single item
|
|
82
|
+
return parseRegExp(item);
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
|
|
61
86
|
Object.keys(inputOptions).forEach(function (key) {
|
|
62
87
|
const option = inputOptions[key];
|
|
63
88
|
|
|
@@ -293,6 +318,15 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, cssM
|
|
|
293
318
|
// Unlike minifyCSS/minifyJS, this is a simple options object, not a function
|
|
294
319
|
// The actual minification is applied inline during attribute processing
|
|
295
320
|
options.minifySVG = getSVGMinifierOptions(option);
|
|
321
|
+
} else if (key === 'customAttrCollapse') {
|
|
322
|
+
// Single RegExp pattern
|
|
323
|
+
options[key] = parseRegExp(option);
|
|
324
|
+
} else if (key === 'customAttrSurround') {
|
|
325
|
+
// Nested array of RegExp pairs: `[[openRegExp, closeRegExp], …]`
|
|
326
|
+
options[key] = parseNestedRegExpArray(option);
|
|
327
|
+
} else if (['customAttrAssign', 'customEventAttributes', 'ignoreCustomComments', 'ignoreCustomFragments'].includes(key)) {
|
|
328
|
+
// Array of RegExp patterns
|
|
329
|
+
options[key] = parseRegExpArray(option);
|
|
296
330
|
} else {
|
|
297
331
|
options[key] = option;
|
|
298
332
|
}
|