html-minifier-next 8.5.0 → 8.5.1
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 +10 -2
- package/dist/types/htmlminifier.d.ts +4 -3
- package/dist/types/htmlminifier.d.ts.map +1 -1
- package/dist/types/htmlparser.d.ts.map +1 -1
- package/html-minifier-next.schema.json +3 -3
- package/package.json +1 -1
- package/src/htmlminifier.js +144 -23
- package/src/htmlparser.js +5 -2
- package/src/lib/option-definitions.js +3 -3
package/README.md
CHANGED
|
@@ -151,7 +151,7 @@ Options can be used in config files (camelCase) or via CLI flags (kebab-case wit
|
|
|
151
151
|
| `customEventAttributes`<br>`--custom-event-attributes` | Array of regexes that allow to support custom event attributes (e.g., `ng-click`)—use with `minifyJS` | `[ /^on[a-z]{3,}$/ ]` |
|
|
152
152
|
| `decodeEntities`<br>`--decode-entities` | Use direct Unicode characters whenever possible | `false` |
|
|
153
153
|
| `ignoreCustomComments`<br>`--ignore-custom-comments` | Array of regexes that allow to ignore matching comments | `[ /^!/, /^\s*#/ ]` |
|
|
154
|
-
| `ignoreCustomFragments`<br>`--ignore-custom-fragments` | Array of regexes that allow to ignore certain fragments, when matched (e.g., `<?php … ?>`, `{{ … }}`, etc
|
|
154
|
+
| `ignoreCustomFragments`<br>`--ignore-custom-fragments` | Array of regexes that allow to ignore certain fragments, when matched (e.g., `<?php … ?>`, `{{ … }}`, etc.; see [“Minifying server-side templates”](#minifying-server-side-templates)) | `[ /<%[\s\S]*?%>/, /<\?[\s\S]*?\?>/ ]` |
|
|
155
155
|
| `includeAutoGeneratedTags`<br>`--include-auto-generated-tags` | Insert elements generated by HTML parser | `false` |
|
|
156
156
|
| `inlineCustomElements`<br>`--inline-custom-elements` | Array of names of custom elements which are inline, for whitespace handling—use with `collapseWhitespace` | `[]` |
|
|
157
157
|
| `keepClosingSlash`<br>`--keep-closing-slash` | Keep the trailing slash on void elements and other start tags that carry one, and read it as closing the element | `false` |
|
|
@@ -181,7 +181,7 @@ Options can be used in config files (camelCase) or via CLI flags (kebab-case wit
|
|
|
181
181
|
| `sortAttributes`<br>`--sort-attributes` | [Sort attributes by frequency](#sorting-attributes-and-style-classes) | `false` |
|
|
182
182
|
| `sortClassNames`<br>`--sort-class-names` | [Sort style classes by frequency](#sorting-attributes-and-style-classes) | `false` |
|
|
183
183
|
| `strictCustomFragments`<br>`--strict-custom-fragments` | [Reject `ignoreCustomFragments` patterns that risk catastrophic backtracking](#redos-protection) (rather than warning about them) | `false` |
|
|
184
|
-
| `trimCustomFragments`<br>`--trim-custom-fragments` | Trim whitespace around custom fragments (`ignoreCustomFragments`)—use with `collapseWhitespace` | `false` |
|
|
184
|
+
| `trimCustomFragments`<br>`--trim-custom-fragments` | Trim whitespace around custom fragments (`ignoreCustomFragments`) where it does not affect the output (see [“Minifying server-side templates”](#minifying-server-side-templates))—use with `collapseWhitespace` | `false` |
|
|
185
185
|
| `useShortDoctype`<br>`--use-short-doctype` | [Replaces the doctype with the short HTML doctype](https://perfectionkills.com/experimenting-with-html-minifier/#use_short_doctype) | `false` |
|
|
186
186
|
|
|
187
187
|
### API-only options
|
|
@@ -680,6 +680,14 @@ For partial HTML fragments (such as template includes, SSI fragments, or closing
|
|
|
680
680
|
|
|
681
681
|
To validate complete HTML markup, use [the W3C validator](https://validator.w3.org/) or one of [several validator packages](https://meiert.com/blog/html-validator-packages/).
|
|
682
682
|
|
|
683
|
+
### Minifying server-side templates
|
|
684
|
+
|
|
685
|
+
By default, `ignoreCustomFragments` preserves `<? … ?>` and `<% … %>` blocks, so PHP, ERB, JSP, and ASP templates can be minified as part of a build, with the markup, CSS, and JavaScript around those blocks being minified as usual. For includes and other partials, add `partialMarkup`.
|
|
686
|
+
|
|
687
|
+
Whitespace next to template blocks is collapsed but kept, as it may matter once the template renders. `trimCustomFragments` removes it where HTML Minifier Next would remove whitespace anyway, as between block-level elements: `<ul> <?php foreach ($items as $item): ?> <li>` loses it, `Hello <?= $name ?>!` keeps it. Tags keep the spacing of their source, and whitespace next to template blocks in attribute values, CSS, and JavaScript is always kept.
|
|
688
|
+
|
|
689
|
+
Where pages are rendered ahead of serving, as with static exports or full-page caches, you can also minify the rendered HTML instead, which makes this a non-issue.
|
|
690
|
+
|
|
683
691
|
### Regex options and flags
|
|
684
692
|
|
|
685
693
|
`customAttrAssign` and `customAttrSurround` patterns are merged into one attribute pattern which carries no flags of its own. `i` and `s` are written into each pattern’s source instead, so they survive the merge. `u`, `v`, and `m` cannot be, and none of them fails loudly when dropped: `u` and `v` only narrow what syntax is legal, so a source valid under either stays valid without it and quietly matches something else—a dropped `u` leaves `\p{L}` matching the literal text `p{L}`—while a dropped `m` leaves `^` and `$` matching at the ends of the input rather than of each line.
|
|
@@ -492,9 +492,10 @@ export type MinifierOptions = {
|
|
|
492
492
|
*/
|
|
493
493
|
sortClassNames?: boolean | ((value: string) => string);
|
|
494
494
|
/**
|
|
495
|
-
* When true, whitespace around ignored custom fragments
|
|
496
|
-
*
|
|
497
|
-
*
|
|
495
|
+
* When true, whitespace around ignored custom fragments is removed rather
|
|
496
|
+
* than collapsed where the minifier removes whitespace anyway (e.g., between
|
|
497
|
+
* block-level elements). Whitespace in tags, attribute values, CSS, and
|
|
498
|
+
* JavaScript is kept.
|
|
498
499
|
*
|
|
499
500
|
* Default: `false`
|
|
500
501
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"htmlminifier.d.ts","sourceRoot":"","sources":["../../src/htmlminifier.js"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"htmlminifier.d.ts","sourceRoot":"","sources":["../../src/htmlminifier.js"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"htmlparser.d.ts","sourceRoot":"","sources":["../../src/htmlparser.js"],"names":[],"mappings":"AAeG,YAYG,iBAAiB,GAZV;IACR,KAAK,CAAC,WAAW;IACjB,GAAG,CAAC,WAAW;IACf,KAAK,CAAC,WAAW;IACjB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,WAAW;IACnB,oBAAoB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC3C,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACpC,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACnC,gBAAgB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,kBAAkB,CAAC,EAAE,MAAM,EAAE,EAAE,GAAG,SAAS,CAAC;IAC5C,gBAAgB,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAA;CACxC,CAAmB;AA0CvB,eAAO,MAAM,MAAM,QAA8C,CAAC;AAoLlE,qBAAa,UAAU;IAMd,IAAI;IACJ,OAAO;IANd;;;OAGG;IACH,YAAY,IAAI,EAHL,MAGK,EAAE,OAAO,EAFd,iBAEc,EAGxB;IAEK,KAAK,
|
|
1
|
+
{"version":3,"file":"htmlparser.d.ts","sourceRoot":"","sources":["../../src/htmlparser.js"],"names":[],"mappings":"AAeG,YAYG,iBAAiB,GAZV;IACR,KAAK,CAAC,WAAW;IACjB,GAAG,CAAC,WAAW;IACf,KAAK,CAAC,WAAW;IACjB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,WAAW;IACnB,oBAAoB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC3C,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACpC,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACnC,gBAAgB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,kBAAkB,CAAC,EAAE,MAAM,EAAE,EAAE,GAAG,SAAS,CAAC;IAC5C,gBAAgB,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAA;CACxC,CAAmB;AA0CvB,eAAO,MAAM,MAAM,QAA8C,CAAC;AAoLlE,qBAAa,UAAU;IAMd,IAAI;IACJ,OAAO;IANd;;;OAGG;IACH,YAAY,IAAI,EAHL,MAGK,EAAE,OAAO,EAFd,iBAEc,EAGxB;IAEK,KAAK,kBA2yBV;CACF"}
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
}
|
|
133
133
|
},
|
|
134
134
|
"ignoreCustomFragments": {
|
|
135
|
-
"description": "Array of regexes that allow to ignore certain fragments, when matched (e.g., `<?php … ?>`, `{{ … }}
|
|
135
|
+
"description": "Array of regexes that allow to ignore certain fragments, when matched (e.g., `<?php … ?>`, `{{ … }}`, etc.)",
|
|
136
136
|
"type": [
|
|
137
137
|
"string",
|
|
138
138
|
"array"
|
|
@@ -253,7 +253,7 @@
|
|
|
253
253
|
"type": "boolean"
|
|
254
254
|
},
|
|
255
255
|
"removeEmptyElementsExcept": {
|
|
256
|
-
"description": "Array of elements to preserve (e.g., `td`, `<span aria-hidden=\"true\"
|
|
256
|
+
"description": "Array of elements to preserve (e.g., `td`, `<span aria-hidden=\"true\">`, etc.)—use with `--remove-empty-elements`",
|
|
257
257
|
"type": [
|
|
258
258
|
"string",
|
|
259
259
|
"array"
|
|
@@ -290,7 +290,7 @@
|
|
|
290
290
|
"type": "boolean"
|
|
291
291
|
},
|
|
292
292
|
"trimCustomFragments": {
|
|
293
|
-
"description": "Trim whitespace around custom fragments (`--ignore-custom-fragments`)—use with `--collapse-whitespace`",
|
|
293
|
+
"description": "Trim whitespace around custom fragments (`--ignore-custom-fragments`) where it does not affect the output—use with `--collapse-whitespace`",
|
|
294
294
|
"type": "boolean"
|
|
295
295
|
},
|
|
296
296
|
"strictCustomFragments": {
|
package/package.json
CHANGED
package/src/htmlminifier.js
CHANGED
|
@@ -512,9 +512,10 @@ import { toFragment, replaceCustomFragments } from './lib/fragments.js';
|
|
|
512
512
|
* Default: `false`
|
|
513
513
|
*
|
|
514
514
|
* @prop {boolean} [trimCustomFragments]
|
|
515
|
-
* When true, whitespace around ignored custom fragments
|
|
516
|
-
*
|
|
517
|
-
*
|
|
515
|
+
* When true, whitespace around ignored custom fragments is removed rather
|
|
516
|
+
* than collapsed where the minifier removes whitespace anyway (e.g., between
|
|
517
|
+
* block-level elements). Whitespace in tags, attribute values, CSS, and
|
|
518
|
+
* JavaScript is kept.
|
|
518
519
|
*
|
|
519
520
|
* Default: `false`
|
|
520
521
|
*
|
|
@@ -623,6 +624,7 @@ const DEFAULT_JS_TYPES = new Set(['', 'text/javascript', 'application/javascript
|
|
|
623
624
|
const RE_START_TAG = /^<[^/!]/;
|
|
624
625
|
const RE_END_TAG = /^<\//;
|
|
625
626
|
const RE_WHITESPACE_ONLY = /^\s+$/;
|
|
627
|
+
const RE_TABS = /\t/g;
|
|
626
628
|
const RE_TRAILING_WHITESPACE = /\s$/;
|
|
627
629
|
|
|
628
630
|
// Pre-compiled patterns for `htmlmin:ignore` block content analysis
|
|
@@ -1065,6 +1067,15 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1065
1067
|
const ignoredMarkupChunks = [];
|
|
1066
1068
|
/** @type {Array<RegExpExecArray | null>} */
|
|
1067
1069
|
const ignoredCustomMarkupChunks = [];
|
|
1070
|
+
// Custom fragments whose surrounding whitespace is never trimmed: in tags, where it
|
|
1071
|
+
// separates attributes, and in CSS and JavaScript, where minifiers can’t judge it
|
|
1072
|
+
/** @type {Set<number>} */
|
|
1073
|
+
const customMarkupInTags = new Set();
|
|
1074
|
+
/** @type {Set<number>} */
|
|
1075
|
+
const customMarkupInCode = new Set();
|
|
1076
|
+
// Attributes joined with the custom fragments they run into, to be written as they are
|
|
1077
|
+
/** @type {WeakSet<HTMLAttribute>} */
|
|
1078
|
+
const opaqueAttrs = new WeakSet();
|
|
1068
1079
|
/** @type {string | undefined} */
|
|
1069
1080
|
let uidIgnore;
|
|
1070
1081
|
/** @type {RegExp | undefined} */
|
|
@@ -1075,6 +1086,8 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1075
1086
|
let uidPattern;
|
|
1076
1087
|
/** @type {RegExp | undefined} */
|
|
1077
1088
|
let uidAttrLeadingPattern;
|
|
1089
|
+
/** @type {RegExp | undefined} */
|
|
1090
|
+
let uidAttrExactPattern;
|
|
1078
1091
|
// Create inline tags/text sets with custom elements
|
|
1079
1092
|
const customElementsInput = options.inlineCustomElements ?? [];
|
|
1080
1093
|
const customElementsArr = Array.isArray(customElementsInput) ? customElementsInput : Array.from(customElementsInput);
|
|
@@ -1149,12 +1162,14 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1149
1162
|
uidAttr = uniqueId(value);
|
|
1150
1163
|
uidPattern = new RegExp('(\\s*)' + uidAttr + '([0-9]+)' + uidAttr + '(\\s*)', 'g');
|
|
1151
1164
|
uidAttrLeadingPattern = new RegExp('^\\s*' + uidAttr + '(\\d+)' + uidAttr);
|
|
1165
|
+
uidAttrExactPattern = new RegExp('^' + uidAttr + '(\\d+)' + uidAttr + '$');
|
|
1152
1166
|
|
|
1153
1167
|
if (options.minifyCSS !== identity) {
|
|
1154
1168
|
options.minifyCSS = (function (/** @type {ProcessedOptions['minifyCSS']} */ fn) {
|
|
1155
1169
|
return function (/** @type {string} */ text, /** @type {string | undefined} */ type, /** @type {ProcessedOptions['cssContext']} */ context) {
|
|
1156
1170
|
text = text.replace(/** @type {RegExp} */ (uidPattern), function (/** @type {string} */ _match, /** @type {string} */ _prefix, /** @type {string} */ index) {
|
|
1157
1171
|
const chunks = ignoredCustomMarkupChunks[+index];
|
|
1172
|
+
customMarkupInCode.add(+index);
|
|
1158
1173
|
return (chunks?.[1] ?? '') + uidAttr + index + uidAttr + (chunks?.[2] ?? '');
|
|
1159
1174
|
});
|
|
1160
1175
|
|
|
@@ -1168,6 +1183,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1168
1183
|
return function (/** @type {string} */ text, /** @type {boolean | undefined} */ inline, /** @type {boolean | undefined} */ isModule) {
|
|
1169
1184
|
return fn(text.replace(/** @type {RegExp} */ (uidPattern), function (/** @type {string} */ _match, /** @type {string} */ _prefix, /** @type {string} */ index) {
|
|
1170
1185
|
const chunks = ignoredCustomMarkupChunks[+index];
|
|
1186
|
+
customMarkupInCode.add(+index);
|
|
1171
1187
|
return (chunks?.[1] ?? '') + uidAttr + index + uidAttr + (chunks?.[2] ?? '');
|
|
1172
1188
|
}), inline, isModule);
|
|
1173
1189
|
};
|
|
@@ -1183,6 +1199,77 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1183
1199
|
value = replaceCustomFragments(value, customFragments, replaceFragment);
|
|
1184
1200
|
}
|
|
1185
1201
|
|
|
1202
|
+
// Record the custom fragments in (part of) a tag
|
|
1203
|
+
function markTagFragments(/** @type {string} */ str) {
|
|
1204
|
+
for (const uidMatch of str.matchAll(/** @type {RegExp} */ (uidPattern))) {
|
|
1205
|
+
customMarkupInTags.add(+(uidMatch[2] ?? 0));
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
// Source text of an attribute, as far as it matters
|
|
1210
|
+
function rawAttr(/** @type {HTMLAttribute} */ attr) {
|
|
1211
|
+
if (opaqueAttrs.has(attr)) {
|
|
1212
|
+
return attr.name;
|
|
1213
|
+
}
|
|
1214
|
+
let raw = attr.name;
|
|
1215
|
+
// Skip values the parser filled in for bare boolean attributes
|
|
1216
|
+
if (attr.value !== undefined && !(attr.quote === '' && attr.value === attr.name)) {
|
|
1217
|
+
raw += (attr.customAssign ?? '=') + (attr.quote ?? '') + attr.value + (attr.quote ?? '');
|
|
1218
|
+
}
|
|
1219
|
+
return (attr.customOpen ?? '') + raw + (attr.customClose ?? '');
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
/**
|
|
1223
|
+
* Join each attribute a custom fragment runs into without whitespace with that fragment,
|
|
1224
|
+
* so that no later step separates, drops, changes, or reorders them
|
|
1225
|
+
* @param {HTMLAttribute[]} attrs - Joined in place
|
|
1226
|
+
* @returns {boolean} Whether the first attribute runs into the tag name
|
|
1227
|
+
*/
|
|
1228
|
+
function joinFragmentAttrs(attrs) {
|
|
1229
|
+
const pattern = /** @type {RegExp} */ (uidAttrExactPattern);
|
|
1230
|
+
let joinsTag = false;
|
|
1231
|
+
let writeIndex = 0;
|
|
1232
|
+
// Padding of the previous attribute if that is a fragment without value
|
|
1233
|
+
/** @type {RegExpExecArray | null | undefined} */
|
|
1234
|
+
let prevChunks = null;
|
|
1235
|
+
for (const attr of attrs) {
|
|
1236
|
+
const uidMatch = pattern.exec(attr.name);
|
|
1237
|
+
const chunks = uidMatch ? ignoredCustomMarkupChunks[+(uidMatch[1] ?? 0)] : null;
|
|
1238
|
+
const joins = prevChunks ? prevChunks[2] === '' : chunks?.[1] === '';
|
|
1239
|
+
prevChunks = attr.value === undefined ? chunks : null;
|
|
1240
|
+
if (!joins) {
|
|
1241
|
+
attrs[writeIndex++] = attr;
|
|
1242
|
+
continue;
|
|
1243
|
+
}
|
|
1244
|
+
const prev = writeIndex ? attrs[writeIndex - 1] : undefined;
|
|
1245
|
+
if (!prev) {
|
|
1246
|
+
joinsTag = true;
|
|
1247
|
+
}
|
|
1248
|
+
/** @type {HTMLAttribute} */
|
|
1249
|
+
const joined = { name: (prev ? rawAttr(prev) : '') + rawAttr(attr), customAssign: '=', customOpen: '', customClose: '', quote: '' };
|
|
1250
|
+
opaqueAttrs.add(joined);
|
|
1251
|
+
attrs[prev ? writeIndex - 1 : writeIndex++] = joined;
|
|
1252
|
+
}
|
|
1253
|
+
attrs.length = writeIndex;
|
|
1254
|
+
return joinsTag;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
/**
|
|
1258
|
+
* Restore the whitespace on one side of a custom fragment
|
|
1259
|
+
* @param {string} str - Whitespace the minifier left there and the fragment’s own padding
|
|
1260
|
+
* @param {boolean} trim
|
|
1261
|
+
* @param {boolean} leading
|
|
1262
|
+
*/
|
|
1263
|
+
function restoreFragmentSide(str, trim, leading) {
|
|
1264
|
+
if (!str) {
|
|
1265
|
+
return str;
|
|
1266
|
+
}
|
|
1267
|
+
return collapseWhitespace(str, {
|
|
1268
|
+
preserveLineBreaks: options.preserveLineBreaks,
|
|
1269
|
+
conservativeCollapse: !trim
|
|
1270
|
+
}, leading, !leading);
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1186
1273
|
function canCollapseWhitespace(/** @type {string} */ tag, /** @type {HTMLAttribute[]} */ attrs) {
|
|
1187
1274
|
return options.canCollapseWhitespace(tag, attrs, defaultCanCollapseWhitespace);
|
|
1188
1275
|
}
|
|
@@ -1754,6 +1841,10 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1754
1841
|
|
|
1755
1842
|
buffer.push(openTag);
|
|
1756
1843
|
|
|
1844
|
+
const fragmentJoinsTag = uidAttrExactPattern ? joinFragmentAttrs(attrs) : false;
|
|
1845
|
+
// Whatever runs into the tag name has to stay first
|
|
1846
|
+
const tagFragment = fragmentJoinsTag ? attrs.shift() : undefined;
|
|
1847
|
+
|
|
1757
1848
|
// Remove duplicate attributes (per HTML spec, first occurrence wins)
|
|
1758
1849
|
// Duplicate attributes result in invalid HTML
|
|
1759
1850
|
// https://html.spec.whatwg.org/multipage/parsing.html#attribute-name-state
|
|
@@ -1763,11 +1854,17 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1763
1854
|
// By the time tags are processed, `createSortFns` has replaced any truthy non-function value
|
|
1764
1855
|
/** @type {(tag: string, attrs: HTMLAttribute[]) => void} */ (options.sortAttributes)(tag, attrs);
|
|
1765
1856
|
}
|
|
1857
|
+
if (tagFragment) {
|
|
1858
|
+
attrs.unshift(tagFragment);
|
|
1859
|
+
}
|
|
1766
1860
|
|
|
1767
1861
|
const attrResults = new Array(attrs.length);
|
|
1768
1862
|
let anyThenable = false;
|
|
1769
1863
|
for (let i = 0; i < attrs.length; i++) {
|
|
1770
|
-
const
|
|
1864
|
+
const attr = /** @type {HTMLAttribute} */ (attrs[i]);
|
|
1865
|
+
const result = opaqueAttrs.has(attr)
|
|
1866
|
+
? { name: attr.name, value: undefined, attr }
|
|
1867
|
+
: normalizeAttr(attr, attrs, tag, options, minifyHTML);
|
|
1771
1868
|
if (!anyThenable && isThenable(result)) {
|
|
1772
1869
|
anyThenable = true;
|
|
1773
1870
|
}
|
|
@@ -1785,11 +1882,18 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1785
1882
|
}
|
|
1786
1883
|
}
|
|
1787
1884
|
if (lastKeptIndex !== -1) {
|
|
1788
|
-
|
|
1885
|
+
if (!fragmentJoinsTag) {
|
|
1886
|
+
buffer.push(' ');
|
|
1887
|
+
}
|
|
1789
1888
|
for (let i = 0; i <= lastKeptIndex; i++) {
|
|
1790
1889
|
const normalizedAttr = normalizedAttrs[i];
|
|
1791
1890
|
if (normalizedAttr) {
|
|
1792
|
-
|
|
1891
|
+
if (uidAttr && normalizedAttr.name.includes(uidAttr)) {
|
|
1892
|
+
markTagFragments(normalizedAttr.name);
|
|
1893
|
+
}
|
|
1894
|
+
const builtAttr = buildAttr(normalizedAttr, hasUnarySlash, options, i === lastKeptIndex, uidAttr);
|
|
1895
|
+
// One entry with the tag name, so that no line break can come between them
|
|
1896
|
+
buffer.push(i === 0 && fragmentJoinsTag ? buffer.pop() + builtAttr : builtAttr);
|
|
1793
1897
|
}
|
|
1794
1898
|
}
|
|
1795
1899
|
} else if (optional && optionalStartTags.has(tag)) {
|
|
@@ -1815,7 +1919,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1815
1919
|
currentTag = '';
|
|
1816
1920
|
}
|
|
1817
1921
|
},
|
|
1818
|
-
end: function (/** @type {string} */ tag, /** @type {HTMLAttribute[]} */ attrs, /** @type {boolean} */ autoGenerated) {
|
|
1922
|
+
end: function (/** @type {string} */ tag, /** @type {HTMLAttribute[]} */ attrs, /** @type {boolean} */ autoGenerated, /** @type {string | undefined} */ rest) {
|
|
1819
1923
|
// As in `start`: `lowerTag` stays '' when no foreign content is around
|
|
1820
1924
|
let lowerTag = '';
|
|
1821
1925
|
// Named as the start tag was: an `svg` or `math` element in its own context,
|
|
@@ -1833,6 +1937,18 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1833
1937
|
}
|
|
1834
1938
|
}
|
|
1835
1939
|
tag = name(tag);
|
|
1940
|
+
let endTagText = '</' + tag + '>';
|
|
1941
|
+
// Custom fragments after the name are kept, next to it if they were in the source
|
|
1942
|
+
if (rest && uidAttr && rest.includes(uidAttr)) {
|
|
1943
|
+
markTagFragments(rest);
|
|
1944
|
+
const uidMatch = /** @type {RegExp} */ (uidAttrLeadingPattern).exec(rest);
|
|
1945
|
+
const joinsTag = uidMatch !== null && rest.startsWith('\t' + uidAttr) &&
|
|
1946
|
+
ignoredCustomMarkupChunks[+(uidMatch[1] ?? 0)]?.[1] === '';
|
|
1947
|
+
// Anything else an end tag holds is dropped, as without fragments
|
|
1948
|
+
const marker = uidAttr;
|
|
1949
|
+
const fragments = Array.from(rest.matchAll(/** @type {RegExp} */ (uidPattern)), uid => marker + (uid[2] ?? '') + marker);
|
|
1950
|
+
endTagText = '</' + tag + (joinsTag ? '' : ' ') + fragments.join(' ') + '>';
|
|
1951
|
+
}
|
|
1836
1952
|
|
|
1837
1953
|
// Check if current tag is in a whitespace stack
|
|
1838
1954
|
if (options.collapseWhitespace) {
|
|
@@ -1894,7 +2010,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1894
2010
|
if (autoGenerated && !options.includeAutoGeneratedTags) {
|
|
1895
2011
|
optionalEndTagEmitted = false;
|
|
1896
2012
|
} else {
|
|
1897
|
-
buffer.push(
|
|
2013
|
+
buffer.push(endTagText);
|
|
1898
2014
|
}
|
|
1899
2015
|
charsPrevTag = '/' + tag;
|
|
1900
2016
|
if (!inlineElements.has(tag)) {
|
|
@@ -1907,7 +2023,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1907
2023
|
if (autoGenerated && !options.includeAutoGeneratedTags) {
|
|
1908
2024
|
optionalEndTagEmitted = false;
|
|
1909
2025
|
} else {
|
|
1910
|
-
buffer.push(
|
|
2026
|
+
buffer.push(endTagText);
|
|
1911
2027
|
}
|
|
1912
2028
|
charsPrevTag = '/' + tag;
|
|
1913
2029
|
if (!inlineElements.has(tag)) {
|
|
@@ -2024,21 +2140,26 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
2024
2140
|
|
|
2025
2141
|
return joinResultSegments(buffer, options, uidPattern
|
|
2026
2142
|
? function (/** @type {string} */ str) {
|
|
2027
|
-
return str.replace(/** @type {RegExp} */ (uidPattern), function (/** @type {string} */ match, /** @type {string} */ prefix, /** @type {string} */ index, /** @type {string} */ suffix) {
|
|
2028
|
-
|
|
2029
|
-
if (
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
chunk += suffix;
|
|
2035
|
-
}
|
|
2036
|
-
return collapseWhitespace(chunk, {
|
|
2037
|
-
preserveLineBreaks: options.preserveLineBreaks,
|
|
2038
|
-
conservativeCollapse: !options.trimCustomFragments
|
|
2039
|
-
}, /^[ \n\r\t\f]/.test(chunk), /[ \n\r\t\f]$/.test(chunk));
|
|
2143
|
+
return str.replace(/** @type {RegExp} */ (uidPattern), function (/** @type {string} */ match, /** @type {string} */ prefix, /** @type {string} */ index, /** @type {string} */ suffix, /** @type {number} */ offset, /** @type {string} */ string) {
|
|
2144
|
+
const chunks = ignoredCustomMarkupChunks[+index];
|
|
2145
|
+
if (!chunks) {
|
|
2146
|
+
return match;
|
|
2147
|
+
}
|
|
2148
|
+
if (!options.collapseWhitespace) {
|
|
2149
|
+
return chunks[0];
|
|
2040
2150
|
}
|
|
2041
|
-
|
|
2151
|
+
const leading = chunks[1] ?? '';
|
|
2152
|
+
const trailing = chunks[2] ?? '';
|
|
2153
|
+
const fragment = chunks[0].slice(leading.length, chunks[0].length - trailing.length);
|
|
2154
|
+
const inTag = customMarkupInTags.has(+index);
|
|
2155
|
+
const keep = inTag || customMarkupInCode.has(+index) || !options.trimCustomFragments;
|
|
2156
|
+
// The padding tabs are whitespace to the minifier, so where one is gone, the
|
|
2157
|
+
// minifier removed the whitespace, which makes it safe to trim
|
|
2158
|
+
const trimLeading = !keep && prefix === '';
|
|
2159
|
+
const trimTrailing = inTag ? string.charCodeAt(offset + match.length) === 62 /* > */ : !keep && suffix === '';
|
|
2160
|
+
return restoreFragmentSide(prefix.replace(RE_TABS, '') + leading, trimLeading, true) +
|
|
2161
|
+
fragment +
|
|
2162
|
+
restoreFragmentSide(suffix.replace(RE_TABS, '') + trailing, trimTrailing, false);
|
|
2042
2163
|
});
|
|
2043
2164
|
}
|
|
2044
2165
|
: identity, uidIgnore
|
package/src/htmlparser.js
CHANGED
|
@@ -1033,11 +1033,14 @@ export class HTMLParser {
|
|
|
1033
1033
|
stackIndex = 0;
|
|
1034
1034
|
}
|
|
1035
1035
|
|
|
1036
|
+
// Whatever follows the name, which browsers drop but a custom fragment may stand in for
|
|
1037
|
+
const rest = tag.length > tagName.length + 3 ? tag.slice(tagName.length + 2, -1) : '';
|
|
1038
|
+
|
|
1036
1039
|
if (stackIndex >= 0) {
|
|
1037
1040
|
// Close all the open elements, up the stack
|
|
1038
1041
|
for (let i = stack.length - 1; i >= stackIndex; i--) {
|
|
1039
1042
|
if (handler.end) {
|
|
1040
|
-
handler.end(stack[i]?.tag, stack[i]?.attrs, i > stackIndex || !tag);
|
|
1043
|
+
handler.end(stack[i]?.tag, stack[i]?.attrs, i > stackIndex || !tag, i === stackIndex ? rest : '');
|
|
1041
1044
|
}
|
|
1042
1045
|
}
|
|
1043
1046
|
|
|
@@ -1048,7 +1051,7 @@ export class HTMLParser {
|
|
|
1048
1051
|
} else if (handler.partialMarkup && tagName) {
|
|
1049
1052
|
// In partial markup mode, preserve stray end tags
|
|
1050
1053
|
if (handler.end) {
|
|
1051
|
-
handler.end(tagName, [], false);
|
|
1054
|
+
handler.end(tagName, [], false, rest);
|
|
1052
1055
|
}
|
|
1053
1056
|
} else if (lowerTagName === 'br') {
|
|
1054
1057
|
if (handler.start) {
|
|
@@ -81,7 +81,7 @@ const optionDefinitions = {
|
|
|
81
81
|
type: 'regexpArray'
|
|
82
82
|
},
|
|
83
83
|
ignoreCustomFragments: {
|
|
84
|
-
description: 'Array of regexes that allow to ignore certain fragments, when matched (e.g., `<?php \u2026 ?>`, `{{ \u2026 }}
|
|
84
|
+
description: 'Array of regexes that allow to ignore certain fragments, when matched (e.g., `<?php \u2026 ?>`, `{{ \u2026 }}`, etc.)',
|
|
85
85
|
type: 'regexpArray'
|
|
86
86
|
},
|
|
87
87
|
includeAutoGeneratedTags: {
|
|
@@ -169,7 +169,7 @@ const optionDefinitions = {
|
|
|
169
169
|
type: 'boolean'
|
|
170
170
|
},
|
|
171
171
|
removeEmptyElementsExcept: {
|
|
172
|
-
description: 'Array of elements to preserve (e.g., `td`, `<span aria-hidden="true"
|
|
172
|
+
description: 'Array of elements to preserve (e.g., `td`, `<span aria-hidden="true">`, etc.)—use with `--remove-empty-elements`',
|
|
173
173
|
type: 'jsonArray'
|
|
174
174
|
},
|
|
175
175
|
removeOptionalTags: {
|
|
@@ -197,7 +197,7 @@ const optionDefinitions = {
|
|
|
197
197
|
type: 'boolean'
|
|
198
198
|
},
|
|
199
199
|
trimCustomFragments: {
|
|
200
|
-
description: 'Trim whitespace around custom fragments (`--ignore-custom-fragments`)—use with `--collapse-whitespace`',
|
|
200
|
+
description: 'Trim whitespace around custom fragments (`--ignore-custom-fragments`) where it does not affect the output—use with `--collapse-whitespace`',
|
|
201
201
|
type: 'boolean'
|
|
202
202
|
},
|
|
203
203
|
strictCustomFragments: {
|