html-minifier-next 8.5.3 → 8.7.0
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 +15 -10
- package/cli.js +1 -1
- package/dist/types/htmlminifier.d.ts +10 -0
- package/dist/types/htmlminifier.d.ts.map +1 -1
- package/dist/types/htmlparser.d.ts +1 -0
- package/dist/types/htmlparser.d.ts.map +1 -1
- package/dist/types/lib/option-definitions.d.ts.map +1 -1
- package/dist/types/lib/options.d.ts.map +1 -1
- package/dist/types/lib/whitespace.d.ts +9 -1
- package/dist/types/lib/whitespace.d.ts.map +1 -1
- package/html-minifier-next.schema.json +4 -0
- package/package.json +3 -3
- package/src/htmlminifier.js +149 -40
- package/src/htmlparser.js +4 -0
- package/src/lib/option-definitions.js +4 -0
- package/src/lib/options.js +1 -0
- package/src/lib/unused-css.js +1 -1
- package/src/lib/whitespace.js +39 -0
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Your web page optimization precision tool: HTML Minifier Next (HMN) is a **highly effective, super-configurable, well-tested HTML minifier**, written in JavaScript, that also handles in-document CSS, JavaScript, and SVG minification.
|
|
6
6
|
|
|
7
|
-
The project was based on [HTML Minifier Terser (HMT)](https://github.com/terser/html-minifier-terser), which in turn had been based on [Juriy “kangax” Zaytsev’s HTML Minifier (HM)](https://github.com/kangax/html-minifier). HMN is **the official successor to HTML Minifier**: It’s actively maintained, richer in features, easier to use, and significantly faster. Note that HMN is largely compatible with HM and HMT but isn’t fully drop-in—find [migration guidance in the changelog](https://github.com/j9t/html-minifier-next/blob/main/CHANGELOG.md).
|
|
7
|
+
The project was based on [HTML Minifier Terser (HMT)](https://github.com/terser/html-minifier-terser), which in turn had been based on [Juriy “kangax” Zaytsev’s HTML Minifier (HM)](https://github.com/kangax/html-minifier). HMN is **the official successor to HTML Minifier**: It’s actively maintained, richer in features, easier to use, and significantly faster ([HMN engineering philosophy](https://meiert.com/blog/html-minifier-next-engineering-philosophy/)). Note that HMN is largely compatible with HM and HMT but isn’t fully drop-in—find [migration guidance in the changelog](https://github.com/j9t/html-minifier-next/blob/main/CHANGELOG.md).
|
|
8
8
|
|
|
9
9
|
## Installation
|
|
10
10
|
|
|
@@ -32,7 +32,7 @@ Use `npx html-minifier-next --help` to check all available options:
|
|
|
32
32
|
|
|
33
33
|
| Option | Description | Example |
|
|
34
34
|
| --- | --- | --- |
|
|
35
|
-
| `--zero`, `-z` | Minify all HTML files in the current folder and its subfolders in place (except node_modules), using comprehensive settings (standalone—flag is ignored when combined with other options) | `npx html-minifier-next --zero` |
|
|
35
|
+
| `--zero`, `-z` | Minify all HTML files in the current folder and its subfolders in place (except node_modules), using comprehensive settings (standalone—the flag is ignored when combined with other options) | `npx html-minifier-next --zero` |
|
|
36
36
|
| `--input-dir <dir>`, `-I <dir>` | Specify an input directory | `--input-dir=src` |
|
|
37
37
|
| `--ignore-dir <patterns>`, `-X <patterns>` | Exclude directories—relative to input directory—from processing (comma-separated, overrides config file setting) | `--ignore-dir=libs`, `--ignore-dir=libs,vendor,node_modules` |
|
|
38
38
|
| `--output-dir <dir>`, `-O <dir>` | Specify an output directory | `--output-dir=dist` |
|
|
@@ -141,6 +141,7 @@ Options can be used in config files (camelCase) or via CLI flags (kebab-case wit
|
|
|
141
141
|
| `collapseAttributeWhitespace`<br>`--collapse-attribute-whitespace` | Trim and collapse whitespace characters within attribute values | `false` |
|
|
142
142
|
| `collapseBooleanAttributes`<br>`--collapse-boolean-attributes` | [Omit attribute values from boolean attributes](https://perfectionkills.com/experimenting-with-html-minifier/#collapse_boolean_attributes) | `false` |
|
|
143
143
|
| `collapseInlineTagWhitespace`<br>`--collapse-inline-tag-whitespace` | Collapse whitespace more aggressively between inline elements—use with [`collapseWhitespace`](#combining-whitespace-options) | `false` |
|
|
144
|
+
| `collapseNoBreakSpaces`<br>`--collapse-no-break-spaces` | Remove whitespace next to no-break spaces, narrow no-break spaces, and figure spaces in text (e.g., `a b` → `a b`), keeping runs of them—use with [`collapseWhitespace`](#combining-whitespace-options) | `false` |
|
|
144
145
|
| `collapseWhitespace`<br>`--collapse-whitespace` | [Collapse whitespace that contributes to text nodes in a document tree](https://perfectionkills.com/experimenting-with-html-minifier/#collapse_whitespace); [enable other whitespace options](#combining-whitespace-options) | `false` |
|
|
145
146
|
| `conservativeCollapse`<br>`--conservative-collapse` | Always collapse to one space (never remove it entirely)—use with [`collapseWhitespace`](#combining-whitespace-options) | `false` |
|
|
146
147
|
| `continueOnMinifyError`<br>`--continue-on-minify-error`<br>`--no-continue-on-minify-error` | Continue on minification errors; when `false`, minification errors throw and abort processing | `true` |
|
|
@@ -211,6 +212,7 @@ HTML Minifier Next: Ignoring `conservativeCollapse`—use with `collapseWhitespa
|
|
|
211
212
|
| `canMinifyJS` | `minifyJS`, and not [a function of your own](#javascript-minification) |
|
|
212
213
|
| `canMinifySVG` | `minifySVG`, and not [a function of your own](#svg-minification) |
|
|
213
214
|
| `collapseInlineTagWhitespace` | `collapseWhitespace` |
|
|
215
|
+
| `collapseNoBreakSpaces` | `collapseWhitespace` |
|
|
214
216
|
| `conservativeCollapse` | `collapseWhitespace` |
|
|
215
217
|
| `customEventAttributes` | `minifyJS` |
|
|
216
218
|
| `inlineCustomElements` | `collapseWhitespace` |
|
|
@@ -220,11 +222,11 @@ HTML Minifier Next: Ignoring `conservativeCollapse`—use with `collapseWhitespa
|
|
|
220
222
|
| `removeUnusedCSS` | `minifyCSS`, and not [a function of your own](#unused-css-removal) |
|
|
221
223
|
| `trimCustomFragments` | `collapseWhitespace` |
|
|
222
224
|
|
|
223
|
-
Passing the option `false`, or an empty array, asks for nothing and is not reported. `cacheCSS`, `cacheJS`, and `cacheSVG` are not
|
|
225
|
+
Passing the option `false`, or an empty array, asks for nothing and is not reported. `cacheCSS`, `cacheJS`, and `cacheSVG` are not included because they size a cache rather than transform markup, and therefore don’t change output.
|
|
224
226
|
|
|
225
227
|
### Combining whitespace options
|
|
226
228
|
|
|
227
|
-
`collapseInlineTagWhitespace`, `conservativeCollapse`, and `preserveLineBreaks` are modifiers: They do nothing on their own, and only take effect when `collapseWhitespace` is enabled.
|
|
229
|
+
`collapseInlineTagWhitespace`, `collapseNoBreakSpaces`, `conservativeCollapse`, and `preserveLineBreaks` are modifiers: They do nothing on their own, and only take effect when `collapseWhitespace` is enabled.
|
|
228
230
|
|
|
229
231
|
Given input
|
|
230
232
|
|
|
@@ -247,6 +249,8 @@ you get the following output (condensed, `\n` represents an actual line break):
|
|
|
247
249
|
|
|
248
250
|
Where the modifiers disagree, the preserving one wins—`conservativeCollapse` and `preserveLineBreaks` do not let `collapseInlineTagWhitespace` remove a space or line break entirely.
|
|
249
251
|
|
|
252
|
+
Regardless of these options, whitespace at the start and end of the output goes, and so do lines that a removed comment or tag leaves with nothing but whitespace (blank lines of the source stay). Whitespace stays, however, where it’s kept verbatim (in `pre` or `textarea`, or in `pre`, `textarea`, or `script` content left open at the end), next to custom fragments where their handling keeps it (see `trimCustomFragments`), at either end of partial markup (cf. `partialMarkup`), and where `conservativeCollapse` or `preserveLineBreaks` keep it.
|
|
253
|
+
|
|
250
254
|
### Sorting attributes and style classes
|
|
251
255
|
|
|
252
256
|
`sortAttributes` and `sortClassNames` reorder attributes and class names by frequency, so that repeated markup looks more alike. This doesn’t change the plain-text size of the output, only how well it compresses: `sortAttributes` makes Gzip and Brotli output slightly smaller on average, though not on every page (see [“Optimizing for compression”](#optimizing-for-compression)); `sortClassNames` doesn’t help, and alongside `sortAttributes` cancels most of its gain with Brotli.
|
|
@@ -503,12 +507,11 @@ HTML Minifier Next uses in-memory caches to improve performance when processing
|
|
|
503
507
|
```js
|
|
504
508
|
const result = await minify(html, {
|
|
505
509
|
minifyCSS: true,
|
|
510
|
+
cacheCSS: 750, // CSS cache size, default: 500
|
|
506
511
|
minifyJS: true,
|
|
512
|
+
cacheJS: 250, // JS cache size, default: 500
|
|
507
513
|
minifySVG: true,
|
|
508
|
-
//
|
|
509
|
-
cacheCSS: 750, // CSS cache size, default: 500
|
|
510
|
-
cacheJS: 250, // JS cache size, default: 500
|
|
511
|
-
cacheSVG: 100 // SVG cache size, default: 500
|
|
514
|
+
cacheSVG: 100 // SVG cache size, default: 500
|
|
512
515
|
});
|
|
513
516
|
```
|
|
514
517
|
|
|
@@ -676,7 +679,7 @@ By default, HMN parses markup into a complete tree structure, then modifies it (
|
|
|
676
679
|
|
|
677
680
|
_Input markup (e.g., `<p id="">foo`) → Internal representation of markup in a form of tree (e.g., `{ tag: "p", attr: "id", children: ["foo"] }`) → Transformation of internal representation (e.g., removal of `id` attribute) → Output of resulting markup (e.g., `<p>foo</p>`)_
|
|
678
681
|
|
|
679
|
-
For partial HTML fragments (such as template includes, SSI fragments, or closing tags without opening tags), use the `partialMarkup: true` option. This preserves stray end tags (closing tags without corresponding opening tags) and prevents auto-closing of unclosed tags at the end of input. Note that normal HTML auto-closing rules still apply during parsing—for example, a closing parent tag will still auto-close its unclosed child elements.
|
|
682
|
+
For partial HTML fragments (such as template includes, SSI fragments, or closing tags without opening tags), use the `partialMarkup: true` option. This preserves stray end tags (closing tags without corresponding opening tags) and prevents auto-closing of unclosed tags at the end of input. Note that normal HTML auto-closing rules still apply during parsing—for example, a closing parent tag will still auto-close its unclosed child elements. Whitespace at the start and end of a fragment stays, too, as it may separate the fragment from what surrounds it (unless `collapseWhitespace` removes it).
|
|
680
683
|
|
|
681
684
|
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
685
|
|
|
@@ -841,6 +844,8 @@ Parameters:
|
|
|
841
844
|
|
|
842
845
|
To compare branches (A/B run), execute `npm run benchmark -- --save` on `main`, then `npm run benchmark` on the branch to see the deltas. Add `--core` on both ends when measuring changes to HMN rather than bundled minifiers, or `--cold` when measuring changes to the CSS, JS, or SVG minification paths.
|
|
843
846
|
|
|
847
|
+
For changes of around 1% or less, use `npm run benchmark:ab -- main` instead: It times the working tree against any Git ref in the same session, runs both in turn per file, averages both loading orders, and reports `collapseWhitespace` on and off separately. Add `--aa` to compare the ref with a copy of itself—the noise floor any finding must clear. The script’s header lists further parameters.
|
|
848
|
+
|
|
844
849
|
Reported times are the *fastest* iteration, not the median: Interference can only make a run slower, so the minimum is the most stable estimate. Each run also reports its noise—how far the reported figure moves between the first and second half of the iterations—and any delta smaller than that is marked `within noise` rather than shown as a win or a regression. Raise `--iterations` until the noise sits below the change you are trying to measure.
|
|
845
850
|
|
|
846
851
|
#### Profiling
|
|
@@ -855,7 +860,7 @@ This writes a .cpuprofile file to the working directory. Load it with `npx speed
|
|
|
855
860
|
|
|
856
861
|
## Acknowledgements
|
|
857
862
|
|
|
858
|
-
With many thanks to the previous authors of and contributors to HTML Minifier, especially [Juriy “kangax” Zaytsev](https://github.com/kangax), and to everyone who helped make this new edition better, particularly [Daniel Ruf](https://github.com/DanielRuf), [Jonas Geiler](https://github.com/jonasgeiler),
|
|
863
|
+
With many thanks to the previous authors of and contributors to HTML Minifier, especially [Juriy “kangax” Zaytsev](https://github.com/kangax), and to everyone who helped make this new edition better, particularly [Daniel Ruf](https://github.com/DanielRuf), [Jonas Geiler](https://github.com/jonasgeiler), [Chris Morgan](https://github.com/chris-morgan), and [Andreas Borutta](https://github.com/fermiongithub)!
|
|
859
864
|
|
|
860
865
|
***
|
|
861
866
|
|
package/cli.js
CHANGED
|
@@ -330,7 +330,7 @@ function normalizeConfig(config) {
|
|
|
330
330
|
|
|
331
331
|
/** @type {Record<string, any>} */
|
|
332
332
|
let config = {};
|
|
333
|
-
program.option('-z, --zero', 'Minify all HTML files in the current folder and its subfolders in place (except node_modules), using comprehensive settings (standalone—flag is ignored when combined with other options)');
|
|
333
|
+
program.option('-z, --zero', 'Minify all HTML files in the current folder and its subfolders in place (except node_modules), using comprehensive settings (standalone—the flag is ignored when combined with other options)');
|
|
334
334
|
program.option('-I --input-dir <dir>', 'Specify an input directory');
|
|
335
335
|
program.option('-X --ignore-dir <patterns>', 'Exclude directories—relative to input directory—from processing (comma-separated, overrides config file setting)');
|
|
336
336
|
program.option('-O --output-dir <dir>', 'Specify an output directory');
|
|
@@ -113,6 +113,16 @@ export type MinifierOptions = {
|
|
|
113
113
|
* Default: `false`
|
|
114
114
|
*/
|
|
115
115
|
collapseInlineTagWhitespace?: boolean;
|
|
116
|
+
/**
|
|
117
|
+
* Remove whitespace next to a no-break space (`U+00A0`), narrow no-break
|
|
118
|
+
* space (`U+202F`), or figure space (`U+2007`) in text (for example
|
|
119
|
+
* `a b` → `a b`), which changes how the text renders. Runs of
|
|
120
|
+
* no-break spaces are kept.
|
|
121
|
+
* Must also enable `collapseWhitespace` to have effect.
|
|
122
|
+
*
|
|
123
|
+
* Default: `false`
|
|
124
|
+
*/
|
|
125
|
+
collapseNoBreakSpaces?: boolean;
|
|
116
126
|
/**
|
|
117
127
|
* Collapse multiple whitespace characters into one where allowed. Also
|
|
118
128
|
* controls trimming behaviour in several code paths.
|
|
@@ -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,
|
|
1
|
+
{"version":3,"file":"htmlparser.d.ts","sourceRoot":"","sources":["../../src/htmlparser.js"],"names":[],"mappings":"AAeG,YAaG,iBAAiB,GAbV;IACR,KAAK,CAAC,WAAW;IACjB,GAAG,CAAC,WAAW;IACf,KAAK,CAAC,WAAW;IACjB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,WAAW;IACnB,QAAQ,CAAC,WAAW;IACpB,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,kBA8yBV;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"option-definitions.d.ts","sourceRoot":"","sources":["../../../src/lib/option-definitions.js"],"names":[],"mappings":"AAAA;;GAEG;AAGA,YAAkB,gBAAgB,GAClC;;;;IAAmB,WAAW,EAAnB,MAAM,CACjB;;;;IAAoB,sBAAsB,AAA1C,CACA,EADW,MAAM,CACjB;;;;IAAmB,IAAI,EAAZ,MAAM,CACnB;CAAA,CAAA;AALD;;;;;GAKG;AAEH,+CAA+C;AAC/C,QAAA,MAAM,iBAAiB,EADZ,MAAM,CAAC,MAAM,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"option-definitions.d.ts","sourceRoot":"","sources":["../../../src/lib/option-definitions.js"],"names":[],"mappings":"AAAA;;GAEG;AAGA,YAAkB,gBAAgB,GAClC;;;;IAAmB,WAAW,EAAnB,MAAM,CACjB;;;;IAAoB,sBAAsB,AAA1C,CACA,EADW,MAAM,CACjB;;;;IAAmB,IAAI,EAAZ,MAAM,CACnB;CAAA,CAAA;AALD;;;;;GAKG;AAEH,+CAA+C;AAC/C,QAAA,MAAM,iBAAiB,EADZ,MAAM,CAAC,MAAM,EAAE,gBAAgB,CA2MzC,CAAC;AAEF,QAAA,MAAM,cAAc;IAClB,qBAAqB;IACrB,oBAAoB;IAIpB,qBAAqB;IAIrB,wBAAwB;CACzB,CAAC;AAIF,OAAO,EACL,iBAAiB,EACjB,cAAc,EACf,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../../src/lib/options.js"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../../src/lib/options.js"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,14 @@ declare const trimWhitespace: (str: string) => string;
|
|
|
7
7
|
declare function endsWithWhitespace(str: string): boolean;
|
|
8
8
|
/** @param {string} str */
|
|
9
9
|
declare function collapseWhitespaceAll(str: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* @param {string} str
|
|
12
|
+
* @param {{decodeEntities?: boolean | undefined, preserveLineBreaks?: boolean | undefined}} options
|
|
13
|
+
*/
|
|
14
|
+
declare function collapseNoBreakSpaces(str: string, options: {
|
|
15
|
+
decodeEntities?: boolean | undefined;
|
|
16
|
+
preserveLineBreaks?: boolean | undefined;
|
|
17
|
+
}): string;
|
|
10
18
|
/**
|
|
11
19
|
* @param {string} str
|
|
12
20
|
* @param {{preserveLineBreaks?: boolean | undefined, conservativeCollapse?: boolean | undefined}} options
|
|
@@ -81,5 +89,5 @@ declare function collapseWhitespaceSmart(str: string, prevTag: string, nextTag:
|
|
|
81
89
|
declare function canCollapseWhitespace(tag: string): boolean;
|
|
82
90
|
/** @param {string} tag */
|
|
83
91
|
declare function canTrimWhitespace(tag: string): boolean;
|
|
84
|
-
export { buildInlineSets, defaultInlineSets, endsWithWhitespace, trimWhitespace, collapseWhitespaceAll, collapseWhitespace, collapseWhitespaceSmart, canCollapseWhitespace, canTrimWhitespace };
|
|
92
|
+
export { buildInlineSets, defaultInlineSets, endsWithWhitespace, trimWhitespace, collapseWhitespaceAll, collapseNoBreakSpaces, collapseWhitespace, collapseWhitespaceSmart, canCollapseWhitespace, canTrimWhitespace };
|
|
85
93
|
//# sourceMappingURL=whitespace.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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;AAwCD;;;GAGG;AACH,iBAAS,qBAAqB,CAAC,GAAG,EAHvB,MAGuB,EAAE,OAAO,EAFhC;IAAC,cAAc,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAAC,kBAAkB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAE/C,UAU1C;AAID;;;;;;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,qBAAqB,EACrB,kBAAkB,EAClB,uBAAuB,EACvB,qBAAqB,EACrB,iBAAiB,EAClB,CAAC"}
|
|
@@ -67,6 +67,10 @@
|
|
|
67
67
|
"description": "Collapse whitespace more aggressively between inline elements—use with `--collapse-whitespace`",
|
|
68
68
|
"type": "boolean"
|
|
69
69
|
},
|
|
70
|
+
"collapseNoBreakSpaces": {
|
|
71
|
+
"description": "Remove whitespace next to no-break spaces, narrow no-break spaces, and figure spaces in text (e.g., `a b` → `a b`), keeping runs of them—use with `--collapse-whitespace`",
|
|
72
|
+
"type": "boolean"
|
|
73
|
+
},
|
|
70
74
|
"collapseWhitespace": {
|
|
71
75
|
"description": "Collapse whitespace that contributes to text nodes in a document tree",
|
|
72
76
|
"type": "boolean"
|
package/package.json
CHANGED
|
@@ -17,11 +17,11 @@
|
|
|
17
17
|
"@commitlint/cli": "^21.2.3",
|
|
18
18
|
"@eslint/js": "^10.0.1",
|
|
19
19
|
"@swc/core": "^1.16.2",
|
|
20
|
-
"@types/node": "^26.6.
|
|
20
|
+
"@types/node": "^26.6.3",
|
|
21
21
|
"eslint": "^10.11.0",
|
|
22
22
|
"globals": "^17.12.0",
|
|
23
23
|
"typescript": "^7.0.2",
|
|
24
|
-
"vite": "^8.3.
|
|
24
|
+
"vite": "^8.3.1"
|
|
25
25
|
},
|
|
26
26
|
"engines": {
|
|
27
27
|
"node": ">=22.13"
|
|
@@ -95,5 +95,5 @@
|
|
|
95
95
|
},
|
|
96
96
|
"type": "module",
|
|
97
97
|
"types": "dist/types/htmlminifier.d.ts",
|
|
98
|
-
"version": "8.
|
|
98
|
+
"version": "8.7.0"
|
|
99
99
|
}
|
package/src/htmlminifier.js
CHANGED
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
endsWithWhitespace,
|
|
38
38
|
trimWhitespace,
|
|
39
39
|
collapseWhitespaceAll,
|
|
40
|
+
collapseNoBreakSpaces,
|
|
40
41
|
collapseWhitespace,
|
|
41
42
|
collapseWhitespaceSmart,
|
|
42
43
|
canCollapseWhitespace as defaultCanCollapseWhitespace,
|
|
@@ -185,6 +186,15 @@ import { toFragment, replaceCustomFragments } from './lib/fragments.js';
|
|
|
185
186
|
*
|
|
186
187
|
* Default: `false`
|
|
187
188
|
*
|
|
189
|
+
* @prop {boolean} [collapseNoBreakSpaces]
|
|
190
|
+
* Remove whitespace next to a no-break space (`U+00A0`), narrow no-break
|
|
191
|
+
* space (`U+202F`), or figure space (`U+2007`) in text (for example
|
|
192
|
+
* `a b` → `a b`), which changes how the text renders. Runs of
|
|
193
|
+
* no-break spaces are kept.
|
|
194
|
+
* Must also enable `collapseWhitespace` to have effect.
|
|
195
|
+
*
|
|
196
|
+
* Default: `false`
|
|
197
|
+
*
|
|
188
198
|
* @prop {boolean} [collapseWhitespace]
|
|
189
199
|
* Collapse multiple whitespace characters into one where allowed. Also
|
|
190
200
|
* controls trimming behaviour in several code paths.
|
|
@@ -630,6 +640,10 @@ const RE_END_TAG = /^<\//;
|
|
|
630
640
|
const RE_WHITESPACE_ONLY = /^\s+$/;
|
|
631
641
|
const RE_TABS = /\t/g;
|
|
632
642
|
const RE_TRAILING_WHITESPACE = /\s$/;
|
|
643
|
+
const RE_LINE_BREAK_BLANKS_END = /(?:\r\n?|\n)[ \t\f]*$/;
|
|
644
|
+
const RE_BLANKS_LINE_BREAK_START = /^[ \t\f]*(?:\r\n?|\n)/;
|
|
645
|
+
const RE_BLANKS_START = /^[ \t\f]+/;
|
|
646
|
+
const RE_BLANKS_ONLY = /^[ \t\f]+$/;
|
|
633
647
|
|
|
634
648
|
// Pre-compiled patterns for `htmlmin:ignore` block content analysis
|
|
635
649
|
const RE_HTML_COMMENT_START = /^\s*<!--/;
|
|
@@ -1069,6 +1083,10 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1069
1083
|
let optionalStartTag = '';
|
|
1070
1084
|
let optionalEndTag = '';
|
|
1071
1085
|
let optionalEndTagEmitted = false;
|
|
1086
|
+
// Whether a node was removed since the last output
|
|
1087
|
+
let removed = false;
|
|
1088
|
+
// Whether the latest text was kept verbatim, as in `pre` or `script`
|
|
1089
|
+
let textVerbatim = false;
|
|
1072
1090
|
/** @type {string[]} */
|
|
1073
1091
|
const ignoredMarkupChunks = [];
|
|
1074
1092
|
/** @type {Array<RegExpExecArray | null>} */
|
|
@@ -1290,11 +1308,88 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1290
1308
|
function bufferPush(/** @type {string} */ entry, /** @type {boolean} */ isTag) {
|
|
1291
1309
|
buffer.push(entry);
|
|
1292
1310
|
bufferTags?.push(isTag);
|
|
1311
|
+
if (entry) removed = false;
|
|
1293
1312
|
}
|
|
1294
1313
|
|
|
1295
1314
|
function bufferTruncate(/** @type {number} */ length) {
|
|
1296
1315
|
buffer.length = Math.max(0, length);
|
|
1297
1316
|
if (bufferTags) bufferTags.length = buffer.length;
|
|
1317
|
+
removed = true;
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
// The nearest entries before and from `index` that aren’t empty, as removed nodes leave them
|
|
1321
|
+
/** @returns {[number, number]} */
|
|
1322
|
+
function findNeighbors(/** @type {number} */ index) {
|
|
1323
|
+
let before = index - 1;
|
|
1324
|
+
while (before >= 0 && buffer[before] === '') {
|
|
1325
|
+
before--;
|
|
1326
|
+
}
|
|
1327
|
+
let after = index;
|
|
1328
|
+
while (after < buffer.length && buffer[after] === '') {
|
|
1329
|
+
after++;
|
|
1330
|
+
}
|
|
1331
|
+
return [before, after];
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
// With whitespace kept as is, a removed node must not leave a line of nothing but
|
|
1335
|
+
// whitespace behind: The text around the removal (at `index`) drops that line. The start
|
|
1336
|
+
// of the output counts as a line break, and blank lines of the source stay.
|
|
1337
|
+
function removeWhitespaceLine(/** @type {number} */ index) {
|
|
1338
|
+
const [first, after] = findNeighbors(index);
|
|
1339
|
+
// The end of the output is left to `trimOutputWhitespace`
|
|
1340
|
+
if (after === buffer.length) {
|
|
1341
|
+
return;
|
|
1342
|
+
}
|
|
1343
|
+
const next = buffer[after] ?? '';
|
|
1344
|
+
if (!RE_BLANKS_LINE_BREAK_START.test(next)) {
|
|
1345
|
+
return;
|
|
1346
|
+
}
|
|
1347
|
+
// Blanks between nodes removed from the same line belong to that line
|
|
1348
|
+
let before = first;
|
|
1349
|
+
while (before >= 0 && (buffer[before] === '' || RE_BLANKS_ONLY.test(buffer[before] ?? ''))) {
|
|
1350
|
+
before--;
|
|
1351
|
+
}
|
|
1352
|
+
const prev = buffer[before] ?? '';
|
|
1353
|
+
if (before >= 0 && !RE_LINE_BREAK_BLANKS_END.test(prev)) {
|
|
1354
|
+
return;
|
|
1355
|
+
}
|
|
1356
|
+
for (let i = before + 1; i <= first; i++) {
|
|
1357
|
+
buffer[i] = '';
|
|
1358
|
+
}
|
|
1359
|
+
// The line break goes on the side of the text before the removal, so that the text after
|
|
1360
|
+
// it keeps its own for a removal that may follow
|
|
1361
|
+
if (before < 0) {
|
|
1362
|
+
buffer[after] = next.replace(RE_BLANKS_LINE_BREAK_START, '');
|
|
1363
|
+
} else {
|
|
1364
|
+
buffer[before] = prev.replace(RE_LINE_BREAK_BLANKS_END, '');
|
|
1365
|
+
buffer[after] = next.replace(RE_BLANKS_START, '');
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
// Whitespace at either end of the output goes, but not verbatim whitespace, nor whitespace
|
|
1370
|
+
// that `conservativeCollapse` or `preserveLineBreaks` promise to keep, nor that of partial
|
|
1371
|
+
// markup, whose ends join other markup
|
|
1372
|
+
function trimOutputWhitespace() {
|
|
1373
|
+
if (options.partialMarkup || (options.collapseWhitespace && (options.conservativeCollapse || options.preserveLineBreaks))) {
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
// Tags start with `<` and end with `>`, so whitespace at either end belongs to text
|
|
1377
|
+
if (!textVerbatim) {
|
|
1378
|
+
let index = buffer.length - 1;
|
|
1379
|
+
while (index >= 0 && (buffer[index] === '' || RE_WS_ONLY.test(buffer[index] ?? ''))) {
|
|
1380
|
+
buffer[index--] = '';
|
|
1381
|
+
}
|
|
1382
|
+
if (index >= 0) {
|
|
1383
|
+
buffer[index] = (buffer[index] ?? '').replace(RE_WS_END, '');
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
let index = 0;
|
|
1387
|
+
while (index < buffer.length && (buffer[index] === '' || RE_WS_ONLY.test(buffer[index] ?? ''))) {
|
|
1388
|
+
buffer[index++] = '';
|
|
1389
|
+
}
|
|
1390
|
+
if (index < buffer.length) {
|
|
1391
|
+
buffer[index] = (buffer[index] ?? '').replace(RE_WS_START, '');
|
|
1392
|
+
}
|
|
1298
1393
|
}
|
|
1299
1394
|
|
|
1300
1395
|
function removeStartTag() {
|
|
@@ -1322,14 +1417,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1322
1417
|
// single character it is due: a line break under `preserveLineBreaks`, a no-break space
|
|
1323
1418
|
// where the run holds one, a space under `conservativeCollapse`
|
|
1324
1419
|
function mergeWhitespaceRuns(/** @type {number} */ index) {
|
|
1325
|
-
|
|
1326
|
-
while (before >= 0 && buffer[before] === '') {
|
|
1327
|
-
before--;
|
|
1328
|
-
}
|
|
1329
|
-
let after = index;
|
|
1330
|
-
while (after < buffer.length && buffer[after] === '') {
|
|
1331
|
-
after++;
|
|
1332
|
-
}
|
|
1420
|
+
const [before, after] = findNeighbors(index);
|
|
1333
1421
|
if (before < 0 || after >= buffer.length) {
|
|
1334
1422
|
return;
|
|
1335
1423
|
}
|
|
@@ -1347,7 +1435,8 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1347
1435
|
}
|
|
1348
1436
|
}
|
|
1349
1437
|
|
|
1350
|
-
|
|
1438
|
+
// `insideNoTrim` stands in for the stack once the end tag that omits this one has left it
|
|
1439
|
+
function removeEndTag(/** @type {boolean} */ insideNoTrim = false) {
|
|
1351
1440
|
let index = buffer.length - 1;
|
|
1352
1441
|
while (index > 0 && !RE_END_TAG.test(buffer[index] ?? '')) {
|
|
1353
1442
|
index--;
|
|
@@ -1355,13 +1444,17 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1355
1444
|
// Text that follows the end tag is kept as is, so drop the tag alone. Whitespace kept
|
|
1356
1445
|
// verbatim—in `pre`, in `textarea`, or wherever `canTrimWhitespace` says so—counts as
|
|
1357
1446
|
// kept text too, no matter how whitespace is collapsed elsewhere.
|
|
1358
|
-
const noTrim = stackNoTrimWhitespace.length > 0;
|
|
1447
|
+
const noTrim = insideNoTrim || stackNoTrimWhitespace.length > 0;
|
|
1359
1448
|
if (index < buffer.length - 1 && (keepsWhitespace() || noTrim) && RE_END_TAG.test(buffer[index] ?? '')) {
|
|
1360
1449
|
buffer.splice(index, 1);
|
|
1361
1450
|
bufferTags?.splice(index, 1);
|
|
1362
|
-
//
|
|
1363
|
-
if (
|
|
1364
|
-
|
|
1451
|
+
// Collapsed whitespace can merge, kept whitespace can leave a line of only whitespace
|
|
1452
|
+
if (!noTrim) {
|
|
1453
|
+
if (options.collapseWhitespace) {
|
|
1454
|
+
mergeWhitespaceRuns(index);
|
|
1455
|
+
} else {
|
|
1456
|
+
removeWhitespaceLine(index);
|
|
1457
|
+
}
|
|
1365
1458
|
}
|
|
1366
1459
|
return;
|
|
1367
1460
|
}
|
|
@@ -1442,7 +1535,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1442
1535
|
// Multiple newlines are likely intentional formatting, single newline is often a template artifact
|
|
1443
1536
|
// Treat CRLF (`\r\n`), CR (`\r`), and LF (`\n`) as single line-ending units
|
|
1444
1537
|
if (textNextTag && textNextTag === '/' + topTag && /[^\r\n](?:\r\n|\r|\n)[ \t]*$/.test(text)) {
|
|
1445
|
-
text = text.replace(
|
|
1538
|
+
text = text.replace(RE_LINE_BREAK_BLANKS_END, '');
|
|
1446
1539
|
}
|
|
1447
1540
|
}
|
|
1448
1541
|
}
|
|
@@ -1511,6 +1604,9 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1511
1604
|
if (!stackNoCollapseWhitespace.length && textNextTag !== 'html' && !(textPrevTag && textNextTag)) {
|
|
1512
1605
|
text = collapseWhitespace(text, options, false, false, true);
|
|
1513
1606
|
}
|
|
1607
|
+
if (options.collapseNoBreakSpaces && !stackNoCollapseWhitespace.length && !holdsRawText()) {
|
|
1608
|
+
text = collapseNoBreakSpaces(text, options);
|
|
1609
|
+
}
|
|
1514
1610
|
}
|
|
1515
1611
|
return text;
|
|
1516
1612
|
}
|
|
@@ -1556,7 +1652,11 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1556
1652
|
}
|
|
1557
1653
|
}
|
|
1558
1654
|
charsPrevTag = /^\s*$/.test(text) ? textPrevTag : 'comment';
|
|
1559
|
-
|
|
1655
|
+
const rawText = Boolean(text) && holdsRawText();
|
|
1656
|
+
if (text) {
|
|
1657
|
+
textVerbatim = rawText || stackNoTrimWhitespace.length > 0;
|
|
1658
|
+
}
|
|
1659
|
+
if (options.decodeEntities && text && !rawText) {
|
|
1560
1660
|
// Escape any `&` symbols that start either:
|
|
1561
1661
|
// 1. a legacy-named character reference (i.e., one that doesn’t end with `;`)
|
|
1562
1662
|
// 2. or any other character reference (i.e., one that does end with `;`)
|
|
@@ -1581,7 +1681,11 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1581
1681
|
if (text) {
|
|
1582
1682
|
hasChars = true;
|
|
1583
1683
|
}
|
|
1684
|
+
const followsRemoval = removed && Boolean(text) && !options.collapseWhitespace && !stackNoTrimWhitespace.length;
|
|
1584
1685
|
bufferPush(text, false);
|
|
1686
|
+
if (followsRemoval) {
|
|
1687
|
+
removeWhitespaceLine(buffer.length - 1);
|
|
1688
|
+
}
|
|
1585
1689
|
}
|
|
1586
1690
|
|
|
1587
1691
|
// Comment finalization (sync): Optional tag handling, `htmlmin:ignore` whitespace collapsing, buffer push
|
|
@@ -1845,19 +1949,18 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1845
1949
|
optionalEndTagEmitted = false;
|
|
1846
1950
|
}
|
|
1847
1951
|
|
|
1848
|
-
// Set whitespace flags for nested tags (e.g., `<code>` within a `<pre>`)
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1952
|
+
// Set whitespace flags for nested tags (e.g., `<code>` within a `<pre>`); verbatim
|
|
1953
|
+
// whitespace is tracked without `collapseWhitespace` too, as `removeWhitespaceLine` spares it
|
|
1954
|
+
if (options.collapseWhitespace && !stackNoTrimWhitespace.length) {
|
|
1955
|
+
squashTrailingWhitespace(tag);
|
|
1956
|
+
}
|
|
1957
|
+
if (!unary) {
|
|
1958
|
+
if (!canTrimWhitespace(tag, attrs) || stackNoTrimWhitespace.length) {
|
|
1959
|
+
stackNoTrimWhitespace.push(tag);
|
|
1960
|
+
if (tag === 'pre' || tag === 'textarea') preTextareaDepth++;
|
|
1852
1961
|
}
|
|
1853
|
-
if (!
|
|
1854
|
-
|
|
1855
|
-
stackNoTrimWhitespace.push(tag);
|
|
1856
|
-
if (tag === 'pre' || tag === 'textarea') preTextareaDepth++;
|
|
1857
|
-
}
|
|
1858
|
-
if (!canCollapseWhitespace(tag, attrs) || stackNoCollapseWhitespace.length) {
|
|
1859
|
-
stackNoCollapseWhitespace.push(tag);
|
|
1860
|
-
}
|
|
1962
|
+
if (options.collapseWhitespace && (!canCollapseWhitespace(tag, attrs) || stackNoCollapseWhitespace.length)) {
|
|
1963
|
+
stackNoCollapseWhitespace.push(tag);
|
|
1861
1964
|
}
|
|
1862
1965
|
}
|
|
1863
1966
|
|
|
@@ -1991,19 +2094,18 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1991
2094
|
}
|
|
1992
2095
|
|
|
1993
2096
|
// Check if current tag is in a whitespace stack
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
}
|
|
2000
|
-
} else {
|
|
2001
|
-
squashTrailingWhitespace('/' + tag);
|
|
2002
|
-
}
|
|
2003
|
-
if (stackNoCollapseWhitespace.length &&
|
|
2004
|
-
tag === stackNoCollapseWhitespace[stackNoCollapseWhitespace.length - 1]) {
|
|
2005
|
-
stackNoCollapseWhitespace.pop();
|
|
2097
|
+
const insideNoTrim = stackNoTrimWhitespace.length > 0;
|
|
2098
|
+
if (stackNoTrimWhitespace.length) {
|
|
2099
|
+
if (tag === stackNoTrimWhitespace[stackNoTrimWhitespace.length - 1]) {
|
|
2100
|
+
if (tag === 'pre' || tag === 'textarea') preTextareaDepth--;
|
|
2101
|
+
stackNoTrimWhitespace.pop();
|
|
2006
2102
|
}
|
|
2103
|
+
} else if (options.collapseWhitespace) {
|
|
2104
|
+
squashTrailingWhitespace('/' + tag);
|
|
2105
|
+
}
|
|
2106
|
+
if (stackNoCollapseWhitespace.length &&
|
|
2107
|
+
tag === stackNoCollapseWhitespace[stackNoCollapseWhitespace.length - 1]) {
|
|
2108
|
+
stackNoCollapseWhitespace.pop();
|
|
2007
2109
|
}
|
|
2008
2110
|
|
|
2009
2111
|
let isElementEmpty = false;
|
|
@@ -2025,7 +2127,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
2025
2127
|
// (https://html.spec.whatwg.org/multipage/syntax.html#optional-tags);
|
|
2026
2128
|
// except for `</dt>` or `</thead>`, end tags may be omitted if no more content in parent element
|
|
2027
2129
|
if (tag && optionalEndTag && optionalEndTagEmitted && !trailingElements.has(optionalEndTag) && (optionalEndTag !== 'p' || (!pInlineElements.has(tag) && !tag.includes('-')))) {
|
|
2028
|
-
removeEndTag();
|
|
2130
|
+
removeEndTag(insideNoTrim);
|
|
2029
2131
|
}
|
|
2030
2132
|
optionalEndTag = optionalEndTags.has(tag) ? tag : '';
|
|
2031
2133
|
optionalEndTagEmitted = true;
|
|
@@ -2132,12 +2234,16 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
2132
2234
|
text = prefix + text + suffix;
|
|
2133
2235
|
} else {
|
|
2134
2236
|
text = '';
|
|
2237
|
+
removed = true;
|
|
2135
2238
|
}
|
|
2136
2239
|
} else {
|
|
2137
2240
|
text = prefix + text + suffix;
|
|
2138
2241
|
}
|
|
2139
2242
|
commentFinalize(text);
|
|
2140
2243
|
},
|
|
2244
|
+
strayEnd: function () {
|
|
2245
|
+
removed = true;
|
|
2246
|
+
},
|
|
2141
2247
|
doctype: function (/** @type {string} */ doctype) {
|
|
2142
2248
|
bufferPush(options.useShortDoctype
|
|
2143
2249
|
? '<!doctype' +
|
|
@@ -2177,9 +2283,12 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
2177
2283
|
removeEndTag();
|
|
2178
2284
|
}
|
|
2179
2285
|
}
|
|
2286
|
+
// Collapsing also trims whitespace before trailing inline end tags; trimming the output
|
|
2287
|
+
// catches what it leaves at either end, like a space after a removed leading comment
|
|
2180
2288
|
if (options.collapseWhitespace) {
|
|
2181
2289
|
squashTrailingWhitespace('br');
|
|
2182
2290
|
}
|
|
2291
|
+
trimOutputWhitespace();
|
|
2183
2292
|
|
|
2184
2293
|
return joinResultSegments(buffer, bufferTags ?? [], [uidAttr, uidIgnore].filter(marker => marker !== undefined), options, uidPattern
|
|
2185
2294
|
? function (/** @type {string} */ str) {
|
package/src/htmlparser.js
CHANGED
|
@@ -19,6 +19,7 @@ import { endlessRawTextElements, escapableRawTextElements, genericRawTextElement
|
|
|
19
19
|
* chars?: Function,
|
|
20
20
|
* comment?: Function,
|
|
21
21
|
* doctype?: Function,
|
|
22
|
+
* strayEnd?: Function,
|
|
22
23
|
* continueOnParseError?: boolean | undefined,
|
|
23
24
|
* partialMarkup?: boolean | undefined,
|
|
24
25
|
* wantsNextTag?: boolean | undefined,
|
|
@@ -1063,6 +1064,9 @@ export class HTMLParser {
|
|
|
1063
1064
|
if (handler.end) {
|
|
1064
1065
|
handler.end(tagName, []);
|
|
1065
1066
|
}
|
|
1067
|
+
} else if (handler.strayEnd) {
|
|
1068
|
+
// An end tag with nothing to close is dropped
|
|
1069
|
+
handler.strayEnd();
|
|
1066
1070
|
}
|
|
1067
1071
|
// Returned so the parse loop can skip lowercasing the name again
|
|
1068
1072
|
return lowerTagName;
|
|
@@ -39,6 +39,10 @@ const optionDefinitions = {
|
|
|
39
39
|
description: 'Collapse whitespace more aggressively between inline elements—use with `--collapse-whitespace`',
|
|
40
40
|
type: 'boolean'
|
|
41
41
|
},
|
|
42
|
+
collapseNoBreakSpaces: {
|
|
43
|
+
description: 'Remove whitespace next to no-break spaces, narrow no-break spaces, and figure spaces in text (e.g., `a b` → `a b`), keeping runs of them—use with `--collapse-whitespace`',
|
|
44
|
+
type: 'boolean'
|
|
45
|
+
},
|
|
42
46
|
collapseWhitespace: {
|
|
43
47
|
description: 'Collapse whitespace that contributes to text nodes in a document tree',
|
|
44
48
|
type: 'boolean'
|
package/src/lib/options.js
CHANGED
|
@@ -138,6 +138,7 @@ const optionDependencies = [
|
|
|
138
138
|
{ option: 'canMinifyJS', requires: 'minifyJS', unusable: replacedByOwnFunction('minifyJS'), clear: true },
|
|
139
139
|
{ option: 'canMinifySVG', requires: 'minifySVG', unusable: replacedByOwnFunction('minifySVG'), clear: true },
|
|
140
140
|
{ option: 'collapseInlineTagWhitespace', requires: 'collapseWhitespace' },
|
|
141
|
+
{ option: 'collapseNoBreakSpaces', requires: 'collapseWhitespace' },
|
|
141
142
|
{ option: 'conservativeCollapse', requires: 'collapseWhitespace' },
|
|
142
143
|
{ option: 'customEventAttributes', requires: 'minifyJS' },
|
|
143
144
|
{ option: 'inlineCustomElements', requires: 'collapseWhitespace' },
|
package/src/lib/unused-css.js
CHANGED
|
@@ -70,7 +70,7 @@ function unescapeIdentifier(identifier) {
|
|
|
70
70
|
if (!hex) {
|
|
71
71
|
return literal;
|
|
72
72
|
}
|
|
73
|
-
// Per CSS Syntax spec, a null, surrogate, or out-of-range escape becomes U+FFFD
|
|
73
|
+
// Per CSS Syntax spec, a null, surrogate, or out-of-range escape becomes `U+FFFD`
|
|
74
74
|
const code = parseInt(hex, 16);
|
|
75
75
|
return (code === 0 || code > 0x10FFFF || (code >= 0xD800 && code <= 0xDFFF))
|
|
76
76
|
? '\uFFFD'
|
package/src/lib/whitespace.js
CHANGED
|
@@ -88,6 +88,44 @@ function collapseWhitespaceAllKnown(str) {
|
|
|
88
88
|
return str.replace(RE_ALL_WS_NBSP, collapseRun);
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
// Collapse whitespace into adjacent no-break spaces
|
|
92
|
+
|
|
93
|
+
// A no-break space (`U+00A0`), narrow no-break space (`U+202F`), or figure space (`U+2007`),
|
|
94
|
+
// literal or as a character reference; numeric references and ` ` also work without
|
|
95
|
+
// the semicolon in text
|
|
96
|
+
const RE_NO_BREAK_SPACE_WS = /([ \n\r\t\f]*)(\xA0|\u202F|\u2007|&(?:nbsp;?|NonBreakingSpace;|numsp;|#0*(?:160|8239|8199)(?![0-9]);?|#[xX]0*(?:[aA]0|202[fF]|2007)(?![0-9a-fA-F]);?))([ \n\r\t\f]*)/g;
|
|
97
|
+
// Decoded text holds references only as text, so there the characters alone count
|
|
98
|
+
const RE_NO_BREAK_SPACE_CHAR_WS = /([ \n\r\t\f]*)([\xA0\u202F\u2007])([ \n\r\t\f]*)/g;
|
|
99
|
+
const RE_LINE_BREAK = /[\n\r]/;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @param {string} _match
|
|
103
|
+
* @param {string} before
|
|
104
|
+
* @param {string} noBreakSpace
|
|
105
|
+
* @param {string} after
|
|
106
|
+
*/
|
|
107
|
+
function keepLineBreaks(_match, before, noBreakSpace, after) {
|
|
108
|
+
return (RE_LINE_BREAK.test(before) ? before : '') + noBreakSpace + (RE_LINE_BREAK.test(after) ? after : '');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Callers pass text whose whitespace runs are already collapsed, as each match
|
|
112
|
+
// rescans the run in front of it
|
|
113
|
+
/**
|
|
114
|
+
* @param {string} str
|
|
115
|
+
* @param {{decodeEntities?: boolean | undefined, preserveLineBreaks?: boolean | undefined}} options
|
|
116
|
+
*/
|
|
117
|
+
function collapseNoBreakSpaces(str, options) {
|
|
118
|
+
const decoded = Boolean(options.decodeEntities);
|
|
119
|
+
if (!str || (str.indexOf('\xA0') === -1 && str.indexOf('\u202F') === -1 && str.indexOf('\u2007') === -1 && (decoded || str.indexOf('&') === -1))) {
|
|
120
|
+
return str;
|
|
121
|
+
}
|
|
122
|
+
const pattern = decoded ? RE_NO_BREAK_SPACE_CHAR_WS : RE_NO_BREAK_SPACE_WS;
|
|
123
|
+
if (options.preserveLineBreaks) {
|
|
124
|
+
return str.replace(pattern, keepLineBreaks);
|
|
125
|
+
}
|
|
126
|
+
return str.replace(pattern, '$2');
|
|
127
|
+
}
|
|
128
|
+
|
|
91
129
|
// Collapse whitespace with options
|
|
92
130
|
|
|
93
131
|
/**
|
|
@@ -356,6 +394,7 @@ export {
|
|
|
356
394
|
endsWithWhitespace,
|
|
357
395
|
trimWhitespace,
|
|
358
396
|
collapseWhitespaceAll,
|
|
397
|
+
collapseNoBreakSpaces,
|
|
359
398
|
collapseWhitespace,
|
|
360
399
|
collapseWhitespaceSmart,
|
|
361
400
|
canCollapseWhitespace,
|