html-minifier-next 4.14.1 → 4.14.3

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 CHANGED
@@ -14,6 +14,12 @@ From npm for use as a command line app:
14
14
  npm i -g html-minifier-next
15
15
  ```
16
16
 
17
+ Or use directly with npx (no installation required):
18
+
19
+ ```shell
20
+ npx html-minifier-next --help
21
+ ```
22
+
17
23
  From npm for programmatic use:
18
24
 
19
25
  ```shell
@@ -67,7 +73,7 @@ module.exports = {
67
73
 
68
74
  **Using a configuration file:**
69
75
 
70
- ```bash
76
+ ```shell
71
77
  # Specify config file
72
78
  html-minifier-next --config-file=html-minifier.json --input-dir=src --output-dir=dist
73
79
 
@@ -116,7 +122,7 @@ To review the specific options set, [presets.js](https://github.com/j9t/html-min
116
122
 
117
123
  **Using presets:**
118
124
 
119
- ```bash
125
+ ```shell
120
126
  # Via CLI flag
121
127
  html-minifier-next --preset conservative input.html
122
128
 
@@ -237,7 +243,7 @@ You can choose between different JS minifiers using the `engine` field:
237
243
  ```js
238
244
  const result = await minify(html, {
239
245
  minifyJS: {
240
- engine: 'swc', // Use swc for faster minification
246
+ engine: 'swc', // Use SWC for faster minification
241
247
  // SWC-specific options here
242
248
  }
243
249
  });
@@ -248,12 +254,14 @@ const result = await minify(html, {
248
254
  * `terser` (default): The standard JavaScript minifier with excellent compression
249
255
  * [`swc`](https://swc.rs/): Rust-based minifier that’s significantly faster than Terser (requires separate installation)
250
256
 
251
- **To use swc**, install it as a dependency:
257
+ **To use SWC**, install it as a dependency:
252
258
 
253
- ```bash
259
+ ```shell
254
260
  npm i @swc/core
255
261
  ```
256
262
 
263
+ (Build-only users may want to install it as a dev dependency: `npm i -D @swc/core`.)
264
+
257
265
  **Important:** Inline event handlers (e.g., `onclick="return false"`) always use Terser regardless of the `engine` setting, as SWC doesn’t support bare return statements. This is handled automatically—you don’t need to do anything special.
258
266
 
259
267
  You can pass engine-specific configuration options:
@@ -295,33 +303,33 @@ How does HTML Minifier Next compare to other minifiers? (All with the most aggre
295
303
  <!-- Auto-generated benchmarks, don’t edit -->
296
304
  | 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>[![npm last update](https://img.shields.io/npm/last-update/html-minifier-next)](https://socket.dev/npm/package/html-minifier-next) | [HTML Minifier Terser](https://github.com/terser/html-minifier-terser)<br>[![npm last update](https://img.shields.io/npm/last-update/html-minifier-terser)](https://socket.dev/npm/package/html-minifier-terser) | [htmlnano](https://github.com/posthtml/htmlnano)<br>[![npm last update](https://img.shields.io/npm/last-update/htmlnano)](https://socket.dev/npm/package/htmlnano) | [@swc/html](https://github.com/swc-project/swc)<br>[![npm last update](https://img.shields.io/npm/last-update/@swc/html)](https://socket.dev/npm/package/@swc/html) | [minify-html](https://github.com/wilsonzlin/minify-html)<br>[![npm last update](https://img.shields.io/npm/last-update/@minify-html/node)](https://socket.dev/npm/package/@minify-html/node) | [minimize](https://github.com/Swaagie/minimize)<br>[![npm last update](https://img.shields.io/npm/last-update/minimize)](https://socket.dev/npm/package/minimize) | [html­com­pressor.­com](https://htmlcompressor.com/) |
297
305
  | --- | --- | --- | --- | --- | --- | --- | --- | --- |
298
- | [A List Apart](https://alistapart.com/) | 59 | **49** | 50 | 51 | 52 | 51 | 54 | 52 |
306
+ | [A List Apart](https://alistapart.com/) | 59 | **50** | **50** | 51 | 52 | 51 | 54 | 52 |
299
307
  | [Apple](https://www.apple.com/) | 266 | **207** | **207** | 236 | 239 | 240 | 242 | 243 |
300
- | [BBC](https://www.bbc.co.uk/) | 730 | **663** | 673 | 685 | 686 | 687 | 724 | n/a |
308
+ | [BBC](https://www.bbc.co.uk/) | 727 | **661** | 671 | 683 | 683 | 685 | 721 | n/a |
301
309
  | [CERN](https://home.cern/) | 152 | **83** | 84 | 91 | 91 | 91 | 93 | 96 |
302
- | [CSS-Tricks](https://css-tricks.com/) | 162 | 121 | **120** | 127 | 143 | 143 | 148 | 145 |
303
- | [ECMAScript](https://tc39.es/ecma262/) | 7250 | **6352** | **6352** | 6573 | 6455 | 6578 | 6626 | n/a |
310
+ | [CSS-Tricks](https://css-tricks.com/) | 162 | 122 | **120** | 127 | 143 | 143 | 148 | 145 |
311
+ | [ECMAScript](https://tc39.es/ecma262/) | 7250 | **6354** | **6354** | 6573 | 6455 | 6578 | 6626 | n/a |
304
312
  | [EDRi](https://edri.org/) | 80 | **59** | 60 | 70 | 70 | 71 | 75 | 73 |
305
- | [EFF](https://www.eff.org/) | 55 | **45** | 46 | 49 | 48 | 48 | 50 | 50 |
313
+ | [EFF](https://www.eff.org/) | 55 | **45** | 47 | 49 | 48 | 48 | 50 | 50 |
306
314
  | [European Alternatives](https://european-alternatives.eu/) | 48 | **30** | **30** | 32 | 32 | 32 | 32 | 32 |
307
- | [FAZ](https://www.faz.net/aktuell/) | 1562 | 1455 | 1460 | **1401** | 1487 | 1498 | 1509 | n/a |
315
+ | [FAZ](https://www.faz.net/aktuell/) | 1591 | 1486 | 1491 | **1427** | 1515 | 1526 | 1538 | n/a |
308
316
  | [French Tech](https://lafrenchtech.gouv.fr/) | 152 | **122** | **122** | 126 | 125 | 125 | 132 | 127 |
309
317
  | [Frontend Dogma](https://frontenddogma.com/) | 224 | **214** | 216 | 237 | 222 | 224 | 243 | 224 |
310
318
  | [Google](https://www.google.com/) | 18 | **16** | 17 | 17 | 17 | 17 | 18 | 18 |
311
- | [Ground News](https://ground.news/) | 2344 | **2062** | 2064 | 2159 | 2184 | 2188 | 2331 | n/a |
319
+ | [Ground News](https://ground.news/) | 2358 | **2076** | 2077 | 2173 | 2198 | 2202 | 2345 | n/a |
312
320
  | [HTML Living Standard](https://html.spec.whatwg.org/multipage/) | 149 | **147** | **147** | 153 | **147** | 149 | 155 | 149 |
313
321
  | [Igalia](https://www.igalia.com/) | 50 | **34** | **34** | 36 | 36 | 36 | 37 | 37 |
314
- | [Leanpub](https://leanpub.com/) | 222 | 192 | **190** | 207 | 207 | 207 | 217 | 219 |
322
+ | [Leanpub](https://leanpub.com/) | 222 | 192 | **191** | 207 | 207 | 207 | 217 | 219 |
315
323
  | [Mastodon](https://mastodon.social/explore) | 37 | **28** | **28** | 32 | 35 | 35 | 36 | 36 |
316
324
  | [MDN](https://developer.mozilla.org/en-US/) | 109 | **62** | **62** | 64 | 65 | 65 | 68 | 68 |
317
- | [Middle East Eye](https://www.middleeasteye.net/) | 222 | **195** | **195** | 202 | 200 | 200 | 202 | 202 |
325
+ | [Middle East Eye](https://www.middleeasteye.net/) | 222 | **196** | **196** | 202 | 200 | 200 | 202 | 203 |
318
326
  | [Nielsen Norman Group](https://www.nngroup.com/) | 86 | 74 | 74 | **55** | 74 | 75 | 77 | 76 |
319
- | [SitePoint](https://www.sitepoint.com/) | 501 | **370** | **370** | 442 | 475 | 480 | 498 | n/a |
327
+ | [SitePoint](https://www.sitepoint.com/) | 491 | **360** | **360** | 431 | 465 | 470 | 488 | n/a |
320
328
  | [TetraLogical](https://tetralogical.com/) | 44 | 38 | 38 | **35** | 38 | 39 | 39 | 39 |
321
329
  | [TPGi](https://www.tpgi.com/) | 175 | **159** | 161 | 160 | 164 | 166 | 172 | 172 |
322
330
  | [United Nations](https://www.un.org/en/) | 152 | **113** | 114 | 121 | 125 | 125 | 131 | 124 |
323
331
  | [W3C](https://www.w3.org/) | 50 | **36** | **36** | 39 | 38 | 38 | 41 | 39 |
324
- | **Average processing time** | | 264 ms (26/26) | 365 ms (26/26) | 162 ms (26/26) | 56 ms (26/26) | **16 ms (26/26)** | 323 ms (26/26) | 1373 ms (21/26) |
332
+ | **Average processing time** | | 259 ms (26/26) | 375 ms (26/26) | 164 ms (26/26) | 54 ms (26/26) | **16 ms (26/26)** | 327 ms (26/26) | 1497 ms (21/26) |
325
333
 
326
334
  (Last updated: Dec 21, 2025)
327
335
  <!-- End auto-generated -->
@@ -332,13 +340,19 @@ How does HTML Minifier Next compare to other minifiers? (All with the most aggre
332
340
 
333
341
  **Sample command line:**
334
342
 
335
- ```bash
343
+ ```shell
336
344
  html-minifier-next --collapse-whitespace --remove-comments --minify-js --input-dir=. --output-dir=example
337
345
  ```
338
346
 
347
+ Another example, using npx:
348
+
349
+ ```shell
350
+ npx html-minifier-next --input-dir=test --file-ext html --preset comprehensive --output-dir example
351
+ ```
352
+
339
353
  **Process specific files and directories:**
340
354
 
341
- ```bash
355
+ ```shell
342
356
  # Process only HTML files
343
357
  html-minifier-next --collapse-whitespace --input-dir=src --output-dir=dist --file-ext=html
344
358
 
@@ -356,7 +370,7 @@ html-minifier-next --collapse-whitespace --input-dir=src --output-dir=dist
356
370
 
357
371
  **Exclude directories from processing:**
358
372
 
359
- ```bash
373
+ ```shell
360
374
  # Ignore a single directory
361
375
  html-minifier-next --collapse-whitespace --input-dir=src --output-dir=dist --ignore-dir=libs
362
376
 
@@ -369,7 +383,7 @@ html-minifier-next --collapse-whitespace --input-dir=src --output-dir=dist --ign
369
383
 
370
384
  **Dry run mode (preview outcome without writing files):**
371
385
 
372
- ```bash
386
+ ```shell
373
387
  # Preview with output file
374
388
  html-minifier-next input.html -o output.html --dry --collapse-whitespace
375
389
 
@@ -384,7 +398,7 @@ html-minifier-next --input-dir=src --output-dir=dist --dry --collapse-whitespace
384
398
 
385
399
  **Verbose mode (show detailed processing information):**
386
400
 
387
- ```bash
401
+ ```shell
388
402
  # Show processing details while minifying
389
403
  html-minifier-next --input-dir=src --output-dir=dist --verbose --collapse-whitespace
390
404
  # Output: Options: collapseWhitespace, html5, includeAutoGeneratedTags
@@ -1135,10 +1135,24 @@ function collapseWhitespaceSmart(str, prevTag, nextTag, options, inlineElements,
1135
1135
  if (trimLeft && !options.collapseInlineTagWhitespace) {
1136
1136
  trimLeft = prevTag.charAt(0) === '/' ? !inlineElements.has(prevTag.slice(1)) : !inlineTextSet.has(prevTag);
1137
1137
  }
1138
+ // When `collapseInlineTagWhitespace` is enabled, still preserve whitespace around inline text elements
1139
+ if (trimLeft && options.collapseInlineTagWhitespace) {
1140
+ const tagName = prevTag.charAt(0) === '/' ? prevTag.slice(1) : prevTag;
1141
+ if (inlineElementsToKeepWhitespaceWithin.has(tagName)) {
1142
+ trimLeft = false;
1143
+ }
1144
+ }
1138
1145
  let trimRight = nextTag && !inlineElementsToKeepWhitespace.has(nextTag);
1139
1146
  if (trimRight && !options.collapseInlineTagWhitespace) {
1140
1147
  trimRight = nextTag.charAt(0) === '/' ? !inlineTextSet.has(nextTag.slice(1)) : !inlineElements.has(nextTag);
1141
1148
  }
1149
+ // When `collapseInlineTagWhitespace` is enabled, still preserve whitespace around inline text elements
1150
+ if (trimRight && options.collapseInlineTagWhitespace) {
1151
+ const tagName = nextTag.charAt(0) === '/' ? nextTag.slice(1) : nextTag;
1152
+ if (inlineElementsToKeepWhitespaceWithin.has(tagName)) {
1153
+ trimRight = false;
1154
+ }
1155
+ }
1142
1156
  return collapseWhitespace(str, options, trimLeft, trimRight, prevTag && nextTag);
1143
1157
  }
1144
1158
 
@@ -3747,10 +3747,24 @@ function collapseWhitespaceSmart(str, prevTag, nextTag, options, inlineElements,
3747
3747
  if (trimLeft && !options.collapseInlineTagWhitespace) {
3748
3748
  trimLeft = prevTag.charAt(0) === '/' ? !inlineElements.has(prevTag.slice(1)) : !inlineTextSet.has(prevTag);
3749
3749
  }
3750
+ // When `collapseInlineTagWhitespace` is enabled, still preserve whitespace around inline text elements
3751
+ if (trimLeft && options.collapseInlineTagWhitespace) {
3752
+ const tagName = prevTag.charAt(0) === '/' ? prevTag.slice(1) : prevTag;
3753
+ if (inlineElementsToKeepWhitespaceWithin.has(tagName)) {
3754
+ trimLeft = false;
3755
+ }
3756
+ }
3750
3757
  let trimRight = nextTag && !inlineElementsToKeepWhitespace.has(nextTag);
3751
3758
  if (trimRight && !options.collapseInlineTagWhitespace) {
3752
3759
  trimRight = nextTag.charAt(0) === '/' ? !inlineTextSet.has(nextTag.slice(1)) : !inlineElements.has(nextTag);
3753
3760
  }
3761
+ // When `collapseInlineTagWhitespace` is enabled, still preserve whitespace around inline text elements
3762
+ if (trimRight && options.collapseInlineTagWhitespace) {
3763
+ const tagName = nextTag.charAt(0) === '/' ? nextTag.slice(1) : nextTag;
3764
+ if (inlineElementsToKeepWhitespaceWithin.has(tagName)) {
3765
+ trimRight = false;
3766
+ }
3767
+ }
3754
3768
  return collapseWhitespace(str, options, trimLeft, trimRight, prevTag && nextTag);
3755
3769
  }
3756
3770
 
@@ -1 +1 @@
1
- {"version":3,"file":"whitespace.d.ts","sourceRoot":"","sources":["../../../src/lib/whitespace.js"],"names":[],"mappings":"AAeA,8CAOC;AAID,qDAgBC;AAID,iHAyDC;AAID,0IAUC;AAID,yDAEC;AAED,qDAEC"}
1
+ {"version":3,"file":"whitespace.d.ts","sourceRoot":"","sources":["../../../src/lib/whitespace.js"],"names":[],"mappings":"AAgBA,8CAOC;AAID,qDAgBC;AAID,iHAyDC;AAID,0IAwBC;AAID,yDAEC;AAED,qDAEC"}
package/package.json CHANGED
@@ -93,5 +93,5 @@
93
93
  "test:watch": "node --test --watch tests/*.spec.js"
94
94
  },
95
95
  "type": "module",
96
- "version": "4.14.1"
96
+ "version": "4.14.3"
97
97
  }
@@ -8,7 +8,8 @@ import {
8
8
  RE_NBSP_LEAD_GROUP,
9
9
  RE_NBSP_TRAILING_GROUP,
10
10
  RE_NBSP_TRAILING_STRIP,
11
- inlineElementsToKeepWhitespace
11
+ inlineElementsToKeepWhitespace,
12
+ inlineElementsToKeepWhitespaceWithin
12
13
  } from './constants.js';
13
14
 
14
15
  // Trim whitespace
@@ -110,10 +111,24 @@ function collapseWhitespaceSmart(str, prevTag, nextTag, options, inlineElements,
110
111
  if (trimLeft && !options.collapseInlineTagWhitespace) {
111
112
  trimLeft = prevTag.charAt(0) === '/' ? !inlineElements.has(prevTag.slice(1)) : !inlineTextSet.has(prevTag);
112
113
  }
114
+ // When `collapseInlineTagWhitespace` is enabled, still preserve whitespace around inline text elements
115
+ if (trimLeft && options.collapseInlineTagWhitespace) {
116
+ const tagName = prevTag.charAt(0) === '/' ? prevTag.slice(1) : prevTag;
117
+ if (inlineElementsToKeepWhitespaceWithin.has(tagName)) {
118
+ trimLeft = false;
119
+ }
120
+ }
113
121
  let trimRight = nextTag && !inlineElementsToKeepWhitespace.has(nextTag);
114
122
  if (trimRight && !options.collapseInlineTagWhitespace) {
115
123
  trimRight = nextTag.charAt(0) === '/' ? !inlineTextSet.has(nextTag.slice(1)) : !inlineElements.has(nextTag);
116
124
  }
125
+ // When `collapseInlineTagWhitespace` is enabled, still preserve whitespace around inline text elements
126
+ if (trimRight && options.collapseInlineTagWhitespace) {
127
+ const tagName = nextTag.charAt(0) === '/' ? nextTag.slice(1) : nextTag;
128
+ if (inlineElementsToKeepWhitespaceWithin.has(tagName)) {
129
+ trimRight = false;
130
+ }
131
+ }
117
132
  return collapseWhitespace(str, options, trimLeft, trimRight, prevTag && nextTag);
118
133
  }
119
134