html-minifier-next 4.9.1 → 4.9.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 CHANGED
@@ -233,30 +233,32 @@ How does HTML Minifier Next compare to other minifiers? (All with the most aggre
233
233
  | Site | Original Size (KB) | [HTML Minifier Next](https://github.com/j9t/html-minifier-next)<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/) |
234
234
  | --- | --- | --- | --- | --- | --- | --- | --- | --- |
235
235
  | [A List Apart](https://alistapart.com/) | 59 | **49** | 50 | 51 | 52 | 51 | 54 | 52 |
236
- | [Apple](https://www.apple.com/) | 260 | **204** | **204** | 232 | 235 | 236 | 238 | 239 |
237
- | [BBC](https://www.bbc.co.uk/) | 803 | **683** | 693 | 746 | 757 | 760 | 796 | n/a |
238
- | [CSS-Tricks](https://css-tricks.com/) | 161 | 121 | **119** | 126 | 142 | 142 | 147 | 143 |
236
+ | [Apple](https://www.apple.com/) | 260 | **203** | **203** | 231 | 235 | 236 | 237 | 238 |
237
+ | [BBC](https://www.bbc.co.uk/) | 904 | **772** | 783 | 840 | 852 | 855 | 896 | n/a |
238
+ | [CSS-Tricks](https://css-tricks.com/) | 160 | 120 | **119** | 126 | 141 | 141 | 146 | 143 |
239
239
  | [ECMAScript](https://tc39.es/ecma262/) | 7238 | **6341** | **6341** | 6561 | 6444 | 6567 | 6614 | n/a |
240
- | [EFF](https://www.eff.org/) | 56 | **47** | 48 | 50 | 49 | 49 | 51 | 51 |
241
- | [FAZ](https://www.faz.net/aktuell/) | 1602 | 1492 | 1497 | **1435** | 1525 | 1537 | 1548 | n/a |
242
- | [Frontend Dogma](https://frontenddogma.com/) | 222 | **212** | 213 | 234 | 219 | 221 | 240 | 221 |
240
+ | [EDRi](https://edri.org/) | 80 | **59** | 60 | 70 | 70 | 71 | 75 | 73 |
241
+ | [EFF](https://www.eff.org/) | 56 | **47** | **47** | 50 | 49 | 49 | 51 | 50 |
242
+ | [European Alternatives](https://european-alternatives.eu/) | 48 | **30** | **30** | 32 | 32 | 32 | 32 | 32 |
243
+ | [FAZ](https://www.faz.net/aktuell/) | 1559 | 1452 | 1457 | **1397** | 1484 | 1495 | 1506 | n/a |
244
+ | [Frontend Dogma](https://frontenddogma.com/) | 221 | **211** | 213 | 234 | 219 | 221 | 239 | 220 |
243
245
  | [Google](https://www.google.com/) | 18 | **17** | **17** | **17** | **17** | **17** | 18 | 18 |
244
- | [Ground News](https://ground.news/) | 1576 | **1353** | 1356 | 1450 | 1473 | 1478 | 1563 | n/a |
246
+ | [Ground News](https://ground.news/) | 1967 | **1707** | 1710 | 1810 | 1835 | 1840 | 1954 | n/a |
245
247
  | [HTML Living Standard](https://html.spec.whatwg.org/multipage/) | 149 | **147** | **147** | 153 | **147** | 149 | 155 | 149 |
246
- | [Igalia](https://www.igalia.com/) | 49 | **33** | **33** | 35 | 35 | 35 | 36 | 36 |
247
- | [Leanpub](https://leanpub.com/) | 2036 | **1755** | **1755** | 1762 | 1761 | 1759 | 2031 | n/a |
248
+ | [Igalia](https://www.igalia.com/) | 49 | **33** | **33** | 36 | 35 | 36 | 36 | 36 |
249
+ | [Leanpub](https://leanpub.com/) | 1268 | **1071** | **1071** | 1078 | 1076 | 1072 | 1263 | n/a |
248
250
  | [Mastodon](https://mastodon.social/explore) | 36 | **27** | **27** | 31 | 34 | 34 | 35 | 35 |
249
- | [MDN](https://developer.mozilla.org/en-US/) | 107 | **61** | **61** | 63 | 63 | 64 | 66 | 67 |
250
- | [Middle East Eye](https://www.middleeasteye.net/) | 223 | **195** | 196 | 203 | 201 | 200 | 202 | 203 |
251
- | [Nielsen Norman Group](https://www.nngroup.com/) | 84 | 72 | 72 | **53** | 72 | 73 | 74 | 73 |
251
+ | [MDN](https://developer.mozilla.org/en-US/) | 109 | **62** | **62** | 64 | 65 | 65 | 68 | 68 |
252
+ | [Middle East Eye](https://www.middleeasteye.net/) | 223 | **195** | **195** | 202 | 200 | 200 | 202 | 203 |
253
+ | [Nielsen Norman Group](https://www.nngroup.com/) | 84 | 71 | 72 | **53** | 71 | 73 | 74 | 73 |
252
254
  | [SitePoint](https://www.sitepoint.com/) | 487 | **346** | **346** | 424 | 461 | 466 | 484 | n/a |
253
255
  | [TetraLogical](https://tetralogical.com/) | 44 | 38 | 38 | **35** | 38 | 38 | 39 | 39 |
254
- | [TPGi](https://www.tpgi.com/) | 175 | **160** | 162 | **160** | 165 | 166 | 172 | 172 |
256
+ | [TPGi](https://www.tpgi.com/) | 176 | **160** | 162 | **160** | 165 | 166 | 173 | 172 |
255
257
  | [United Nations](https://www.un.org/en/) | 150 | **112** | 113 | 120 | 124 | 124 | 129 | 122 |
256
258
  | [W3C](https://www.w3.org/) | 50 | **35** | 36 | 38 | 38 | 38 | 40 | 38 |
257
- | **Average processing time** | | 338 ms (22/22) | 385 ms (22/22) | 191 ms (22/22) | 70 ms (22/22) | **18 ms (22/22)** | 365 ms (22/22) | 1403 ms (16/22) |
259
+ | **Average processing time** | | 313 ms (24/24) | 367 ms (24/24) | 184 ms (24/24) | 62 ms (24/24) | **18 ms (24/24)** | 336 ms (24/24) | 1392 ms (18/24) |
258
260
 
259
- (Last updated: Dec 12, 2025)
261
+ (Last updated: Dec 14, 2025)
260
262
  <!-- End auto-generated -->
261
263
 
262
264
  ## Examples
@@ -5,18 +5,6 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  var entities = require('entities');
6
6
  var RelateURL = require('relateurl');
7
7
 
8
- async function replaceAsync(str, regex, asyncFn) {
9
- const promises = [];
10
-
11
- str.replace(regex, (match, ...args) => {
12
- const promise = asyncFn(match, ...args);
13
- promises.push(promise);
14
- });
15
-
16
- const data = await Promise.all(promises);
17
- return str.replace(regex, () => data.shift());
18
- }
19
-
20
8
  /*!
21
9
  * HTML Parser By John Resig (ejohn.org)
22
10
  * Modified by Juriy “kangax” Zaytsev
@@ -24,6 +12,15 @@ async function replaceAsync(str, regex, asyncFn) {
24
12
  * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
25
13
  */
26
14
 
15
+ /*
16
+ * // Use like so:
17
+ * HTMLParser(htmlString, {
18
+ * start: function(tag, attrs, unary) {},
19
+ * end: function(tag) {},
20
+ * chars: function(text) {},
21
+ * comment: function(text) {}
22
+ * });
23
+ */
27
24
 
28
25
  class CaseInsensitiveSet extends Set {
29
26
  has(str) {
@@ -192,7 +189,7 @@ class HTMLParser {
192
189
  const endTagMatch = html.match(endTag);
193
190
  if (endTagMatch) {
194
191
  html = html.substring(endTagMatch[0].length);
195
- await replaceAsync(endTagMatch[0], endTag, parseEndTag);
192
+ await parseEndTag(endTagMatch[0], endTagMatch[1]);
196
193
  prevTag = '/' + endTagMatch[1].toLowerCase();
197
194
  continue;
198
195
  }
@@ -243,21 +240,29 @@ class HTMLParser {
243
240
  // Use pre-compiled regex for common tags (`script`, `style`, `noscript`) to avoid regex creation overhead
244
241
  const reStackedTag = preCompiledStackedTags[stackedTag] || reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)</' + stackedTag + '[^>]*>', 'i'));
245
242
 
246
- html = await replaceAsync(html, reStackedTag, async (_, text) => {
243
+ const m = reStackedTag.exec(html);
244
+ if (m) {
245
+ let text = m[1];
247
246
  if (stackedTag !== 'script' && stackedTag !== 'style' && stackedTag !== 'noscript') {
248
247
  text = text
249
248
  .replace(/<!--([\s\S]*?)-->/g, '$1')
250
249
  .replace(/<!\[CDATA\[([\s\S]*?)]]>/g, '$1');
251
250
  }
252
-
253
251
  if (handler.chars) {
254
252
  await handler.chars(text);
255
253
  }
256
-
257
- return '';
258
- });
259
-
260
- await parseEndTag('</' + stackedTag + '>', stackedTag);
254
+ // Advance HTML past the matched special tag content and its closing tag
255
+ html = html.slice(m.index + m[0].length);
256
+ await parseEndTag('</' + stackedTag + '>', stackedTag);
257
+ } else {
258
+ // No closing tag found; to avoid infinite loop, break similarly to previous behavior
259
+ if (handler.continueOnParseError && handler.chars && html) {
260
+ await handler.chars(html[0], prevTag, '');
261
+ html = html.substring(1);
262
+ } else {
263
+ break;
264
+ }
265
+ }
261
266
  }
262
267
 
263
268
  if (html === last) {
@@ -644,6 +649,18 @@ class TokenChain {
644
649
  }
645
650
  }
646
651
 
652
+ async function replaceAsync(str, regex, asyncFn) {
653
+ const promises = [];
654
+
655
+ str.replace(regex, (match, ...args) => {
656
+ const promise = asyncFn(match, ...args);
657
+ promises.push(promise);
658
+ });
659
+
660
+ const data = await Promise.all(promises);
661
+ return str.replace(regex, () => data.shift());
662
+ }
663
+
647
664
  /**
648
665
  * Preset configurations for HTML Minifier Next
649
666
  *
@@ -2294,7 +2311,9 @@ async function createSortFns(value, options, uidIgnore, uidAttr) {
2294
2311
  currentType === 'text/html') {
2295
2312
  await scan(text);
2296
2313
  }
2297
- }
2314
+ },
2315
+ // We never need `nextTag` information in this scan
2316
+ wantsNextTag: false
2298
2317
  });
2299
2318
 
2300
2319
  await parser.parse();
@@ -2530,6 +2549,8 @@ async function minifyHTML(value, options, partialMarkup) {
2530
2549
  customAttrAssign: options.customAttrAssign,
2531
2550
  customAttrSurround: options.customAttrSurround,
2532
2551
  html5: options.html5,
2552
+ // Compute `nextTag` only when whitespace collapse features require it
2553
+ wantsNextTag: !!(options.collapseWhitespace || options.collapseInlineTagWhitespace || options.conservativeCollapse),
2533
2554
 
2534
2555
  start: async function (tag, attrs, unary, unarySlash, autoGenerated) {
2535
2556
  if (tag.toLowerCase() === 'svg') {
@@ -5147,18 +5147,6 @@ function requireLib () {
5147
5147
  var libExports = requireLib();
5148
5148
  var RelateURL = /*@__PURE__*/getDefaultExportFromCjs(libExports);
5149
5149
 
5150
- async function replaceAsync(str, regex, asyncFn) {
5151
- const promises = [];
5152
-
5153
- str.replace(regex, (match, ...args) => {
5154
- const promise = asyncFn(match, ...args);
5155
- promises.push(promise);
5156
- });
5157
-
5158
- const data = await Promise.all(promises);
5159
- return str.replace(regex, () => data.shift());
5160
- }
5161
-
5162
5150
  /*!
5163
5151
  * HTML Parser By John Resig (ejohn.org)
5164
5152
  * Modified by Juriy “kangax” Zaytsev
@@ -5166,6 +5154,15 @@ async function replaceAsync(str, regex, asyncFn) {
5166
5154
  * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
5167
5155
  */
5168
5156
 
5157
+ /*
5158
+ * // Use like so:
5159
+ * HTMLParser(htmlString, {
5160
+ * start: function(tag, attrs, unary) {},
5161
+ * end: function(tag) {},
5162
+ * chars: function(text) {},
5163
+ * comment: function(text) {}
5164
+ * });
5165
+ */
5169
5166
 
5170
5167
  class CaseInsensitiveSet extends Set {
5171
5168
  has(str) {
@@ -5334,7 +5331,7 @@ class HTMLParser {
5334
5331
  const endTagMatch = html.match(endTag);
5335
5332
  if (endTagMatch) {
5336
5333
  html = html.substring(endTagMatch[0].length);
5337
- await replaceAsync(endTagMatch[0], endTag, parseEndTag);
5334
+ await parseEndTag(endTagMatch[0], endTagMatch[1]);
5338
5335
  prevTag = '/' + endTagMatch[1].toLowerCase();
5339
5336
  continue;
5340
5337
  }
@@ -5385,21 +5382,29 @@ class HTMLParser {
5385
5382
  // Use pre-compiled regex for common tags (`script`, `style`, `noscript`) to avoid regex creation overhead
5386
5383
  const reStackedTag = preCompiledStackedTags[stackedTag] || reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)</' + stackedTag + '[^>]*>', 'i'));
5387
5384
 
5388
- html = await replaceAsync(html, reStackedTag, async (_, text) => {
5385
+ const m = reStackedTag.exec(html);
5386
+ if (m) {
5387
+ let text = m[1];
5389
5388
  if (stackedTag !== 'script' && stackedTag !== 'style' && stackedTag !== 'noscript') {
5390
5389
  text = text
5391
5390
  .replace(/<!--([\s\S]*?)-->/g, '$1')
5392
5391
  .replace(/<!\[CDATA\[([\s\S]*?)]]>/g, '$1');
5393
5392
  }
5394
-
5395
5393
  if (handler.chars) {
5396
5394
  await handler.chars(text);
5397
5395
  }
5398
-
5399
- return '';
5400
- });
5401
-
5402
- await parseEndTag('</' + stackedTag + '>', stackedTag);
5396
+ // Advance HTML past the matched special tag content and its closing tag
5397
+ html = html.slice(m.index + m[0].length);
5398
+ await parseEndTag('</' + stackedTag + '>', stackedTag);
5399
+ } else {
5400
+ // No closing tag found; to avoid infinite loop, break similarly to previous behavior
5401
+ if (handler.continueOnParseError && handler.chars && html) {
5402
+ await handler.chars(html[0], prevTag, '');
5403
+ html = html.substring(1);
5404
+ } else {
5405
+ break;
5406
+ }
5407
+ }
5403
5408
  }
5404
5409
 
5405
5410
  if (html === last) {
@@ -5786,6 +5791,18 @@ class TokenChain {
5786
5791
  }
5787
5792
  }
5788
5793
 
5794
+ async function replaceAsync(str, regex, asyncFn) {
5795
+ const promises = [];
5796
+
5797
+ str.replace(regex, (match, ...args) => {
5798
+ const promise = asyncFn(match, ...args);
5799
+ promises.push(promise);
5800
+ });
5801
+
5802
+ const data = await Promise.all(promises);
5803
+ return str.replace(regex, () => data.shift());
5804
+ }
5805
+
5789
5806
  /**
5790
5807
  * Preset configurations for HTML Minifier Next
5791
5808
  *
@@ -7436,7 +7453,9 @@ async function createSortFns(value, options, uidIgnore, uidAttr) {
7436
7453
  currentType === 'text/html') {
7437
7454
  await scan(text);
7438
7455
  }
7439
- }
7456
+ },
7457
+ // We never need `nextTag` information in this scan
7458
+ wantsNextTag: false
7440
7459
  });
7441
7460
 
7442
7461
  await parser.parse();
@@ -7672,6 +7691,8 @@ async function minifyHTML(value, options, partialMarkup) {
7672
7691
  customAttrAssign: options.customAttrAssign,
7673
7692
  customAttrSurround: options.customAttrSurround,
7674
7693
  html5: options.html5,
7694
+ // Compute `nextTag` only when whitespace collapse features require it
7695
+ wantsNextTag: !!(options.collapseWhitespace || options.collapseInlineTagWhitespace || options.conservativeCollapse),
7675
7696
 
7676
7697
  start: async function (tag, attrs, unary, unarySlash, autoGenerated) {
7677
7698
  if (tag.toLowerCase() === 'svg') {
@@ -1 +1 @@
1
- {"version":3,"file":"htmlminifier.d.ts","sourceRoot":"","sources":["../../src/htmlminifier.js"],"names":[],"mappings":"AAgvEO,8BAJI,MAAM,YACN,eAAe,GACb,OAAO,CAAC,MAAM,CAAC,CAQ3B;;;;;;;;;;;;UAvtES,MAAM;YACN,MAAM;YACN,MAAM;mBACN,MAAM;iBACN,MAAM;kBACN,MAAM;;;;;;;;;;;;;4BAQN,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,qBAAqB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,KAAK,OAAO;;;;;;;wBAMjG,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,SAAS,EAAE,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,KAAK,OAAO;;;;;;;;oBAMhH,OAAO;;;;;;;;gCAOP,OAAO;;;;;;;;kCAOP,OAAO;;;;;;;;yBAOP,OAAO;;;;;;;;2BAOP,OAAO;;;;;;;;4BAOP,OAAO;;;;;;;2BAOP,OAAO;;;;;;;;uBAMP,MAAM,EAAE;;;;;;yBAOR,MAAM;;;;;;yBAKN,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE;;;;;;;4BAKlB,MAAM,EAAE;;;;;;;oCAMR,MAAM;;;;;;;qBAMN,OAAO;;;;;;;YAMP,OAAO;;;;;;;;2BAMP,MAAM,EAAE;;;;;;;;;4BAOR,MAAM,EAAE;;;;;;;+BAQR,OAAO;;;;;;;2BAMP,SAAS,CAAC,MAAM,CAAC;;;;;;uBAMjB,OAAO;;;;;;;;UAKP,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI;;;;;;;;qBAO1B,MAAM;;;;;;;oBAON,MAAM;;;;;;;;;;gBAMN,OAAO,GAAG,OAAO,CAAC,OAAO,cAAc,EAAE,gBAAgB,CAAC,OAAO,cAAc,EAAE,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;;;;;;;;;;eAS9J,OAAO,GAAG,OAAO,QAAQ,EAAE,aAAa,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;;;;;;;;;;iBASzG,OAAO,GAAG,MAAM,GAAG,OAAO,WAAW,EAAE,OAAO,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;;;;;;;;WAS7F,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM;;;;;;;+BAOxB,OAAO;;;;;;;;;;oBAMP,OAAO;;;;;;;;yBASP,OAAO;;;;;;;gCAOP,OAAO;;;;;;;;iCAMP,OAAO;;;;;;;;;;qBAOP,MAAM,EAAE;;;;;;;qBASR,IAAI,GAAG,GAAG;;;;;;;4BAMV,OAAO;;;;;;;;qBAMP,OAAO;;;;;;;;;4BAOP,OAAO,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;;;;;;;;0BAQtD,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;gCAOP,MAAM,EAAE;;;;;;;;yBAyBR,OAAO;;;;;;;;gCAOP,OAAO;;;;;;;iCAOP,OAAO;;;;;;;oCAMP,OAAO;;;;;;;;;;0BAMP,OAAO;;;;;;;;;qBASP,OAAO,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,IAAI,CAAC;;;;;;;;;oBAQzD,OAAO,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;;;;;;;;0BAQrC,OAAO;;;;;;;sBAOP,OAAO;;wBAnYkC,cAAc;0BAAd,cAAc;+BAAd,cAAc"}
1
+ {"version":3,"file":"htmlminifier.d.ts","sourceRoot":"","sources":["../../src/htmlminifier.js"],"names":[],"mappings":"AAovEO,8BAJI,MAAM,YACN,eAAe,GACb,OAAO,CAAC,MAAM,CAAC,CAQ3B;;;;;;;;;;;;UA3tES,MAAM;YACN,MAAM;YACN,MAAM;mBACN,MAAM;iBACN,MAAM;kBACN,MAAM;;;;;;;;;;;;;4BAQN,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,qBAAqB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,KAAK,OAAO;;;;;;;wBAMjG,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,SAAS,EAAE,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,KAAK,OAAO;;;;;;;;oBAMhH,OAAO;;;;;;;;gCAOP,OAAO;;;;;;;;kCAOP,OAAO;;;;;;;;yBAOP,OAAO;;;;;;;;2BAOP,OAAO;;;;;;;;4BAOP,OAAO;;;;;;;2BAOP,OAAO;;;;;;;;uBAMP,MAAM,EAAE;;;;;;yBAOR,MAAM;;;;;;yBAKN,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE;;;;;;;4BAKlB,MAAM,EAAE;;;;;;;oCAMR,MAAM;;;;;;;qBAMN,OAAO;;;;;;;YAMP,OAAO;;;;;;;;2BAMP,MAAM,EAAE;;;;;;;;;4BAOR,MAAM,EAAE;;;;;;;+BAQR,OAAO;;;;;;;2BAMP,SAAS,CAAC,MAAM,CAAC;;;;;;uBAMjB,OAAO;;;;;;;;UAKP,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI;;;;;;;;qBAO1B,MAAM;;;;;;;oBAON,MAAM;;;;;;;;;;gBAMN,OAAO,GAAG,OAAO,CAAC,OAAO,cAAc,EAAE,gBAAgB,CAAC,OAAO,cAAc,EAAE,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;;;;;;;;;;eAS9J,OAAO,GAAG,OAAO,QAAQ,EAAE,aAAa,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;;;;;;;;;;iBASzG,OAAO,GAAG,MAAM,GAAG,OAAO,WAAW,EAAE,OAAO,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;;;;;;;;WAS7F,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM;;;;;;;+BAOxB,OAAO;;;;;;;;;;oBAMP,OAAO;;;;;;;;yBASP,OAAO;;;;;;;gCAOP,OAAO;;;;;;;;iCAMP,OAAO;;;;;;;;;;qBAOP,MAAM,EAAE;;;;;;;qBASR,IAAI,GAAG,GAAG;;;;;;;4BAMV,OAAO;;;;;;;;qBAMP,OAAO;;;;;;;;;4BAOP,OAAO,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;;;;;;;;0BAQtD,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;gCAOP,MAAM,EAAE;;;;;;;;yBAyBR,OAAO;;;;;;;;gCAOP,OAAO;;;;;;;iCAOP,OAAO;;;;;;;oCAMP,OAAO;;;;;;;;;;0BAMP,OAAO;;;;;;;;;qBASP,OAAO,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,IAAI,CAAC;;;;;;;;;oBAQzD,OAAO,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;;;;;;;;0BAQrC,OAAO;;;;;;;sBAOP,OAAO;;wBAnYkC,cAAc;0BAAd,cAAc;+BAAd,cAAc"}
@@ -1 +1 @@
1
- {"version":3,"file":"htmlparser.d.ts","sourceRoot":"","sources":["../../src/htmlparser.js"],"names":[],"mappings":"AAgDA,4BAAoE;AAoEpE;IACE,qCAGC;IAFC,UAAgB;IAChB,aAAsB;IAGxB,uBA8bC;CACF"}
1
+ {"version":3,"file":"htmlparser.d.ts","sourceRoot":"","sources":["../../src/htmlparser.js"],"names":[],"mappings":"AA8CA,4BAAoE;AAoEpE;IACE,qCAGC;IAFC,UAAgB;IAChB,aAAsB;IAGxB,uBAscC;CACF"}
package/package.json CHANGED
@@ -84,5 +84,5 @@
84
84
  "test:watch": "node --test --watch tests/*.spec.js"
85
85
  },
86
86
  "type": "module",
87
- "version": "4.9.1"
87
+ "version": "4.9.2"
88
88
  }
@@ -1588,7 +1588,9 @@ async function createSortFns(value, options, uidIgnore, uidAttr) {
1588
1588
  currentType === 'text/html') {
1589
1589
  await scan(text);
1590
1590
  }
1591
- }
1591
+ },
1592
+ // We never need `nextTag` information in this scan
1593
+ wantsNextTag: false
1592
1594
  });
1593
1595
 
1594
1596
  await parser.parse();
@@ -1824,6 +1826,8 @@ async function minifyHTML(value, options, partialMarkup) {
1824
1826
  customAttrAssign: options.customAttrAssign,
1825
1827
  customAttrSurround: options.customAttrSurround,
1826
1828
  html5: options.html5,
1829
+ // Compute `nextTag` only when whitespace collapse features require it
1830
+ wantsNextTag: !!(options.collapseWhitespace || options.collapseInlineTagWhitespace || options.conservativeCollapse),
1827
1831
 
1828
1832
  start: async function (tag, attrs, unary, unarySlash, autoGenerated) {
1829
1833
  if (tag.toLowerCase() === 'svg') {
package/src/htmlparser.js CHANGED
@@ -15,8 +15,6 @@
15
15
  * });
16
16
  */
17
17
 
18
- import { replaceAsync } from './utils.js';
19
-
20
18
  class CaseInsensitiveSet extends Set {
21
19
  has(str) {
22
20
  return super.has(str.toLowerCase());
@@ -184,7 +182,7 @@ export class HTMLParser {
184
182
  const endTagMatch = html.match(endTag);
185
183
  if (endTagMatch) {
186
184
  html = html.substring(endTagMatch[0].length);
187
- await replaceAsync(endTagMatch[0], endTag, parseEndTag);
185
+ await parseEndTag(endTagMatch[0], endTagMatch[1]);
188
186
  prevTag = '/' + endTagMatch[1].toLowerCase();
189
187
  continue;
190
188
  }
@@ -235,21 +233,29 @@ export class HTMLParser {
235
233
  // Use pre-compiled regex for common tags (`script`, `style`, `noscript`) to avoid regex creation overhead
236
234
  const reStackedTag = preCompiledStackedTags[stackedTag] || reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)</' + stackedTag + '[^>]*>', 'i'));
237
235
 
238
- html = await replaceAsync(html, reStackedTag, async (_, text) => {
236
+ const m = reStackedTag.exec(html);
237
+ if (m) {
238
+ let text = m[1];
239
239
  if (stackedTag !== 'script' && stackedTag !== 'style' && stackedTag !== 'noscript') {
240
240
  text = text
241
241
  .replace(/<!--([\s\S]*?)-->/g, '$1')
242
242
  .replace(/<!\[CDATA\[([\s\S]*?)]]>/g, '$1');
243
243
  }
244
-
245
244
  if (handler.chars) {
246
245
  await handler.chars(text);
247
246
  }
248
-
249
- return '';
250
- });
251
-
252
- await parseEndTag('</' + stackedTag + '>', stackedTag);
247
+ // Advance HTML past the matched special tag content and its closing tag
248
+ html = html.slice(m.index + m[0].length);
249
+ await parseEndTag('</' + stackedTag + '>', stackedTag);
250
+ } else {
251
+ // No closing tag found; to avoid infinite loop, break similarly to previous behavior
252
+ if (handler.continueOnParseError && handler.chars && html) {
253
+ await handler.chars(html[0], prevTag, '');
254
+ html = html.substring(1);
255
+ } else {
256
+ break;
257
+ }
258
+ }
253
259
  }
254
260
 
255
261
  if (html === last) {