html-minifier-next 6.2.6 → 6.2.7

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/cli.js CHANGED
@@ -3,7 +3,7 @@
3
3
  /**
4
4
  * html-minifier-next CLI tool
5
5
  *
6
- * The MIT License (MIT)
6
+ * MIT License
7
7
  *
8
8
  * Copyright 2014–2016 Zoltan Frombach
9
9
  * Copyright Juriy “kangax” Zaytsev
@@ -3891,6 +3891,7 @@ async function minifyHTML(value, options, partialMarkup) {
3891
3891
  let uidIgnorePlaceholderPattern;
3892
3892
  let uidAttr;
3893
3893
  let uidPattern;
3894
+ let uidAttrLeadingPattern;
3894
3895
  // Create inline tags/text sets with custom elements
3895
3896
  const customElementsInput = options.inlineCustomElements ?? [];
3896
3897
  const customElementsArr = Array.isArray(customElementsInput) ? customElementsInput : Array.from(customElementsInput);
@@ -3982,6 +3983,7 @@ async function minifyHTML(value, options, partialMarkup) {
3982
3983
  if (!uidAttr) {
3983
3984
  uidAttr = uniqueId(value);
3984
3985
  uidPattern = new RegExp('(\\s*)' + uidAttr + '([0-9]+)' + uidAttr + '(\\s*)', 'g');
3986
+ uidAttrLeadingPattern = new RegExp('^\\s*' + uidAttr + '(\\d+)' + uidAttr);
3985
3987
 
3986
3988
  if (options.minifyCSS) {
3987
3989
  options.minifyCSS = (function (fn) {
@@ -4417,15 +4419,28 @@ async function minifyHTML(value, options, partialMarkup) {
4417
4419
  // Finalization phase (sync): Optional tag handling, entity re-encoding, buffer push
4418
4420
  function charsFinalize(text) {
4419
4421
  if (options.removeOptionalTags && text) {
4422
+ // UID-attr tokens are padded with `\t`, which would falsely look like leading whitespace;
4423
+ // resolve single-token text to its actual content for the space/comment checks below
4424
+ let effectiveText = text;
4425
+ if (uidAttrLeadingPattern && text.includes(uidAttr)) {
4426
+ const uidMatch = uidAttrLeadingPattern.exec(text);
4427
+ if (uidMatch) {
4428
+ const idx = +uidMatch[1];
4429
+ const chunks = idx < ignoredCustomMarkupChunks.length ? ignoredCustomMarkupChunks[idx] : null;
4430
+ if (chunks != null) {
4431
+ effectiveText = chunks[0];
4432
+ }
4433
+ }
4434
+ }
4420
4435
  // `<html>` may be omitted if first thing inside is not a comment
4421
4436
  // `<body>` may be omitted if first thing inside is not space, comment, `<meta>`, `<link>`, `<script>`, `<style>`, or `<template>`
4422
- if (optionalStartTag === 'html' || (optionalStartTag === 'body' && !/^\s/.test(text))) {
4437
+ if (optionalStartTag === 'html' || (optionalStartTag === 'body' && !/^\s/.test(effectiveText))) {
4423
4438
  removeStartTag();
4424
4439
  }
4425
4440
  optionalStartTag = '';
4426
4441
  // `</html>` or `</body>` may be omitted if not followed by comment
4427
4442
  // `</head>`, `</colgroup>`, or `</caption>` may be omitted if not followed by space or comment
4428
- if (optionalEndTagEmitted && (compactElements.has(optionalEndTag) || (looseElements.has(optionalEndTag) && !/^\s/.test(text)))) {
4443
+ if (optionalEndTagEmitted && (compactElements.has(optionalEndTag) || (looseElements.has(optionalEndTag) && !/^\s/.test(effectiveText)))) {
4429
4444
  removeEndTag();
4430
4445
  }
4431
4446
  // Don’t reset `optionalEndTag` if text is only whitespace and will be collapsed (not conservatively)
@@ -4491,7 +4506,25 @@ async function minifyHTML(value, options, partialMarkup) {
4491
4506
  // Finalization phase (sync): Optional tag handling, `htmlmin:ignore` whitespace collapsing, buffer push
4492
4507
  function commentFinalize(comment) {
4493
4508
  if (options.removeOptionalTags && comment) {
4494
- // Preceding comments suppress tag omissions
4509
+ if (uidIgnorePlaceholderPattern) {
4510
+ const match = uidIgnorePlaceholderPattern.exec(comment);
4511
+ if (match) {
4512
+ // UID placeholders represent real HTML content, not true HTML comments;
4513
+ // if there’s a pending optional end tag and the ignored content isn’t itself
4514
+ // a comment (which per the HTML spec prevents omission), resolve it now,
4515
+ // before the UID is pushed to the buffer
4516
+ const idx = +match[1];
4517
+ const content = idx < ignoredMarkupChunks.length ? ignoredMarkupChunks[idx] : null;
4518
+ if (optionalEndTag && optionalEndTagEmitted && content != null && !/^\s*<!--/.test(content)) {
4519
+ const firstTagMatch = content.match(/^\s*<([a-zA-Z][^\s/>]*)/);
4520
+ const firstTag = firstTagMatch ? options.name(firstTagMatch[1]) : '';
4521
+ if (canRemovePrecedingTag(optionalEndTag, firstTag)) {
4522
+ removeEndTag();
4523
+ }
4524
+ }
4525
+ }
4526
+ }
4527
+ // Comments (real or placeholder) always suppress optional start tag omissions
4495
4528
  optionalStartTag = '';
4496
4529
  optionalEndTag = '';
4497
4530
  optionalEndTagEmitted = false;
@@ -1 +1 @@
1
- {"version":3,"file":"htmlminifier.d.ts","sourceRoot":"","sources":["../../src/htmlminifier.js"],"names":[],"mappings":"AA2wDO,8BAJI,MAAM,YACN,eAAe,GACb,OAAO,CAAC,MAAM,CAAC,CAwB3B;;;;;;;;;;;;UA3/CS,MAAM;;;;;;;;;;;;;;;;;;mCAaA,MAAM,SAAS,aAAa,EAAE,yBAAyB,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,KAAK,OAAO;;;;;;;+BAM3F,MAAM,GAAG,IAAI,SAAS,aAAa,EAAE,GAAG,SAAS,qBAAqB,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,KAAK,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA6JtG,OAAO,KAAK,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2HA2BiF,MAAM,SAAS,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM;;;;;;;;;;;;;;;;iBASxG,QAAQ,GAAG,KAAK;gBAAgC,MAAM,WAAW,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM;;;;;;;;;;;eAa/H,MAAM;gBAAY,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM;;;;;;;;;;;;;;;;;mBAiBzE,MAAM,KAAK,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kDA+DF,MAAM,OAAO,MAAM,KAAK,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sCA2EpC,MAAM,SAAS,aAAa,EAAE,KAAK,IAAI;;;;;;;;;wCAQrC,MAAM,KAAK,MAAM;;;;;;;;;;;;;;;;;wBAtqBK,cAAc;0BAAd,cAAc;+BAAd,cAAc"}
1
+ {"version":3,"file":"htmlminifier.d.ts","sourceRoot":"","sources":["../../src/htmlminifier.js"],"names":[],"mappings":"AA4yDO,8BAJI,MAAM,YACN,eAAe,GACb,OAAO,CAAC,MAAM,CAAC,CAwB3B;;;;;;;;;;;;UA5hDS,MAAM;;;;;;;;;;;;;;;;;;mCAaA,MAAM,SAAS,aAAa,EAAE,yBAAyB,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,KAAK,OAAO;;;;;;;+BAM3F,MAAM,GAAG,IAAI,SAAS,aAAa,EAAE,GAAG,SAAS,qBAAqB,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,KAAK,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA6JtG,OAAO,KAAK,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2HA2BiF,MAAM,SAAS,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM;;;;;;;;;;;;;;;;iBASxG,QAAQ,GAAG,KAAK;gBAAgC,MAAM,WAAW,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM;;;;;;;;;;;eAa/H,MAAM;gBAAY,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM;;;;;;;;;;;;;;;;;mBAiBzE,MAAM,KAAK,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kDA+DF,MAAM,OAAO,MAAM,KAAK,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sCA2EpC,MAAM,SAAS,aAAa,EAAE,KAAK,IAAI;;;;;;;;;wCAQrC,MAAM,KAAK,MAAM;;;;;;;;;;;;;;;;;wBAtqBK,cAAc;0BAAd,cAAc;+BAAd,cAAc"}
package/package.json CHANGED
@@ -97,5 +97,5 @@
97
97
  },
98
98
  "type": "module",
99
99
  "types": "./dist/types/htmlminifier.d.ts",
100
- "version": "6.2.6"
100
+ "version": "6.2.7"
101
101
  }
@@ -949,6 +949,7 @@ async function minifyHTML(value, options, partialMarkup) {
949
949
  let uidIgnorePlaceholderPattern;
950
950
  let uidAttr;
951
951
  let uidPattern;
952
+ let uidAttrLeadingPattern;
952
953
  // Create inline tags/text sets with custom elements
953
954
  const customElementsInput = options.inlineCustomElements ?? [];
954
955
  const customElementsArr = Array.isArray(customElementsInput) ? customElementsInput : Array.from(customElementsInput);
@@ -1040,6 +1041,7 @@ async function minifyHTML(value, options, partialMarkup) {
1040
1041
  if (!uidAttr) {
1041
1042
  uidAttr = uniqueId(value);
1042
1043
  uidPattern = new RegExp('(\\s*)' + uidAttr + '([0-9]+)' + uidAttr + '(\\s*)', 'g');
1044
+ uidAttrLeadingPattern = new RegExp('^\\s*' + uidAttr + '(\\d+)' + uidAttr);
1043
1045
 
1044
1046
  if (options.minifyCSS) {
1045
1047
  options.minifyCSS = (function (fn) {
@@ -1475,15 +1477,28 @@ async function minifyHTML(value, options, partialMarkup) {
1475
1477
  // Finalization phase (sync): Optional tag handling, entity re-encoding, buffer push
1476
1478
  function charsFinalize(text) {
1477
1479
  if (options.removeOptionalTags && text) {
1480
+ // UID-attr tokens are padded with `\t`, which would falsely look like leading whitespace;
1481
+ // resolve single-token text to its actual content for the space/comment checks below
1482
+ let effectiveText = text;
1483
+ if (uidAttrLeadingPattern && text.includes(uidAttr)) {
1484
+ const uidMatch = uidAttrLeadingPattern.exec(text);
1485
+ if (uidMatch) {
1486
+ const idx = +uidMatch[1];
1487
+ const chunks = idx < ignoredCustomMarkupChunks.length ? ignoredCustomMarkupChunks[idx] : null;
1488
+ if (chunks != null) {
1489
+ effectiveText = chunks[0];
1490
+ }
1491
+ }
1492
+ }
1478
1493
  // `<html>` may be omitted if first thing inside is not a comment
1479
1494
  // `<body>` may be omitted if first thing inside is not space, comment, `<meta>`, `<link>`, `<script>`, `<style>`, or `<template>`
1480
- if (optionalStartTag === 'html' || (optionalStartTag === 'body' && !/^\s/.test(text))) {
1495
+ if (optionalStartTag === 'html' || (optionalStartTag === 'body' && !/^\s/.test(effectiveText))) {
1481
1496
  removeStartTag();
1482
1497
  }
1483
1498
  optionalStartTag = '';
1484
1499
  // `</html>` or `</body>` may be omitted if not followed by comment
1485
1500
  // `</head>`, `</colgroup>`, or `</caption>` may be omitted if not followed by space or comment
1486
- if (optionalEndTagEmitted && (compactElements.has(optionalEndTag) || (looseElements.has(optionalEndTag) && !/^\s/.test(text)))) {
1501
+ if (optionalEndTagEmitted && (compactElements.has(optionalEndTag) || (looseElements.has(optionalEndTag) && !/^\s/.test(effectiveText)))) {
1487
1502
  removeEndTag();
1488
1503
  }
1489
1504
  // Don’t reset `optionalEndTag` if text is only whitespace and will be collapsed (not conservatively)
@@ -1549,7 +1564,25 @@ async function minifyHTML(value, options, partialMarkup) {
1549
1564
  // Finalization phase (sync): Optional tag handling, `htmlmin:ignore` whitespace collapsing, buffer push
1550
1565
  function commentFinalize(comment) {
1551
1566
  if (options.removeOptionalTags && comment) {
1552
- // Preceding comments suppress tag omissions
1567
+ if (uidIgnorePlaceholderPattern) {
1568
+ const match = uidIgnorePlaceholderPattern.exec(comment);
1569
+ if (match) {
1570
+ // UID placeholders represent real HTML content, not true HTML comments;
1571
+ // if there’s a pending optional end tag and the ignored content isn’t itself
1572
+ // a comment (which per the HTML spec prevents omission), resolve it now,
1573
+ // before the UID is pushed to the buffer
1574
+ const idx = +match[1];
1575
+ const content = idx < ignoredMarkupChunks.length ? ignoredMarkupChunks[idx] : null;
1576
+ if (optionalEndTag && optionalEndTagEmitted && content != null && !/^\s*<!--/.test(content)) {
1577
+ const firstTagMatch = content.match(/^\s*<([a-zA-Z][^\s/>]*)/);
1578
+ const firstTag = firstTagMatch ? options.name(firstTagMatch[1]) : '';
1579
+ if (canRemovePrecedingTag(optionalEndTag, firstTag)) {
1580
+ removeEndTag();
1581
+ }
1582
+ }
1583
+ }
1584
+ }
1585
+ // Comments (real or placeholder) always suppress optional start tag omissions
1553
1586
  optionalStartTag = '';
1554
1587
  optionalEndTag = '';
1555
1588
  optionalEndTagEmitted = false;