html-minifier-next 8.5.1 → 8.5.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 +2 -2
- package/dist/types/htmlminifier.d.ts +4 -3
- package/dist/types/htmlminifier.d.ts.map +1 -1
- package/dist/types/htmlparser.d.ts +0 -1
- package/dist/types/htmlparser.d.ts.map +1 -1
- package/dist/types/lib/constants.d.ts +5 -2
- package/dist/types/lib/constants.d.ts.map +1 -1
- package/dist/types/lib/whitespace.d.ts +44 -4
- package/dist/types/lib/whitespace.d.ts.map +1 -1
- package/html-minifier-next.schema.json +2 -2
- package/package.json +4 -4
- package/src/htmlminifier.js +123 -52
- package/src/htmlparser.js +0 -1
- package/src/lib/constants.js +12 -1
- package/src/lib/option-definitions.js +2 -2
- package/src/lib/whitespace.js +58 -16
package/README.md
CHANGED
|
@@ -156,13 +156,13 @@ Options can be used in config files (camelCase) or via CLI flags (kebab-case wit
|
|
|
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` |
|
|
158
158
|
| `maxInputLength`<br>`--max-input-length` | Maximum input length to prevent ReDoS attacks (disabled by default) | `undefined` |
|
|
159
|
-
| `maxLineLength`<br>`--max-line-length` | Specify a maximum line length;
|
|
159
|
+
| `maxLineLength`<br>`--max-line-length` | Specify a maximum line length; output will be split by newlines in tags only (between attributes or before a tag’s `>`), where they can’t change the page, so lines may run longer | `undefined` |
|
|
160
160
|
| `mergeScripts`<br>`--merge-scripts` | Merge consecutive inline `script` elements into one (only merges compatible scripts with same `type`, matching `async`/`defer`/`nomodule`/`nonce`) | `false` |
|
|
161
161
|
| `minifyCSS`<br>`--minify-css` | Minify CSS in `style` elements and attributes (uses [Lightning CSS](https://lightningcss.dev/)) | `false` (could be `true`, `Object`, `Function(text, type)`) |
|
|
162
162
|
| `minifyJS`<br>`--minify-js` | Minify JavaScript in `script` elements and event attributes (uses [Terser](https://terser.org/) or [SWC](https://swc.rs/)) | `false` (could be `true`, `Object`, `Function(text, inline)`) |
|
|
163
163
|
| `minifySVG`<br>`--minify-svg` | Minify SVG elements (uses [SVGO](https://svgo.dev/)) | `false` (could be `true`, `Object`) |
|
|
164
164
|
| `minifyURLs`<br>`--minify-urls` | Minify URLs in various attributes | `false` (could be `true`, `String`, `Object`, `Function(text)`) |
|
|
165
|
-
| `noNewlinesBeforeTagClose`<br>`--no-newlines-before-tag-close` | Never
|
|
165
|
+
| `noNewlinesBeforeTagClose`<br>`--no-newlines-before-tag-close` | Never split a line in a tag that closes an element—use with `maxLineLength` | `false` |
|
|
166
166
|
| `partialMarkup`<br>`--partial-markup` | Treat input as a partial HTML fragment, preserving stray end tags (closing tags without opening tags) and preventing auto-closing of unclosed tags at end of input | `false` |
|
|
167
167
|
| `preserveLineBreaks`<br>`--preserve-line-breaks` | Always collapse to one line break (never remove it entirely) when whitespace between tags includes a line break—use with [`collapseWhitespace`](#combining-whitespace-options) | `false` |
|
|
168
168
|
| `preventAttributesEscaping`<br>`--prevent-attributes-escaping` | Prevents the escaping of the values of attributes | `false` |
|
|
@@ -244,7 +244,8 @@ export type MinifierOptions = {
|
|
|
244
244
|
maxInputLength?: number;
|
|
245
245
|
/**
|
|
246
246
|
* Maximum line length for the output. When set the minifier will wrap
|
|
247
|
-
* output to the given number of characters where possible
|
|
247
|
+
* output to the given number of characters where possible, splitting
|
|
248
|
+
* lines only in tags—between attributes or before a tag’s `>`.
|
|
248
249
|
*
|
|
249
250
|
* Default: No limit
|
|
250
251
|
*/
|
|
@@ -308,8 +309,8 @@ export type MinifierOptions = {
|
|
|
308
309
|
*/
|
|
309
310
|
minifySVG?: boolean | Object;
|
|
310
311
|
/**
|
|
311
|
-
* When wrapping lines,
|
|
312
|
-
*
|
|
312
|
+
* When wrapping lines, never split a line in an end tag (e.g., keeps
|
|
313
|
+
* `</a>` from becoming `</a` and `>` on two lines).
|
|
313
314
|
*
|
|
314
315
|
* Default: `false`
|
|
315
316
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"htmlminifier.d.ts","sourceRoot":"","sources":["../../src/htmlminifier.js"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;
|
|
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;
|
|
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;AA6NvB,qBAAa,UAAU;IAMd,IAAI;IACJ,OAAO;IANd;;;OAGG;IACH,YAAY,IAAI,EAHL,MAGK,EAAE,OAAO,EAFd,iBAEc,EAGxB;IAEK,KAAK,kBA2yBV;CACF"}
|
|
@@ -26,7 +26,10 @@ declare const RE_EMPTY_ATTRIBUTE: RegExp;
|
|
|
26
26
|
declare const inlineElementsToKeepWhitespaceAround: Set<string>;
|
|
27
27
|
declare const inlineElementsToKeepWhitespaceWithin: Set<string>;
|
|
28
28
|
declare const inlineElementsToKeepWhitespace: Set<string>;
|
|
29
|
-
|
|
29
|
+
/** @param {Iterable<string>} names */
|
|
30
|
+
declare const toEndTags: (names: Iterable<string>) => Set<string>;
|
|
31
|
+
declare const inlineElementsToKeepWhitespaceWithinEither: Set<string>;
|
|
32
|
+
declare const formControlElementsEither: Set<string>;
|
|
30
33
|
declare const generalDefaults: {
|
|
31
34
|
autocorrect: string;
|
|
32
35
|
fetchpriority: string;
|
|
@@ -124,5 +127,5 @@ declare const svgoPluginsInline: {
|
|
|
124
127
|
};
|
|
125
128
|
}[];
|
|
126
129
|
declare const namesForeignMixedCase: Map<string, string>;
|
|
127
|
-
export { RE_WS_START, RE_WS_END, RE_WS_ONLY, RE_WS_NBSP_END, RE_ALL_WS_NBSP, RE_NBSP_LEADING_GROUP, RE_NBSP_LEAD_GROUP, RE_NBSP_TRAILING_GROUP, RE_NBSP_TRAILING_STRIP, RE_EVENT_ATTR_DEFAULT, RE_CAN_REMOVE_ATTR_QUOTES, RE_TRAILING_SEMICOLON, RE_AMP_ENTITY, RE_LEGACY_ENTITIES, RE_ESCAPE_LT, RE_ESCAPE_LT_RAW_TEXT, RE_HTML_ENCODING, RE_ATTR_WS_CHECK, RE_ATTR_WS_COLLAPSE, RE_ATTR_WS_TRIM, RE_STYLE_ELEMENT, RE_EMPTY_ATTRIBUTE, inlineElementsToKeepWhitespaceAround, inlineElementsToKeepWhitespaceWithin, inlineElementsToKeepWhitespace,
|
|
130
|
+
export { RE_WS_START, RE_WS_END, RE_WS_ONLY, RE_WS_NBSP_END, RE_ALL_WS_NBSP, RE_NBSP_LEADING_GROUP, RE_NBSP_LEAD_GROUP, RE_NBSP_TRAILING_GROUP, RE_NBSP_TRAILING_STRIP, RE_EVENT_ATTR_DEFAULT, RE_CAN_REMOVE_ATTR_QUOTES, RE_TRAILING_SEMICOLON, RE_AMP_ENTITY, RE_LEGACY_ENTITIES, RE_ESCAPE_LT, RE_ESCAPE_LT_RAW_TEXT, RE_HTML_ENCODING, RE_ATTR_WS_CHECK, RE_ATTR_WS_COLLAPSE, RE_ATTR_WS_TRIM, RE_STYLE_ELEMENT, RE_EMPTY_ATTRIBUTE, inlineElementsToKeepWhitespaceAround, inlineElementsToKeepWhitespaceWithin, inlineElementsToKeepWhitespace, inlineElementsToKeepWhitespaceWithinEither, formControlElementsEither, toEndTags, generalDefaults, tagDefaults, executableScriptsMimetypes, keepScriptsMimetypes, jsonScriptTypes, isSimpleBoolean, isBooleanValue, collapsibleValues, srcsetElements, MISSING_DEPENDENCY, namesForeignMixedCase, svgoPluginsInline, optionalStartTags, optionalEndTags, headerElements, descriptionElements, pBlockElements, pInlineElements, rubyEndTagOmission, rubyRtcEndTagOmission, optionElements, tableContentElements, tableSectionElements, cellElements, topLevelElements, compactElements, looseElements, trailingElements, htmlElements, specialContentElements, genericRawTextElements, endlessRawTextElements, escapableRawTextElements, rawTextElements };
|
|
128
131
|
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/lib/constants.js"],"names":[],"mappings":"AAAA;;GAEG;AAEH,QAAA,MAAM,WAAW,QAAkB,CAAC;AACpC,QAAA,MAAM,SAAS,QAAkB,CAAC;AAClC,QAAA,MAAM,UAAU,QAAmB,CAAC;AACpC,QAAA,MAAM,cAAc,QAAqB,CAAC;AAC1C,QAAA,MAAM,cAAc,QAAsB,CAAC;AAC3C,QAAA,MAAM,qBAAqB,QAAuB,CAAC;AACnD,QAAA,MAAM,kBAAkB,QAAqB,CAAC;AAC9C,QAAA,MAAM,sBAAsB,QAAqB,CAAC;AAClD,QAAA,MAAM,sBAAsB,QAAc,CAAC;AAC3C,QAAA,MAAM,qBAAqB,QAAkB,CAAC;AAC9C,QAAA,MAAM,yBAAyB,QAA0B,CAAC;AAC1D,QAAA,MAAM,qBAAqB,QAAO,CAAC;AACnC,QAAA,MAAM,aAAa,QAAwB,CAAC;AAC5C,QAAA,MAAM,kBAAkB,QAA+oB,CAAC;AACxqB,QAAA,MAAM,YAAY,QAAO,CAAC;AAK1B,QAAA,MAAM,qBAAqB,EAAc,MAAM,CAAC,MAAM,EAAE,MAAM,CAG3D,CAAC;AAGJ,QAAA,MAAM,gBAAgB,QAA4C,CAAC;AACnE,QAAA,MAAM,gBAAgB,QAAgB,CAAC;AACvC,QAAA,MAAM,mBAAmB,QAAkB,CAAC;AAC5C,QAAA,MAAM,eAAe,QAAiC,CAAC;AACvD,QAAA,MAAM,gBAAgB,QAAkB,CAAC;AACzC,QAAA,MAAM,kBAAkB,QAE6B,CAAC;AAKtD,QAAA,MAAM,oCAAoC,aAAqY,CAAC;AAGhb,QAAA,MAAM,oCAAoC,aAAkM,CAAC;AAG7O,QAAA,MAAM,8BAA8B,aAA8C,CAAC;
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/lib/constants.js"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
/** @param {string} str */
|
|
5
5
|
declare const trimWhitespace: (str: string) => string;
|
|
6
6
|
/** @param {string} str */
|
|
7
|
+
declare function endsWithWhitespace(str: string): boolean;
|
|
8
|
+
/** @param {string} str */
|
|
7
9
|
declare function collapseWhitespaceAll(str: string): string;
|
|
8
10
|
/**
|
|
9
11
|
* @param {string} str
|
|
@@ -16,6 +18,45 @@ declare function collapseWhitespace(str: string, options: {
|
|
|
16
18
|
preserveLineBreaks?: boolean | undefined;
|
|
17
19
|
conservativeCollapse?: boolean | undefined;
|
|
18
20
|
}, trimLeft: boolean, trimRight: boolean, collapseAll?: boolean): string;
|
|
21
|
+
export type InlineSets = {
|
|
22
|
+
/**
|
|
23
|
+
* - Elements whose surrounding whitespace is kept
|
|
24
|
+
*/
|
|
25
|
+
around: Set<string>;
|
|
26
|
+
/**
|
|
27
|
+
* - The same, spelled as end tags
|
|
28
|
+
*/
|
|
29
|
+
aroundEnd: Set<string>;
|
|
30
|
+
/**
|
|
31
|
+
* - Elements whose inner whitespace is kept
|
|
32
|
+
*/
|
|
33
|
+
within: Set<string>;
|
|
34
|
+
/**
|
|
35
|
+
* - The same, spelled as end tags
|
|
36
|
+
*/
|
|
37
|
+
withinEnd: Set<string>;
|
|
38
|
+
/**
|
|
39
|
+
* - `within` under both spellings
|
|
40
|
+
*/
|
|
41
|
+
withinEither: Set<string>;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* @typedef {object} InlineSets
|
|
45
|
+
* @prop {Set<string>} around - Elements whose surrounding whitespace is kept
|
|
46
|
+
* @prop {Set<string>} aroundEnd - The same, spelled as end tags
|
|
47
|
+
* @prop {Set<string>} within - Elements whose inner whitespace is kept
|
|
48
|
+
* @prop {Set<string>} withinEnd - The same, spelled as end tags
|
|
49
|
+
* @prop {Set<string>} withinEither - `within` under both spellings
|
|
50
|
+
*/
|
|
51
|
+
/**
|
|
52
|
+
* Inline-element membership for the whitespace pass, each set paired with its end-tag
|
|
53
|
+
* spelling so a lookup takes the tag as it comes rather than slicing off the slash
|
|
54
|
+
* @param {Set<string>} around
|
|
55
|
+
* @param {Set<string>} within
|
|
56
|
+
* @returns {InlineSets}
|
|
57
|
+
*/
|
|
58
|
+
declare function buildInlineSets(around: Set<string>, within: Set<string>): InlineSets;
|
|
59
|
+
declare const defaultInlineSets: InlineSets;
|
|
19
60
|
/**
|
|
20
61
|
* @param {string} str
|
|
21
62
|
* @param {string} prevTag
|
|
@@ -23,8 +64,7 @@ declare function collapseWhitespace(str: string, options: {
|
|
|
23
64
|
* @param {Array<{name: string, value?: string}>} prevAttrs
|
|
24
65
|
* @param {Array<{name: string, value?: string}>} nextAttrs
|
|
25
66
|
* @param {{preserveLineBreaks?: boolean, conservativeCollapse?: boolean, collapseInlineTagWhitespace?: boolean}} options
|
|
26
|
-
* @param {
|
|
27
|
-
* @param {Set<string>} inlineTextSet
|
|
67
|
+
* @param {InlineSets} inlineSets
|
|
28
68
|
*/
|
|
29
69
|
declare function collapseWhitespaceSmart(str: string, prevTag: string, nextTag: string, prevAttrs: Array<{
|
|
30
70
|
name: string;
|
|
@@ -36,10 +76,10 @@ declare function collapseWhitespaceSmart(str: string, prevTag: string, nextTag:
|
|
|
36
76
|
preserveLineBreaks?: boolean;
|
|
37
77
|
conservativeCollapse?: boolean;
|
|
38
78
|
collapseInlineTagWhitespace?: boolean;
|
|
39
|
-
},
|
|
79
|
+
}, inlineSets: InlineSets): string;
|
|
40
80
|
/** @param {string} tag */
|
|
41
81
|
declare function canCollapseWhitespace(tag: string): boolean;
|
|
42
82
|
/** @param {string} tag */
|
|
43
83
|
declare function canTrimWhitespace(tag: string): boolean;
|
|
44
|
-
export { trimWhitespace, collapseWhitespaceAll, collapseWhitespace, collapseWhitespaceSmart, canCollapseWhitespace, canTrimWhitespace };
|
|
84
|
+
export { buildInlineSets, defaultInlineSets, endsWithWhitespace, trimWhitespace, collapseWhitespaceAll, collapseWhitespace, collapseWhitespaceSmart, canCollapseWhitespace, canTrimWhitespace };
|
|
45
85
|
//# sourceMappingURL=whitespace.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"whitespace.d.ts","sourceRoot":"","sources":["../../../src/lib/whitespace.js"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"whitespace.d.ts","sourceRoot":"","sources":["../../../src/lib/whitespace.js"],"names":[],"mappings":"AAAA;;GAEG;AAyBH,0BAA0B;AAC1B,QAAA,MAAM,cAAc,QADR,MAAM,WAQjB,CAAC;AAaF,0BAA0B;AAC1B,iBAAS,kBAAkB,CAAC,GAAG,EADnB,MACmB,WAE9B;AAaD,0BAA0B;AAC1B,iBAAS,qBAAqB,CAAC,GAAG,EADtB,MACsB,UAOjC;AAoBD;;;;;;GAMG;AACH,iBAAS,kBAAkB,CAAC,GAAG,EANpB,MAMoB,EAAE,OAAO,EAL7B;IAAC,kBAAkB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAAC,oBAAoB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAKxD,EAAE,QAAQ,EAJvC,OAIuC,EAAE,SAAS,EAHlD,OAGkD,EAAE,WAAW,AAFvE,CAA8B,EAAtB,OAEuE,UAcjF;AAwGE,YAAkB,UAAU,GAC5B;;;;IAAoB,MAAM,EAAnB,GAAG,CAAC,MAAM,CAAC,CAClB;;;;IAAoB,SAAS,EAAtB,GAAG,CAAC,MAAM,CAAC,CAClB;;;;IAAoB,MAAM,EAAnB,GAAG,CAAC,MAAM,CAAC,CAClB;;;;IAAoB,SAAS,EAAtB,GAAG,CAAC,MAAM,CAAC,CAClB;;;;IAAoB,YAAY,EAAzB,GAAG,CAAC,MAAM,CAAC,CACpB;CAAA,CAAA;AAPD;;;;;;;GAOG;AAEH;;;;;;GAMG;AACH,iBAAS,eAAe,CAAC,MAAM,EAJpB,GAAG,CAAC,MAAM,CAIU,EAAE,MAAM,EAH5B,GAAG,CAAC,MAAM,CAGkB,GAF1B,UAAU,CAWtB;AAGD,QAAA,MAAM,iBAAiB,YAA8F,CAAC;AAiBtH;;;;;;;;GAQG;AACH,iBAAS,uBAAuB,CAAC,GAAG,EARzB,MAQyB,EAAE,OAAO,EAPlC,MAOkC,EAAE,OAAO,EAN3C,MAM2C,EAAE,SAAS,EALtD,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAC,CAKkB,EAAE,SAAS,EAJjE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAC,CAI6B,EAAE,OAAO,EAH1E;IAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAAC,2BAA2B,CAAC,EAAE,OAAO,CAAA;CAG1B,EAAE,UAAU,EAFtF,UAEsF,UA6DhG;AASD,0BAA0B;AAC1B,iBAAS,qBAAqB,CAAC,GAAG,EADtB,MACsB,WAEjC;AAED,0BAA0B;AAC1B,iBAAS,iBAAiB,CAAC,GAAG,EADlB,MACkB,WAE7B;AAID,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,EACd,qBAAqB,EACrB,kBAAkB,EAClB,uBAAuB,EACvB,qBAAqB,EACrB,iBAAiB,EAClB,CAAC"}
|
|
@@ -165,7 +165,7 @@
|
|
|
165
165
|
"minimum": 0
|
|
166
166
|
},
|
|
167
167
|
"maxLineLength": {
|
|
168
|
-
"description": "Specify a maximum line length;
|
|
168
|
+
"description": "Specify a maximum line length; output will be split by newlines in tags only (between attributes or before a tag’s `>`), where they can’t change the page, so lines may run longer",
|
|
169
169
|
"type": "integer",
|
|
170
170
|
"minimum": 0
|
|
171
171
|
},
|
|
@@ -203,7 +203,7 @@
|
|
|
203
203
|
]
|
|
204
204
|
},
|
|
205
205
|
"noNewlinesBeforeTagClose": {
|
|
206
|
-
"description": "Never
|
|
206
|
+
"description": "Never split a line in a tag that closes an element—use with `--max-line-length`",
|
|
207
207
|
"type": "boolean"
|
|
208
208
|
},
|
|
209
209
|
"partialMarkup": {
|
package/package.json
CHANGED
|
@@ -14,11 +14,11 @@
|
|
|
14
14
|
},
|
|
15
15
|
"description": "Highly effective, super-configurable, well-tested web page minifier (enhanced successor to HTML Minifier)",
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"@commitlint/cli": "^21.2.
|
|
17
|
+
"@commitlint/cli": "^21.2.3",
|
|
18
18
|
"@eslint/js": "^10.0.1",
|
|
19
19
|
"@swc/core": "^1.16.2",
|
|
20
|
-
"@types/node": "^26.
|
|
21
|
-
"eslint": "^10.
|
|
20
|
+
"@types/node": "^26.6.2",
|
|
21
|
+
"eslint": "^10.11.0",
|
|
22
22
|
"globals": "^17.12.0",
|
|
23
23
|
"typescript": "^7.0.2",
|
|
24
24
|
"vite": "^8.3.0"
|
|
@@ -95,5 +95,5 @@
|
|
|
95
95
|
},
|
|
96
96
|
"type": "module",
|
|
97
97
|
"types": "dist/types/htmlminifier.d.ts",
|
|
98
|
-
"version": "8.5.
|
|
98
|
+
"version": "8.5.3"
|
|
99
99
|
}
|
package/src/htmlminifier.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HTMLParser
|
|
1
|
+
import { HTMLParser } from './htmlparser.js';
|
|
2
2
|
import TokenChain from './tokenchain.js';
|
|
3
3
|
import { presets, getPreset, getPresetNames } from './presets.js';
|
|
4
4
|
|
|
@@ -32,6 +32,9 @@ import {
|
|
|
32
32
|
} from './lib/constants.js';
|
|
33
33
|
|
|
34
34
|
import {
|
|
35
|
+
buildInlineSets,
|
|
36
|
+
defaultInlineSets,
|
|
37
|
+
endsWithWhitespace,
|
|
35
38
|
trimWhitespace,
|
|
36
39
|
collapseWhitespaceAll,
|
|
37
40
|
collapseWhitespace,
|
|
@@ -297,7 +300,8 @@ import { toFragment, replaceCustomFragments } from './lib/fragments.js';
|
|
|
297
300
|
*
|
|
298
301
|
* @prop {number} [maxLineLength]
|
|
299
302
|
* Maximum line length for the output. When set the minifier will wrap
|
|
300
|
-
* output to the given number of characters where possible
|
|
303
|
+
* output to the given number of characters where possible, splitting
|
|
304
|
+
* lines only in tags—between attributes or before a tag’s `>`.
|
|
301
305
|
*
|
|
302
306
|
* Default: No limit
|
|
303
307
|
*
|
|
@@ -350,8 +354,8 @@ import { toFragment, replaceCustomFragments } from './lib/fragments.js';
|
|
|
350
354
|
* Default: `false`
|
|
351
355
|
*
|
|
352
356
|
* @prop {boolean} [noNewlinesBeforeTagClose]
|
|
353
|
-
* When wrapping lines,
|
|
354
|
-
*
|
|
357
|
+
* When wrapping lines, never split a line in an end tag (e.g., keeps
|
|
358
|
+
* `</a>` from becoming `</a` and `>` on two lines).
|
|
355
359
|
*
|
|
356
360
|
* Default: `false`
|
|
357
361
|
*
|
|
@@ -1047,6 +1051,8 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1047
1051
|
|
|
1048
1052
|
/** @type {string[]} */
|
|
1049
1053
|
const buffer = [];
|
|
1054
|
+
// Parallel to `buffer`: whether an entry belongs to a start or end tag, the only place lines may break
|
|
1055
|
+
const bufferTags = options.maxLineLength ? /** @type {boolean[]} */ ([]) : null;
|
|
1050
1056
|
/** @type {string} */
|
|
1051
1057
|
let charsPrevTag = '';
|
|
1052
1058
|
let currentChars = '';
|
|
@@ -1099,6 +1105,9 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1099
1105
|
const inlineElements = normalizedCustomElements.length
|
|
1100
1106
|
? new Set([...inlineElementsToKeepWhitespaceAround, ...normalizedCustomElements])
|
|
1101
1107
|
: inlineElementsToKeepWhitespaceAround;
|
|
1108
|
+
const inlineSets = normalizedCustomElements.length
|
|
1109
|
+
? buildInlineSets(inlineElements, inlineTextSet)
|
|
1110
|
+
: defaultInlineSets;
|
|
1102
1111
|
|
|
1103
1112
|
// Parse `removeEmptyElementsExcept` option
|
|
1104
1113
|
/** @type {Array<{tag: string, attrs: {[x: string]: string | undefined} | null}>} */
|
|
@@ -1278,12 +1287,22 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1278
1287
|
return options.canTrimWhitespace(tag, attrs, defaultCanTrimWhitespace);
|
|
1279
1288
|
}
|
|
1280
1289
|
|
|
1290
|
+
function bufferPush(/** @type {string} */ entry, /** @type {boolean} */ isTag) {
|
|
1291
|
+
buffer.push(entry);
|
|
1292
|
+
bufferTags?.push(isTag);
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
function bufferTruncate(/** @type {number} */ length) {
|
|
1296
|
+
buffer.length = Math.max(0, length);
|
|
1297
|
+
if (bufferTags) bufferTags.length = buffer.length;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1281
1300
|
function removeStartTag() {
|
|
1282
1301
|
let index = buffer.length - 1;
|
|
1283
1302
|
while (index > 0 && !RE_START_TAG.test(buffer[index] ?? '')) {
|
|
1284
1303
|
index--;
|
|
1285
1304
|
}
|
|
1286
|
-
|
|
1305
|
+
bufferTruncate(index);
|
|
1287
1306
|
}
|
|
1288
1307
|
|
|
1289
1308
|
// Whether the element being parsed holds text rather than markup. The namespace decides
|
|
@@ -1339,24 +1358,39 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1339
1358
|
const noTrim = stackNoTrimWhitespace.length > 0;
|
|
1340
1359
|
if (index < buffer.length - 1 && (keepsWhitespace() || noTrim) && RE_END_TAG.test(buffer[index] ?? '')) {
|
|
1341
1360
|
buffer.splice(index, 1);
|
|
1361
|
+
bufferTags?.splice(index, 1);
|
|
1342
1362
|
// Only collapsed whitespace can merge
|
|
1343
1363
|
if (options.collapseWhitespace && !noTrim) {
|
|
1344
1364
|
mergeWhitespaceRuns(index);
|
|
1345
1365
|
}
|
|
1346
1366
|
return;
|
|
1347
1367
|
}
|
|
1348
|
-
|
|
1368
|
+
bufferTruncate(index);
|
|
1349
1369
|
}
|
|
1350
1370
|
|
|
1351
1371
|
// Look for trailing whitespaces, bypass any inline tags
|
|
1352
1372
|
function trimTrailingWhitespace(/** @type {number} */ index, /** @type {string} */ nextTag) {
|
|
1373
|
+
// Nothing to the left of the text is in play here, so only its trailing run can
|
|
1374
|
+
// change—unless preserved line breaks bring the leading run into play, too
|
|
1375
|
+
const trailingOnly = !options.preserveLineBreaks;
|
|
1353
1376
|
for (let prevTag = ''; index >= 0 && canTrimWhitespace(prevTag, emptyAttrs); index--) {
|
|
1354
1377
|
const str = buffer[index] ?? '';
|
|
1355
1378
|
const endTagName = matchPlainEndTag(str);
|
|
1356
1379
|
if (endTagName !== null) {
|
|
1357
1380
|
prevTag = endTagName;
|
|
1358
|
-
|
|
1359
|
-
|
|
1381
|
+
continue;
|
|
1382
|
+
}
|
|
1383
|
+
// A tag ends the search, and so does text that survives collapsing. Text with no
|
|
1384
|
+
// trailing run comes back from the call unchanged, so it can end the search on
|
|
1385
|
+
// the spot; empty text cannot, as the search has to carry on past it
|
|
1386
|
+
if (str.charCodeAt(str.length - 1) === 62 /* > */) {
|
|
1387
|
+
break;
|
|
1388
|
+
}
|
|
1389
|
+
if (trailingOnly && str && !endsWithWhitespace(str)) {
|
|
1390
|
+
break;
|
|
1391
|
+
}
|
|
1392
|
+
buffer[index] = collapseWhitespaceSmart(str, '', nextTag, emptyAttrs, emptyAttrs, options, inlineSets);
|
|
1393
|
+
if (buffer[index]) {
|
|
1360
1394
|
break;
|
|
1361
1395
|
}
|
|
1362
1396
|
}
|
|
@@ -1372,7 +1406,9 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1372
1406
|
// minification run then removes
|
|
1373
1407
|
while (charsIndex > 0) {
|
|
1374
1408
|
const item = buffer[charsIndex] ?? '';
|
|
1375
|
-
|
|
1409
|
+
// Comments and empty entries only, as `<!…` or nothing at all
|
|
1410
|
+
const skippable = item === '' || (item.charCodeAt(0) === 60 /* < */ && item.charCodeAt(1) === 33 /* ! */);
|
|
1411
|
+
if (!skippable || (uidIgnore && item.indexOf(uidIgnore) !== -1)) {
|
|
1376
1412
|
break;
|
|
1377
1413
|
}
|
|
1378
1414
|
charsIndex--;
|
|
@@ -1459,12 +1495,12 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1459
1495
|
}
|
|
1460
1496
|
trimTrailingWhitespace(tagIndex - 1, 'br');
|
|
1461
1497
|
}
|
|
1462
|
-
} else if (
|
|
1498
|
+
} else if (inlineSets.withinEither.has(textPrevTag)) {
|
|
1463
1499
|
text = collapseWhitespace(text, options, /(?:^|\s)$/.test(currentChars), false);
|
|
1464
1500
|
}
|
|
1465
1501
|
}
|
|
1466
1502
|
if (textPrevTag || textNextTag) {
|
|
1467
|
-
text = collapseWhitespaceSmart(text, effectivePrevTag, textNextTag, textPrevAttrs, textNextAttrs, options,
|
|
1503
|
+
text = collapseWhitespaceSmart(text, effectivePrevTag, textNextTag, textPrevAttrs, textNextAttrs, options, inlineSets);
|
|
1468
1504
|
} else {
|
|
1469
1505
|
text = collapseWhitespace(text, options, true, true);
|
|
1470
1506
|
}
|
|
@@ -1545,7 +1581,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1545
1581
|
if (text) {
|
|
1546
1582
|
hasChars = true;
|
|
1547
1583
|
}
|
|
1548
|
-
|
|
1584
|
+
bufferPush(text, false);
|
|
1549
1585
|
}
|
|
1550
1586
|
|
|
1551
1587
|
// Comment finalization (sync): Optional tag handling, `htmlmin:ignore` whitespace collapsing, buffer push
|
|
@@ -1656,7 +1692,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1656
1692
|
}
|
|
1657
1693
|
}
|
|
1658
1694
|
|
|
1659
|
-
|
|
1695
|
+
bufferPush(comment, false);
|
|
1660
1696
|
}
|
|
1661
1697
|
|
|
1662
1698
|
// Inline scripts are minified one at a time as the parse reaches them, which serializes
|
|
@@ -1839,7 +1875,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1839
1875
|
const needsXMLSlash = unary && Boolean(options.insideSVG) && Boolean(options.minifySVG);
|
|
1840
1876
|
const hasUnarySlash = needsXMLSlash || (unarySlash && (useNameParentForTag ? optionsParent : options).keepClosingSlash);
|
|
1841
1877
|
|
|
1842
|
-
|
|
1878
|
+
bufferPush(openTag, true);
|
|
1843
1879
|
|
|
1844
1880
|
const fragmentJoinsTag = uidAttrExactPattern ? joinFragmentAttrs(attrs) : false;
|
|
1845
1881
|
// Whatever runs into the tag name has to stay first
|
|
@@ -1883,7 +1919,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1883
1919
|
}
|
|
1884
1920
|
if (lastKeptIndex !== -1) {
|
|
1885
1921
|
if (!fragmentJoinsTag) {
|
|
1886
|
-
|
|
1922
|
+
bufferPush(' ', true);
|
|
1887
1923
|
}
|
|
1888
1924
|
for (let i = 0; i <= lastKeptIndex; i++) {
|
|
1889
1925
|
const normalizedAttr = normalizedAttrs[i];
|
|
@@ -1893,7 +1929,11 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1893
1929
|
}
|
|
1894
1930
|
const builtAttr = buildAttr(normalizedAttr, hasUnarySlash, options, i === lastKeptIndex, uidAttr);
|
|
1895
1931
|
// One entry with the tag name, so that no line break can come between them
|
|
1896
|
-
|
|
1932
|
+
if (i === 0 && fragmentJoinsTag) {
|
|
1933
|
+
buffer[buffer.length - 1] += builtAttr;
|
|
1934
|
+
} else {
|
|
1935
|
+
bufferPush(builtAttr, true);
|
|
1936
|
+
}
|
|
1897
1937
|
}
|
|
1898
1938
|
}
|
|
1899
1939
|
} else if (optional && optionalStartTags.has(tag)) {
|
|
@@ -1901,7 +1941,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1901
1941
|
optionalStartTag = tag;
|
|
1902
1942
|
}
|
|
1903
1943
|
|
|
1904
|
-
buffer
|
|
1944
|
+
buffer[buffer.length - 1] += (hasUnarySlash ? '/' : '') + '>';
|
|
1905
1945
|
|
|
1906
1946
|
if (autoGenerated && !options.includeAutoGeneratedTags) {
|
|
1907
1947
|
removeStartTag();
|
|
@@ -2010,7 +2050,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
2010
2050
|
if (autoGenerated && !options.includeAutoGeneratedTags) {
|
|
2011
2051
|
optionalEndTagEmitted = false;
|
|
2012
2052
|
} else {
|
|
2013
|
-
|
|
2053
|
+
bufferPush(endTagText, true);
|
|
2014
2054
|
}
|
|
2015
2055
|
charsPrevTag = '/' + tag;
|
|
2016
2056
|
if (!inlineElements.has(tag)) {
|
|
@@ -2023,7 +2063,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
2023
2063
|
if (autoGenerated && !options.includeAutoGeneratedTags) {
|
|
2024
2064
|
optionalEndTagEmitted = false;
|
|
2025
2065
|
} else {
|
|
2026
|
-
|
|
2066
|
+
bufferPush(endTagText, true);
|
|
2027
2067
|
}
|
|
2028
2068
|
charsPrevTag = '/' + tag;
|
|
2029
2069
|
if (!inlineElements.has(tag)) {
|
|
@@ -2099,10 +2139,10 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
2099
2139
|
commentFinalize(text);
|
|
2100
2140
|
},
|
|
2101
2141
|
doctype: function (/** @type {string} */ doctype) {
|
|
2102
|
-
|
|
2142
|
+
bufferPush(options.useShortDoctype
|
|
2103
2143
|
? '<!doctype' +
|
|
2104
2144
|
(options.removeTagWhitespace ? '' : ' ') + 'html>'
|
|
2105
|
-
: collapseWhitespaceAll(doctype));
|
|
2145
|
+
: collapseWhitespaceAll(doctype), false);
|
|
2106
2146
|
}
|
|
2107
2147
|
});
|
|
2108
2148
|
|
|
@@ -2119,7 +2159,10 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
2119
2159
|
);
|
|
2120
2160
|
for (let i = svgBlocks.length - 1; i >= 0; i--) {
|
|
2121
2161
|
const block = svgBlocks[i];
|
|
2122
|
-
if (block)
|
|
2162
|
+
if (block) {
|
|
2163
|
+
buffer.splice(block.start, block.end - block.start, optimized[i] ?? '');
|
|
2164
|
+
bufferTags?.splice(block.start, block.end - block.start, false);
|
|
2165
|
+
}
|
|
2123
2166
|
}
|
|
2124
2167
|
}
|
|
2125
2168
|
|
|
@@ -2138,7 +2181,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
2138
2181
|
squashTrailingWhitespace('br');
|
|
2139
2182
|
}
|
|
2140
2183
|
|
|
2141
|
-
return joinResultSegments(buffer, options, uidPattern
|
|
2184
|
+
return joinResultSegments(buffer, bufferTags ?? [], [uidAttr, uidIgnore].filter(marker => marker !== undefined), options, uidPattern
|
|
2142
2185
|
? function (/** @type {string} */ str) {
|
|
2143
2186
|
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
2187
|
const chunks = ignoredCustomMarkupChunks[+index];
|
|
@@ -2173,46 +2216,74 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
2173
2216
|
|
|
2174
2217
|
/**
|
|
2175
2218
|
* @param {string[]} results
|
|
2219
|
+
* @param {boolean[]} resultTags Whether each result belongs to a start or end tag
|
|
2220
|
+
* @param {string[]} markers Custom fragment and `htmlmin:ignore` placeholder markers
|
|
2176
2221
|
* @param {ProcessedOptions} options
|
|
2177
2222
|
* @param {Function} restoreCustom
|
|
2178
2223
|
* @param {Function} restoreIgnore
|
|
2179
2224
|
*/
|
|
2180
|
-
function joinResultSegments(results, options, restoreCustom, restoreIgnore) {
|
|
2225
|
+
function joinResultSegments(results, resultTags, markers, options, restoreCustom, restoreIgnore) {
|
|
2181
2226
|
let str;
|
|
2182
2227
|
const maxLineLength = options.maxLineLength;
|
|
2183
|
-
const noNewlinesBeforeTagClose = options.noNewlinesBeforeTagClose;
|
|
2184
2228
|
|
|
2185
2229
|
if (maxLineLength) {
|
|
2186
|
-
|
|
2187
|
-
//
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2230
|
+
// Whitespace in tags is the only whitespace that can’t change the output, so lines break
|
|
2231
|
+
// there alone—in place of the space between attributes, or right before a tag’s `>`
|
|
2232
|
+
const noNewlinesBeforeTagClose = options.noNewlinesBeforeTagClose;
|
|
2233
|
+
/** @type {string[]} */
|
|
2234
|
+
const lines = [];
|
|
2235
|
+
let lineLength = 0;
|
|
2236
|
+
// Output since the last break opportunity, and what that opportunity is when not taken;
|
|
2237
|
+
// fragments are restored by chunk, as whitespace around them decides how they come back
|
|
2238
|
+
let chunk = '';
|
|
2239
|
+
/** @type {string | null} */
|
|
2240
|
+
let separator = null;
|
|
2241
|
+
const flush = (/** @type {string | null} */ nextSeparator) => {
|
|
2242
|
+
chunk = restoreIgnore(restoreCustom(chunk));
|
|
2243
|
+
const newline = chunk.indexOf('\n');
|
|
2244
|
+
const head = newline < 0 ? chunk.length : newline;
|
|
2245
|
+
// A line already too long gains nothing from moving a `>` that ends a line of its own
|
|
2246
|
+
const isUseless = separator === '' && head === 1 && lineLength > maxLineLength;
|
|
2247
|
+
if (separator !== null) {
|
|
2248
|
+
if (lineLength > 0 && lineLength + separator.length + head > maxLineLength && !isUseless) {
|
|
2249
|
+
lines.push('\n');
|
|
2250
|
+
lineLength = 0;
|
|
2251
|
+
} else if (separator) {
|
|
2252
|
+
lines.push(separator);
|
|
2253
|
+
lineLength += separator.length;
|
|
2254
|
+
}
|
|
2203
2255
|
}
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2256
|
+
lines.push(chunk);
|
|
2257
|
+
lineLength = newline < 0 ? lineLength + chunk.length : chunk.length - chunk.lastIndexOf('\n') - 1;
|
|
2258
|
+
chunk = '';
|
|
2259
|
+
separator = nextSeparator;
|
|
2260
|
+
};
|
|
2261
|
+
for (let index = 0; index < results.length; index++) {
|
|
2262
|
+
const result = results[index] ?? '';
|
|
2263
|
+
if (!resultTags[index]) {
|
|
2264
|
+
chunk += result;
|
|
2265
|
+
} else if (markers.length && markers.some(marker => result.includes(marker))) {
|
|
2266
|
+
// Whitespace next to a fragment stays as is, as it may belong to it
|
|
2267
|
+
if (!chunk && separator !== null) {
|
|
2268
|
+
chunk = separator;
|
|
2269
|
+
separator = null;
|
|
2270
|
+
}
|
|
2271
|
+
chunk += result;
|
|
2272
|
+
} else if (result.charCodeAt(result.length - 1) === 32 /* space */) {
|
|
2273
|
+
chunk += result.slice(0, -1);
|
|
2274
|
+
flush(' ');
|
|
2275
|
+
} else if (result.charCodeAt(result.length - 1) === 62 /* > */ &&
|
|
2276
|
+
result.charCodeAt(result.length - 2) !== 47 /* / */ &&
|
|
2277
|
+
!(noNewlinesBeforeTagClose && result.startsWith('</'))) {
|
|
2278
|
+
chunk += result.slice(0, -1);
|
|
2279
|
+
flush('');
|
|
2280
|
+
chunk = '>';
|
|
2281
|
+
} else {
|
|
2282
|
+
chunk += result;
|
|
2210
2283
|
}
|
|
2211
2284
|
}
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
}
|
|
2215
|
-
str = lines.join('\n');
|
|
2285
|
+
flush(null);
|
|
2286
|
+
str = lines.join('');
|
|
2216
2287
|
} else {
|
|
2217
2288
|
str = restoreIgnore(restoreCustom(results.join('')));
|
|
2218
2289
|
}
|
package/src/htmlparser.js
CHANGED
|
@@ -67,7 +67,6 @@ const qnameCapture = (function () {
|
|
|
67
67
|
})();
|
|
68
68
|
const startTagOpen = new RegExp('^<' + qnameCapture);
|
|
69
69
|
const endTagOpen = new RegExp('^</' + qnameCapture);
|
|
70
|
-
export const endTag = new RegExp('^</' + qnameCapture + '[^>]*>');
|
|
71
70
|
|
|
72
71
|
let IS_REGEX_CAPTURING_BROKEN = false;
|
|
73
72
|
'x'.replace(/x(.)?/g, function (/** @type {string} */ m, /** @type {string | undefined} */ g) {
|
package/src/lib/constants.js
CHANGED
|
@@ -50,6 +50,15 @@ const inlineElementsToKeepWhitespace = new Set(['comment', 'img', 'input', 'wbr'
|
|
|
50
50
|
// Form control elements (for conditional whitespace collapsing)
|
|
51
51
|
const formControlElements = new Set(['input', 'button', 'select', 'textarea', 'output', 'meter', 'progress']);
|
|
52
52
|
|
|
53
|
+
// End-tag spellings (`/a`) of a set of element names
|
|
54
|
+
/** @param {Iterable<string>} names */
|
|
55
|
+
const toEndTags = names => new Set(Array.from(names, name => '/' + name));
|
|
56
|
+
|
|
57
|
+
// Membership under either spelling, for the whitespace checks that ignore the slash;
|
|
58
|
+
// looking a tag up as it comes keeps the hot path from slicing one string per call
|
|
59
|
+
const inlineElementsToKeepWhitespaceWithinEither = new Set([...inlineElementsToKeepWhitespaceWithin, ...toEndTags(inlineElementsToKeepWhitespaceWithin)]);
|
|
60
|
+
const formControlElementsEither = new Set([...formControlElements, ...toEndTags(formControlElements)]);
|
|
61
|
+
|
|
53
62
|
// Default attribute values
|
|
54
63
|
|
|
55
64
|
// Default attribute values (could apply to any element)
|
|
@@ -285,7 +294,9 @@ export {
|
|
|
285
294
|
inlineElementsToKeepWhitespaceAround,
|
|
286
295
|
inlineElementsToKeepWhitespaceWithin,
|
|
287
296
|
inlineElementsToKeepWhitespace,
|
|
288
|
-
|
|
297
|
+
inlineElementsToKeepWhitespaceWithinEither,
|
|
298
|
+
formControlElementsEither,
|
|
299
|
+
toEndTags,
|
|
289
300
|
|
|
290
301
|
// Default values
|
|
291
302
|
generalDefaults,
|
|
@@ -101,7 +101,7 @@ const optionDefinitions = {
|
|
|
101
101
|
type: 'int'
|
|
102
102
|
},
|
|
103
103
|
maxLineLength: {
|
|
104
|
-
description: 'Specify a maximum line length;
|
|
104
|
+
description: 'Specify a maximum line length; output will be split by newlines in tags only (between attributes or before a tag’s `>`), where they can’t change the page, so lines may run longer',
|
|
105
105
|
type: 'int'
|
|
106
106
|
},
|
|
107
107
|
mergeScripts: {
|
|
@@ -125,7 +125,7 @@ const optionDefinitions = {
|
|
|
125
125
|
type: 'json'
|
|
126
126
|
},
|
|
127
127
|
noNewlinesBeforeTagClose: {
|
|
128
|
-
description: 'Never
|
|
128
|
+
description: 'Never split a line in a tag that closes an element—use with `--max-line-length`',
|
|
129
129
|
type: 'boolean'
|
|
130
130
|
},
|
|
131
131
|
partialMarkup: {
|
package/src/lib/whitespace.js
CHANGED
|
@@ -9,14 +9,18 @@ import {
|
|
|
9
9
|
RE_NBSP_TRAILING_GROUP,
|
|
10
10
|
RE_NBSP_TRAILING_STRIP,
|
|
11
11
|
inlineElementsToKeepWhitespace,
|
|
12
|
+
inlineElementsToKeepWhitespaceAround,
|
|
12
13
|
inlineElementsToKeepWhitespaceWithin,
|
|
13
|
-
|
|
14
|
+
inlineElementsToKeepWhitespaceWithinEither,
|
|
15
|
+
formControlElementsEither,
|
|
16
|
+
toEndTags
|
|
14
17
|
} from './constants.js';
|
|
15
18
|
|
|
16
19
|
const RE_ANY_WS_NBSP = /[ \n\r\t\f\xA0]/;
|
|
17
20
|
const RE_TAB_NBSP = /[\t\xA0]/;
|
|
18
21
|
const RE_ASCII_WS_RUN = /[ \n\r\f]+/g;
|
|
19
|
-
|
|
22
|
+
// Every reason `collapseWhitespaceAllKnown` has to do any work at all, in one scan
|
|
23
|
+
const RE_COLLAPSIBLE = /[\t\xA0\n\r\f]| {2}/;
|
|
20
24
|
const RE_NON_WS = /\S/;
|
|
21
25
|
|
|
22
26
|
// Trim whitespace
|
|
@@ -41,6 +45,12 @@ function isWsOrNbsp(code) {
|
|
|
41
45
|
return code === 32 || code === 10 || code === 13 || code === 9 || code === 12 || code === 160;
|
|
42
46
|
}
|
|
43
47
|
|
|
48
|
+
// Whether the last character is whitespace, no-break space included
|
|
49
|
+
/** @param {string} str */
|
|
50
|
+
function endsWithWhitespace(str) {
|
|
51
|
+
return str.length > 0 && isWsOrNbsp(str.charCodeAt(str.length - 1));
|
|
52
|
+
}
|
|
53
|
+
|
|
44
54
|
// Collapse all whitespace
|
|
45
55
|
|
|
46
56
|
// Module scope so the hot path doesn’t allocate a closure per call
|
|
@@ -65,10 +75,15 @@ function collapseWhitespaceAll(str) {
|
|
|
65
75
|
// As `collapseWhitespaceAll`, for callers that already know `str` holds whitespace
|
|
66
76
|
/** @param {string} str */
|
|
67
77
|
function collapseWhitespaceAllKnown(str) {
|
|
78
|
+
// Most text reaching here is already collapsed, so settle that in one scan rather
|
|
79
|
+
// than two—single spaces with no tab and no no-break space leave nothing to do
|
|
80
|
+
if (!RE_COLLAPSIBLE.test(str)) {
|
|
81
|
+
return str;
|
|
82
|
+
}
|
|
68
83
|
// Only a tab or a no-break space makes the replacement depend on the run itself;
|
|
69
84
|
// without either, every run becomes one space and no per-match callback is needed
|
|
70
85
|
if (!RE_TAB_NBSP.test(str)) {
|
|
71
|
-
return
|
|
86
|
+
return str.replace(RE_ASCII_WS_RUN, ' ');
|
|
72
87
|
}
|
|
73
88
|
return str.replace(RE_ALL_WS_NBSP, collapseRun);
|
|
74
89
|
}
|
|
@@ -199,6 +214,36 @@ function collapseWhitespaceKnown(str, options, trimLeft, trimRight, collapseAll)
|
|
|
199
214
|
|
|
200
215
|
// Collapse whitespace smartly based on surrounding tags
|
|
201
216
|
|
|
217
|
+
/**
|
|
218
|
+
* @typedef {object} InlineSets
|
|
219
|
+
* @prop {Set<string>} around - Elements whose surrounding whitespace is kept
|
|
220
|
+
* @prop {Set<string>} aroundEnd - The same, spelled as end tags
|
|
221
|
+
* @prop {Set<string>} within - Elements whose inner whitespace is kept
|
|
222
|
+
* @prop {Set<string>} withinEnd - The same, spelled as end tags
|
|
223
|
+
* @prop {Set<string>} withinEither - `within` under both spellings
|
|
224
|
+
*/
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Inline-element membership for the whitespace pass, each set paired with its end-tag
|
|
228
|
+
* spelling so a lookup takes the tag as it comes rather than slicing off the slash
|
|
229
|
+
* @param {Set<string>} around
|
|
230
|
+
* @param {Set<string>} within
|
|
231
|
+
* @returns {InlineSets}
|
|
232
|
+
*/
|
|
233
|
+
function buildInlineSets(around, within) {
|
|
234
|
+
const withinEnd = toEndTags(within);
|
|
235
|
+
return {
|
|
236
|
+
around,
|
|
237
|
+
aroundEnd: toEndTags(around),
|
|
238
|
+
within,
|
|
239
|
+
withinEnd,
|
|
240
|
+
withinEither: new Set([...within, ...withinEnd])
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// The sets for a document without custom inline elements, built once
|
|
245
|
+
const defaultInlineSets = buildInlineSets(inlineElementsToKeepWhitespaceAround, inlineElementsToKeepWhitespaceWithin);
|
|
246
|
+
|
|
202
247
|
// Check if an input element has `type="hidden"`; module-scope so the hot path
|
|
203
248
|
// doesn’t allocate a closure per text node
|
|
204
249
|
/**
|
|
@@ -221,10 +266,9 @@ function isHiddenInput(attrs) {
|
|
|
221
266
|
* @param {Array<{name: string, value?: string}>} prevAttrs
|
|
222
267
|
* @param {Array<{name: string, value?: string}>} nextAttrs
|
|
223
268
|
* @param {{preserveLineBreaks?: boolean, conservativeCollapse?: boolean, collapseInlineTagWhitespace?: boolean}} options
|
|
224
|
-
* @param {
|
|
225
|
-
* @param {Set<string>} inlineTextSet
|
|
269
|
+
* @param {InlineSets} inlineSets
|
|
226
270
|
*/
|
|
227
|
-
function collapseWhitespaceSmart(str, prevTag, nextTag, prevAttrs, nextAttrs, options,
|
|
271
|
+
function collapseWhitespaceSmart(str, prevTag, nextTag, prevAttrs, nextAttrs, options, inlineSets) {
|
|
228
272
|
if (!str) return str;
|
|
229
273
|
|
|
230
274
|
// Fast path: No whitespace at all—every decision below would leave `str`
|
|
@@ -253,7 +297,7 @@ function collapseWhitespaceSmart(str, prevTag, nextTag, prevAttrs, nextAttrs, op
|
|
|
253
297
|
}
|
|
254
298
|
// Aggressive mode: Collapse between all form controls
|
|
255
299
|
if (inlineOption && (!trimLeft || !trimRight) &&
|
|
256
|
-
|
|
300
|
+
formControlElementsEither.has(prevTag) && formControlElementsEither.has(nextTag)) {
|
|
257
301
|
trimLeft = true;
|
|
258
302
|
trimRight = true;
|
|
259
303
|
}
|
|
@@ -262,21 +306,21 @@ function collapseWhitespaceSmart(str, prevTag, nextTag, prevAttrs, nextAttrs, op
|
|
|
262
306
|
if (trimLeft) {
|
|
263
307
|
if (inlineOption) {
|
|
264
308
|
// Still preserve whitespace around inline text elements
|
|
265
|
-
if (
|
|
309
|
+
if (inlineElementsToKeepWhitespaceWithinEither.has(prevTag)) {
|
|
266
310
|
trimLeft = false;
|
|
267
311
|
}
|
|
268
312
|
} else {
|
|
269
|
-
trimLeft = prevTag.charCodeAt(0) === 47 /* / */ ? !
|
|
313
|
+
trimLeft = prevTag.charCodeAt(0) === 47 /* / */ ? !inlineSets.aroundEnd.has(prevTag) : !inlineSets.within.has(prevTag);
|
|
270
314
|
}
|
|
271
315
|
}
|
|
272
316
|
|
|
273
317
|
if (trimRight) {
|
|
274
318
|
if (inlineOption) {
|
|
275
|
-
if (
|
|
319
|
+
if (inlineElementsToKeepWhitespaceWithinEither.has(nextTag)) {
|
|
276
320
|
trimRight = false;
|
|
277
321
|
}
|
|
278
322
|
} else {
|
|
279
|
-
trimRight = nextTag.charCodeAt(0) === 47 /* / */ ? !
|
|
323
|
+
trimRight = nextTag.charCodeAt(0) === 47 /* / */ ? !inlineSets.withinEnd.has(nextTag) : !inlineSets.around.has(nextTag);
|
|
280
324
|
}
|
|
281
325
|
}
|
|
282
326
|
|
|
@@ -287,11 +331,6 @@ function collapseWhitespaceSmart(str, prevTag, nextTag, prevAttrs, nextAttrs, op
|
|
|
287
331
|
return collapseWhitespaceKnown(str, options, trimLeft, trimRight, collapseAll);
|
|
288
332
|
}
|
|
289
333
|
|
|
290
|
-
/** @param {string} tag */
|
|
291
|
-
function stripSlash(tag) {
|
|
292
|
-
return tag && tag.charCodeAt(0) === 47 /* / */ ? tag.slice(1) : tag;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
334
|
// Collapse/trim whitespace for given tag
|
|
296
335
|
|
|
297
336
|
// `xmp`, `listing`, and `plaintext` render preformatted, as `pre` does
|
|
@@ -312,6 +351,9 @@ function canTrimWhitespace(tag) {
|
|
|
312
351
|
// Exports
|
|
313
352
|
|
|
314
353
|
export {
|
|
354
|
+
buildInlineSets,
|
|
355
|
+
defaultInlineSets,
|
|
356
|
+
endsWithWhitespace,
|
|
315
357
|
trimWhitespace,
|
|
316
358
|
collapseWhitespaceAll,
|
|
317
359
|
collapseWhitespace,
|