html-minifier-next 8.4.5 → 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 +88 -9
- package/dist/types/htmlminifier.d.ts +32 -3
- package/dist/types/htmlminifier.d.ts.map +1 -1
- package/dist/types/htmlparser.d.ts.map +1 -1
- package/dist/types/lib/options.d.ts +11 -4
- package/dist/types/lib/options.d.ts.map +1 -1
- package/html-minifier-next.schema.json +3 -3
- package/package.json +3 -3
- package/src/htmlminifier.js +169 -23
- package/src/htmlparser.js +5 -2
- package/src/lib/option-definitions.js +3 -3
- package/src/lib/options.js +61 -3
package/README.md
CHANGED
|
@@ -108,7 +108,7 @@ HTML Minifier Next provides presets for common use cases. Presets are pre-config
|
|
|
108
108
|
* `conservative`: Basic minification with whitespace collapsing, comment removal, and removal of select attributes.
|
|
109
109
|
* `comprehensive`: More advanced minification for better file size reduction, including relevant conservative options plus attribute quote removal, optional tag removal, and more.
|
|
110
110
|
|
|
111
|
-
To review the specific options set, [presets.js](https://github.com/j9t/html-minifier-next/blob/main/src/presets.js) lists them in an accessible manner.
|
|
111
|
+
To review the specific options set, [presets.js](https://github.com/j9t/html-minifier-next/blob/main/src/presets.js) lists them in an accessible manner. For output served with Gzip or Brotli, see [“Optimizing for compression”](#optimizing-for-compression) for what to add.
|
|
112
112
|
|
|
113
113
|
**Using presets:**
|
|
114
114
|
|
|
@@ -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` |
|
|
@@ -172,7 +172,7 @@ Options can be used in config files (camelCase) or via CLI flags (kebab-case wit
|
|
|
172
172
|
| `removeComments`<br>`--remove-comments` | [Strip HTML comments](https://perfectionkills.com/experimenting-with-html-minifier/#remove_comments) | `false` |
|
|
173
173
|
| `removeDefaultTypeAttributes`<br>`--remove-default-type-attributes` | Remove default `type` attributes from `style`/`link` (e.g., `type="text/css"`) and `script` (e.g., `type="text/javascript"`) elements; other `type` attribute values are left intact | `false` |
|
|
174
174
|
| `removeEmptyAttributes`<br>`--remove-empty-attributes` | [Remove all attributes with whitespace-only values](https://perfectionkills.com/experimenting-with-html-minifier/#remove_empty_or_blank_attributes) | `false` (could be `true`, `Function(attrName, tag)`) |
|
|
175
|
-
| `removeEmptyElements`<br>`--remove-empty-elements` | [Remove all elements with empty contents](
|
|
175
|
+
| `removeEmptyElements`<br>`--remove-empty-elements` | [Remove all elements with empty contents](#removing-empty-elements) | `false` |
|
|
176
176
|
| `removeEmptyElementsExcept`<br>`--remove-empty-elements-except` | Array of elements to preserve—use with `removeEmptyElements`; accepts simple tag names (e.g., `["td"]`) or HTML-like markup with attributes (e.g., `["<span aria-hidden='true'>"]`); supports double quotes, single quotes, and unquoted attribute values | `[]` |
|
|
177
177
|
| `removeOptionalTags`<br>`--remove-optional-tags` | [Remove optional tags](https://perfectionkills.com/experimenting-with-html-minifier/#remove_optional_tags) | `false` |
|
|
178
178
|
| `removeRedundantAttributes`<br>`--remove-redundant-attributes` | [Remove attributes when value matches default](https://meiert.com/blog/optional-html/#toc-attribute-values) | `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
|
|
@@ -191,6 +191,9 @@ A few options take functions and are therefore only available programmatically,
|
|
|
191
191
|
| Option | Description | Default |
|
|
192
192
|
| --- | --- | --- |
|
|
193
193
|
| `canCollapseWhitespace` | `Function(tag, attrs, defaultFn)` that determines whether whitespace inside an element can be collapsed—override to protect additional elements, delegating to `defaultFn` for the rest | Built-in handling (protects `pre`, `textarea`, etc.) |
|
|
194
|
+
| `canMinifyCSS` | Synchronous `Function(text, type)` that determines whether `minifyCSS` may process a given piece of CSS—returning `false` [leaves it as if `minifyCSS` were off](#css-minification) | All CSS is minified |
|
|
195
|
+
| `canMinifyJS` | Synchronous `Function(text, inline)` that determines whether `minifyJS` may process a given piece of JavaScript—returning `false` [leaves it unminified](#javascript-minification) | All JavaScript is minified |
|
|
196
|
+
| `canMinifySVG` | Synchronous `Function(text)` that determines whether `minifySVG` may pass a given outermost `svg` element to SVGO—returning `false` [skips SVGO for it](#svg-minification) | All SVG is minified |
|
|
194
197
|
| `canTrimWhitespace` | `Function(tag, attrs, defaultFn)` that determines whether leading and trailing whitespace around an element may be trimmed | Built-in handling |
|
|
195
198
|
| `log` | `Function(message)` called with warnings and errors, including minification errors swallowed by `continueOnMinifyError` (e.g., pass `console.error` to surface them); the CLI wires this up under `--verbose` and `--dry` | No-op (errors are silent) |
|
|
196
199
|
|
|
@@ -204,6 +207,9 @@ HTML Minifier Next: Ignoring `conservativeCollapse`—use with `collapseWhitespa
|
|
|
204
207
|
|
|
205
208
|
| Option | Needs |
|
|
206
209
|
| --- | --- |
|
|
210
|
+
| `canMinifyCSS` | `minifyCSS`, and not [a function of your own](#css-minification) |
|
|
211
|
+
| `canMinifyJS` | `minifyJS`, and not [a function of your own](#javascript-minification) |
|
|
212
|
+
| `canMinifySVG` | `minifySVG`, and not [a function of your own](#svg-minification) |
|
|
207
213
|
| `collapseInlineTagWhitespace` | `collapseWhitespace` |
|
|
208
214
|
| `conservativeCollapse` | `collapseWhitespace` |
|
|
209
215
|
| `customEventAttributes` | `minifyJS` |
|
|
@@ -243,7 +249,44 @@ Where the modifiers disagree, the preserving one wins—`conservativeCollapse` a
|
|
|
243
249
|
|
|
244
250
|
### Sorting attributes and style classes
|
|
245
251
|
|
|
246
|
-
|
|
252
|
+
`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.
|
|
253
|
+
|
|
254
|
+
### Optimizing for compression
|
|
255
|
+
|
|
256
|
+
Most HTML is served compressed, so the Gzip or Brotli size is often what counts—and it doesn’t always follow the raw size. Added to the `comprehensive` preset, these options changed output size as follows (average per page, relative to `comprehensive`):
|
|
257
|
+
|
|
258
|
+
| Added to `comprehensive` | Raw | Gzip (level 6) | Brotli (quality 6) | Brotli (quality 11) |
|
|
259
|
+
| --- | --- | --- | --- | --- |
|
|
260
|
+
| `sortAttributes` | ±0% | −0.20% | −0.21% | −0.16% |
|
|
261
|
+
| `sortAttributes`, `removeAttributeQuotes: false`, `quoteCharacter: '"'` | +1.75% | +0.08% | −0.15% | −0.39% |
|
|
262
|
+
| `removeEmptyElements` | −1.15% | −0.75% | −0.68% | −0.63% |
|
|
263
|
+
|
|
264
|
+
* **Gzip or on-the-fly Brotli:** Add `sortAttributes`. The gain is small and uneven, however, and sorting can double minification time for very large documents.
|
|
265
|
+
* **Brotli precompressed at quality 11** (as for static files compressed at build time): Add `sortAttributes`, don’t remove attribute quotes, and make the quotes double quotes. Raw and Gzip output grow, so this only pays off when clients receive the precompressed Brotli files.
|
|
266
|
+
* **`removeEmptyElements`** saves more than either, but may change the respective document—see [“Removing empty elements”](#removing-empty-elements) before enabling it.
|
|
267
|
+
|
|
268
|
+
```shell
|
|
269
|
+
# Gzip or on-the-fly Brotli
|
|
270
|
+
npx html-minifier-next --preset comprehensive --sort-attributes input.html
|
|
271
|
+
|
|
272
|
+
# Brotli precompressed at quality 11
|
|
273
|
+
npx html-minifier-next --preset comprehensive --sort-attributes --no-remove-attribute-quotes --quote-character='"' input.html
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
These figures were measured in September 2026 with HMN 8.4.5 on 31 pages of the [backtest corpus](#regression-tests) (retrieved February 2026). They are indications, not guarantees, as they depend on the markup and will shift as HMN and the minifiers it bundles change. The [benchmark](#working-tree-benchmarks) reports Gzip and Brotli (quality 6) sizes to re-check them.
|
|
277
|
+
|
|
278
|
+
### Removing empty elements
|
|
279
|
+
|
|
280
|
+
`removeEmptyElements` removes elements without content—no text and no child elements (comments don’t count; whitespace does, unless `collapseWhitespace` removes it). It keeps:
|
|
281
|
+
|
|
282
|
+
* elements with an `id` attribute,
|
|
283
|
+
* `textarea` elements,
|
|
284
|
+
* `audio`, `video`, and `script` elements with a `src` attribute, `iframe` elements with `src` or `srcdoc`, `object` elements with `data`, and `applet` elements with `code`,
|
|
285
|
+
* elements inside SVG and MathML—though an empty `svg` or `math` element itself is removed, and HTML inside them (as in `foreignObject` or `annotation-xml`) is still processed.
|
|
286
|
+
|
|
287
|
+
Everything else goes, including elements that are empty on purpose: icons as well as icon links and buttons styled with CSS (e.g., `<i class="icon"></i>` or `<button aria-label="Close"></button>`), `canvas` elements that scripts draw into, empty `option` elements, and empty table cells (which shifts the cells that follow). Keep such elements with `removeEmptyElementsExcept`, for example `["td", "canvas", "<button aria-label>"]`. A parent that is left empty by the removal is kept.
|
|
288
|
+
|
|
289
|
+
Where the markup allows it, the option is effective: On a test corpus, it reduced output by 1.15% raw and 0.75% with Gzip on average (see [“Optimizing for compression”](#optimizing-for-compression)).
|
|
247
290
|
|
|
248
291
|
### CSS minification
|
|
249
292
|
|
|
@@ -284,9 +327,18 @@ const result = await minify(html, {
|
|
|
284
327
|
});
|
|
285
328
|
```
|
|
286
329
|
|
|
330
|
+
To exempt individual pieces of CSS, pass `canMinifyCSS` a synchronous function. It receives the CSS and its type—`undefined` for `style` elements, `'inline'` for `style` attributes, and `'media'` for `media` attributes. CSS for which it returns `false` comes out as if `minifyCSS` were off, so its URLs are not minified and its unused rules not removed, either. (Within `svg` elements, `minifySVG` has SVGO minify CSS as well—exempt those with [`canMinifySVG`](#svg-minification).)
|
|
331
|
+
|
|
332
|
+
```js
|
|
333
|
+
const result = await minify(html, {
|
|
334
|
+
minifyCSS: true,
|
|
335
|
+
canMinifyCSS: text => !text.includes('/* keep */')
|
|
336
|
+
});
|
|
337
|
+
```
|
|
338
|
+
|
|
287
339
|
### Unused CSS removal
|
|
288
340
|
|
|
289
|
-
`removeUnusedCSS` removes rules from `style` elements whose class or ID selectors
|
|
341
|
+
`removeUnusedCSS` removes rules from `style` elements whose class or ID selectors a document doesn’t reference. It needs to be used with `minifyCSS`, because the removal runs through Lightning CSS—passing `minifyCSS` a function of your own replaces that step, so the removal does not apply, either. Both cases are reported through [the `log` hook](#api-only-options). It does not touch `style` or `media` attributes.
|
|
290
342
|
|
|
291
343
|
```js
|
|
292
344
|
const result = await minify(html, {
|
|
@@ -384,6 +436,15 @@ const result = await minify(html, {
|
|
|
384
436
|
});
|
|
385
437
|
```
|
|
386
438
|
|
|
439
|
+
To exempt individual pieces of JavaScript, pass `canMinifyJS` a synchronous function. It receives the JavaScript and whether it is inline—`true` for event handler attributes (e.g., `onclick`), `false` for `script` elements. JavaScript for which it returns `false` is left unminified, though `mergeScripts` still merges it with adjacent scripts:
|
|
440
|
+
|
|
441
|
+
```js
|
|
442
|
+
const result = await minify(html, {
|
|
443
|
+
minifyJS: true,
|
|
444
|
+
canMinifyJS: text => !text.includes('/* keep */')
|
|
445
|
+
});
|
|
446
|
+
```
|
|
447
|
+
|
|
387
448
|
### SVG minification
|
|
388
449
|
|
|
389
450
|
When `minifySVG` is set to `true`, HTML Minifier Next uses [SVGO](https://svgo.dev/) to optimize inline SVG elements. Complete `<svg>` subtrees are extracted and processed as a block, enabling deep structural optimization:
|
|
@@ -420,6 +481,15 @@ const result = await minify(html, {
|
|
|
420
481
|
});
|
|
421
482
|
```
|
|
422
483
|
|
|
484
|
+
To exempt individual `svg` elements, pass `canMinifySVG` a synchronous function. It receives each outermost `svg` element, including any `svg` elements nested in it, as the HTML pass wrote it (e.g., without comments under `removeComments`), and one for which it returns `false` skips SVGO. To keep an `svg` element exactly as written, [wrap it in `<!-- htmlmin:ignore -->`](#ignoring-chunks-of-markup) instead.
|
|
485
|
+
|
|
486
|
+
```js
|
|
487
|
+
const result = await minify(html, {
|
|
488
|
+
minifySVG: true,
|
|
489
|
+
canMinifySVG: text => !text.includes('data-keep')
|
|
490
|
+
});
|
|
491
|
+
```
|
|
492
|
+
|
|
423
493
|
**Important:**
|
|
424
494
|
|
|
425
495
|
* SVG minification only applies within `<svg>` elements
|
|
@@ -610,6 +680,14 @@ For partial HTML fragments (such as template includes, SSI fragments, or closing
|
|
|
610
680
|
|
|
611
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/).
|
|
612
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
|
+
|
|
613
691
|
### Regex options and flags
|
|
614
692
|
|
|
615
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.
|
|
@@ -731,7 +809,7 @@ npm i;
|
|
|
731
809
|
npm run backtest
|
|
732
810
|
```
|
|
733
811
|
|
|
734
|
-
The backtest tool tracks minification performance across Git history. Results are saved in the backtest folder
|
|
812
|
+
The backtest tool tracks minification performance—output size (raw, Gzip, and Brotli) and processing time—across Git history. Results are saved in the backtest folder (results.json).
|
|
735
813
|
|
|
736
814
|
Parameters:
|
|
737
815
|
|
|
@@ -749,7 +827,7 @@ npm i;
|
|
|
749
827
|
npm run benchmark
|
|
750
828
|
```
|
|
751
829
|
|
|
752
|
-
It reuses the backtest corpus (run `npm run backtest` once to download it) and reports per-file output size and processing time.
|
|
830
|
+
It reuses the backtest corpus (run `npm run backtest` once to download it) and reports per-file output size and processing time. Sizes are given raw and compressed—at common defaults for on-the-fly compression, i.e., Gzip at level 6 and Brotli at quality 6—since a change that shrinks raw output can still grow what is transferred.
|
|
753
831
|
|
|
754
832
|
Parameters:
|
|
755
833
|
|
|
@@ -758,7 +836,8 @@ Parameters:
|
|
|
758
836
|
* `--core`: Disables the external minifiers (CSS, JS, SVG, URLs) to isolate HMN’s processing time
|
|
759
837
|
* `--cold`: Switches the minification caches off so CSS, JS, and SVG work is redone on every iteration—without it, warm caches serve those results from memory after the warm-up run and the benchmark cannot see changes to those minifiers
|
|
760
838
|
* `--iterations=N`: Sets the number of timed iterations (default: 5)
|
|
761
|
-
* `--config=PATH`: Uses an alternative options file (default: html-minifier-next.config.json)
|
|
839
|
+
* `--config=PATH`: Uses an alternative options file (default: html-minifier-next.config.json); the file can name a `preset` to start from
|
|
840
|
+
* `--preset=NAME`: Uses a preset instead of an options file (e.g., `--preset=comprehensive`)
|
|
762
841
|
|
|
763
842
|
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.
|
|
764
843
|
|
|
@@ -15,6 +15,34 @@ export type MinifierOptions = {
|
|
|
15
15
|
* Default: Built-in `canCollapseWhitespace` function
|
|
16
16
|
*/
|
|
17
17
|
canCollapseWhitespace?: (tag: string, attrs: HTMLAttribute[], canCollapseWhitespace: (tag: string) => boolean) => boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Synchronous predicate that determines whether the built-in `minifyCSS` may
|
|
20
|
+
* process a given piece of CSS—`type` is `undefined` for `style` elements,
|
|
21
|
+
* `'inline'` for `style` attributes, and `'media'` for `media` attributes.
|
|
22
|
+
* Returning false leaves that CSS as if `minifyCSS` were off, including its URLs
|
|
23
|
+
* and unused rules (within `svg` elements, SVGO still minifies it under `minifySVG`).
|
|
24
|
+
*
|
|
25
|
+
* Default: `null` (all CSS is minified)
|
|
26
|
+
*/
|
|
27
|
+
canMinifyCSS?: ((text: string, type?: 'inline' | 'media') => boolean) | null;
|
|
28
|
+
/**
|
|
29
|
+
* Synchronous predicate that determines whether the built-in `minifyJS` may
|
|
30
|
+
* process a given piece of JavaScript—`inline` is true for event handler
|
|
31
|
+
* attributes. Returning false leaves that JavaScript unminified (`mergeScripts`
|
|
32
|
+
* still applies).
|
|
33
|
+
*
|
|
34
|
+
* Default: `null` (all JavaScript is minified)
|
|
35
|
+
*/
|
|
36
|
+
canMinifyJS?: ((text: string, inline?: boolean) => boolean) | null;
|
|
37
|
+
/**
|
|
38
|
+
* Synchronous predicate that determines whether the built-in `minifySVG` may
|
|
39
|
+
* pass a given outermost `svg` element, nested ones included, to SVGO. It
|
|
40
|
+
* receives the element as serialized by the HTML pass (e.g., without comments
|
|
41
|
+
* under `removeComments`); returning false leaves it as that pass wrote it.
|
|
42
|
+
*
|
|
43
|
+
* Default: `null` (all SVG is minified)
|
|
44
|
+
*/
|
|
45
|
+
canMinifySVG?: ((text: string) => boolean) | null;
|
|
18
46
|
/**
|
|
19
47
|
* Predicate that determines whether leading/trailing whitespace around
|
|
20
48
|
* the element may be trimmed.
|
|
@@ -464,9 +492,10 @@ export type MinifierOptions = {
|
|
|
464
492
|
*/
|
|
465
493
|
sortClassNames?: boolean | ((value: string) => string);
|
|
466
494
|
/**
|
|
467
|
-
* When true, whitespace around ignored custom fragments
|
|
468
|
-
*
|
|
469
|
-
*
|
|
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.
|
|
470
499
|
*
|
|
471
500
|
* Default: `false`
|
|
472
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"}
|
|
@@ -21,6 +21,9 @@ export type ProcessedOptions = Omit<MinifierOptions, 'preset' | 'canCollapseWhit
|
|
|
21
21
|
minifyJS: (text: string, inline?: boolean, isModule?: boolean) => string | Promise<string>;
|
|
22
22
|
minifyURLs: (text: string) => string | Promise<string>;
|
|
23
23
|
minifySVG: ((svgContent: string) => string | Promise<string>) | null;
|
|
24
|
+
canMinifyCSS: ((text: string, type?: 'inline' | 'media') => boolean) | null;
|
|
25
|
+
canMinifyJS: ((text: string, inline?: boolean) => boolean) | null;
|
|
26
|
+
canMinifySVG: ((text: string) => boolean) | null;
|
|
24
27
|
removeUnusedCSS: {
|
|
25
28
|
safelist: Array<string | RegExp>;
|
|
26
29
|
scripts: boolean;
|
|
@@ -64,6 +67,9 @@ export type ProcessedOptions = Omit<MinifierOptions, 'preset' | 'canCollapseWhit
|
|
|
64
67
|
* minifyJS: (text: string, inline?: boolean, isModule?: boolean) => string | Promise<string>,
|
|
65
68
|
* minifyURLs: (text: string) => string | Promise<string>,
|
|
66
69
|
* minifySVG: ((svgContent: string) => string | Promise<string>) | null,
|
|
70
|
+
* canMinifyCSS: ((text: string, type?: 'inline' | 'media') => boolean) | null,
|
|
71
|
+
* canMinifyJS: ((text: string, inline?: boolean) => boolean) | null,
|
|
72
|
+
* canMinifySVG: ((text: string) => boolean) | null,
|
|
67
73
|
* removeUnusedCSS: {safelist: Array<string | RegExp>, scripts: boolean} | null,
|
|
68
74
|
* cssContext?: CSSContext,
|
|
69
75
|
* parallelJS?: boolean,
|
|
@@ -76,6 +82,7 @@ export type ProcessedOptions = Omit<MinifierOptions, 'preset' | 'canCollapseWhit
|
|
|
76
82
|
*/
|
|
77
83
|
/** @param {ProcessedOptions} options */
|
|
78
84
|
declare function shouldMinifyInnerHTML(options: ProcessedOptions): boolean;
|
|
85
|
+
declare const optionKeysExtra: Set<string>;
|
|
79
86
|
export type OptionDependency = OptionDependency[];
|
|
80
87
|
/**
|
|
81
88
|
* Options that do nothing on their own, and the option each one needs to take effect
|
|
@@ -94,13 +101,13 @@ export type OptionDependency = OptionDependency[];
|
|
|
94
101
|
declare const optionDependencies: ({
|
|
95
102
|
option: string;
|
|
96
103
|
requires: string;
|
|
104
|
+
unusable: (input: Record<string, any>) => "which a function of your own replaces" | false;
|
|
105
|
+
clear: boolean;
|
|
106
|
+
} | {
|
|
97
107
|
unusable?: never;
|
|
98
108
|
clear?: never;
|
|
99
|
-
} | {
|
|
100
109
|
option: string;
|
|
101
110
|
requires: string;
|
|
102
|
-
unusable: (/** @type {Record<string, any>} */ input: Record<string, any>) => "which a function of your own replaces" | false;
|
|
103
|
-
clear: boolean;
|
|
104
111
|
})[];
|
|
105
112
|
/**
|
|
106
113
|
* @param {MinifierOptions} inputOptions - User-provided options
|
|
@@ -116,5 +123,5 @@ declare const processOptions: (inputOptions: MinifierOptions, { getLightningCSS,
|
|
|
116
123
|
jsMinifyCache?: LRU | undefined;
|
|
117
124
|
svgMinifyCache?: LRU | undefined;
|
|
118
125
|
}) => ProcessedOptions;
|
|
119
|
-
export { optionDependencies, shouldMinifyInnerHTML, processOptions };
|
|
126
|
+
export { optionDependencies, optionKeysExtra, shouldMinifyInnerHTML, processOptions };
|
|
120
127
|
//# sourceMappingURL=options.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../../src/lib/options.js"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,GAAG,
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../../src/lib/options.js"],"names":[],"mappings":"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"}
|
|
@@ -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
|
@@ -89,11 +89,11 @@
|
|
|
89
89
|
"prepack": "npm run build",
|
|
90
90
|
"prepare": "git config core.hooksPath .githooks || true",
|
|
91
91
|
"serve": "vite",
|
|
92
|
-
"test": "npm run test:types && node --test \"test/**/*.test.js\"",
|
|
92
|
+
"test": "npm run test:types && node --test \"test/**/*.test.js\" \"backtest/*.test.js\"",
|
|
93
93
|
"test:types": "tsc --noEmit && tsc --project tsconfig.test.json",
|
|
94
|
-
"test:watch": "node --test --watch \"test/**/*.test.js\""
|
|
94
|
+
"test:watch": "node --test --watch \"test/**/*.test.js\" \"backtest/*.test.js\""
|
|
95
95
|
},
|
|
96
96
|
"type": "module",
|
|
97
97
|
"types": "dist/types/htmlminifier.d.ts",
|
|
98
|
-
"version": "8.
|
|
98
|
+
"version": "8.5.1"
|
|
99
99
|
}
|
package/src/htmlminifier.js
CHANGED
|
@@ -95,6 +95,31 @@ import { toFragment, replaceCustomFragments } from './lib/fragments.js';
|
|
|
95
95
|
*
|
|
96
96
|
* Default: Built-in `canCollapseWhitespace` function
|
|
97
97
|
*
|
|
98
|
+
* @prop {((text: string, type?: 'inline' | 'media') => boolean) | null} [canMinifyCSS]
|
|
99
|
+
* Synchronous predicate that determines whether the built-in `minifyCSS` may
|
|
100
|
+
* process a given piece of CSS—`type` is `undefined` for `style` elements,
|
|
101
|
+
* `'inline'` for `style` attributes, and `'media'` for `media` attributes.
|
|
102
|
+
* Returning false leaves that CSS as if `minifyCSS` were off, including its URLs
|
|
103
|
+
* and unused rules (within `svg` elements, SVGO still minifies it under `minifySVG`).
|
|
104
|
+
*
|
|
105
|
+
* Default: `null` (all CSS is minified)
|
|
106
|
+
*
|
|
107
|
+
* @prop {((text: string, inline?: boolean) => boolean) | null} [canMinifyJS]
|
|
108
|
+
* Synchronous predicate that determines whether the built-in `minifyJS` may
|
|
109
|
+
* process a given piece of JavaScript—`inline` is true for event handler
|
|
110
|
+
* attributes. Returning false leaves that JavaScript unminified (`mergeScripts`
|
|
111
|
+
* still applies).
|
|
112
|
+
*
|
|
113
|
+
* Default: `null` (all JavaScript is minified)
|
|
114
|
+
*
|
|
115
|
+
* @prop {((text: string) => boolean) | null} [canMinifySVG]
|
|
116
|
+
* Synchronous predicate that determines whether the built-in `minifySVG` may
|
|
117
|
+
* pass a given outermost `svg` element, nested ones included, to SVGO. It
|
|
118
|
+
* receives the element as serialized by the HTML pass (e.g., without comments
|
|
119
|
+
* under `removeComments`); returning false leaves it as that pass wrote it.
|
|
120
|
+
*
|
|
121
|
+
* Default: `null` (all SVG is minified)
|
|
122
|
+
*
|
|
98
123
|
* @prop {(tag: string | null, attrs: HTMLAttribute[] | undefined, canTrimWhitespace: (tag: string) => boolean) => boolean} [canTrimWhitespace]
|
|
99
124
|
* Predicate that determines whether leading/trailing whitespace around
|
|
100
125
|
* the element may be trimmed.
|
|
@@ -487,9 +512,10 @@ import { toFragment, replaceCustomFragments } from './lib/fragments.js';
|
|
|
487
512
|
* Default: `false`
|
|
488
513
|
*
|
|
489
514
|
* @prop {boolean} [trimCustomFragments]
|
|
490
|
-
* When true, whitespace around ignored custom fragments
|
|
491
|
-
*
|
|
492
|
-
*
|
|
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.
|
|
493
519
|
*
|
|
494
520
|
* Default: `false`
|
|
495
521
|
*
|
|
@@ -598,6 +624,7 @@ const DEFAULT_JS_TYPES = new Set(['', 'text/javascript', 'application/javascript
|
|
|
598
624
|
const RE_START_TAG = /^<[^/!]/;
|
|
599
625
|
const RE_END_TAG = /^<\//;
|
|
600
626
|
const RE_WHITESPACE_ONLY = /^\s+$/;
|
|
627
|
+
const RE_TABS = /\t/g;
|
|
601
628
|
const RE_TRAILING_WHITESPACE = /\s$/;
|
|
602
629
|
|
|
603
630
|
// Pre-compiled patterns for `htmlmin:ignore` block content analysis
|
|
@@ -1040,6 +1067,15 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1040
1067
|
const ignoredMarkupChunks = [];
|
|
1041
1068
|
/** @type {Array<RegExpExecArray | null>} */
|
|
1042
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();
|
|
1043
1079
|
/** @type {string | undefined} */
|
|
1044
1080
|
let uidIgnore;
|
|
1045
1081
|
/** @type {RegExp | undefined} */
|
|
@@ -1050,6 +1086,8 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1050
1086
|
let uidPattern;
|
|
1051
1087
|
/** @type {RegExp | undefined} */
|
|
1052
1088
|
let uidAttrLeadingPattern;
|
|
1089
|
+
/** @type {RegExp | undefined} */
|
|
1090
|
+
let uidAttrExactPattern;
|
|
1053
1091
|
// Create inline tags/text sets with custom elements
|
|
1054
1092
|
const customElementsInput = options.inlineCustomElements ?? [];
|
|
1055
1093
|
const customElementsArr = Array.isArray(customElementsInput) ? customElementsInput : Array.from(customElementsInput);
|
|
@@ -1124,12 +1162,14 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1124
1162
|
uidAttr = uniqueId(value);
|
|
1125
1163
|
uidPattern = new RegExp('(\\s*)' + uidAttr + '([0-9]+)' + uidAttr + '(\\s*)', 'g');
|
|
1126
1164
|
uidAttrLeadingPattern = new RegExp('^\\s*' + uidAttr + '(\\d+)' + uidAttr);
|
|
1165
|
+
uidAttrExactPattern = new RegExp('^' + uidAttr + '(\\d+)' + uidAttr + '$');
|
|
1127
1166
|
|
|
1128
1167
|
if (options.minifyCSS !== identity) {
|
|
1129
1168
|
options.minifyCSS = (function (/** @type {ProcessedOptions['minifyCSS']} */ fn) {
|
|
1130
1169
|
return function (/** @type {string} */ text, /** @type {string | undefined} */ type, /** @type {ProcessedOptions['cssContext']} */ context) {
|
|
1131
1170
|
text = text.replace(/** @type {RegExp} */ (uidPattern), function (/** @type {string} */ _match, /** @type {string} */ _prefix, /** @type {string} */ index) {
|
|
1132
1171
|
const chunks = ignoredCustomMarkupChunks[+index];
|
|
1172
|
+
customMarkupInCode.add(+index);
|
|
1133
1173
|
return (chunks?.[1] ?? '') + uidAttr + index + uidAttr + (chunks?.[2] ?? '');
|
|
1134
1174
|
});
|
|
1135
1175
|
|
|
@@ -1143,6 +1183,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1143
1183
|
return function (/** @type {string} */ text, /** @type {boolean | undefined} */ inline, /** @type {boolean | undefined} */ isModule) {
|
|
1144
1184
|
return fn(text.replace(/** @type {RegExp} */ (uidPattern), function (/** @type {string} */ _match, /** @type {string} */ _prefix, /** @type {string} */ index) {
|
|
1145
1185
|
const chunks = ignoredCustomMarkupChunks[+index];
|
|
1186
|
+
customMarkupInCode.add(+index);
|
|
1146
1187
|
return (chunks?.[1] ?? '') + uidAttr + index + uidAttr + (chunks?.[2] ?? '');
|
|
1147
1188
|
}), inline, isModule);
|
|
1148
1189
|
};
|
|
@@ -1158,6 +1199,77 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1158
1199
|
value = replaceCustomFragments(value, customFragments, replaceFragment);
|
|
1159
1200
|
}
|
|
1160
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
|
+
|
|
1161
1273
|
function canCollapseWhitespace(/** @type {string} */ tag, /** @type {HTMLAttribute[]} */ attrs) {
|
|
1162
1274
|
return options.canCollapseWhitespace(tag, attrs, defaultCanCollapseWhitespace);
|
|
1163
1275
|
}
|
|
@@ -1729,6 +1841,10 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1729
1841
|
|
|
1730
1842
|
buffer.push(openTag);
|
|
1731
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
|
+
|
|
1732
1848
|
// Remove duplicate attributes (per HTML spec, first occurrence wins)
|
|
1733
1849
|
// Duplicate attributes result in invalid HTML
|
|
1734
1850
|
// https://html.spec.whatwg.org/multipage/parsing.html#attribute-name-state
|
|
@@ -1738,11 +1854,17 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1738
1854
|
// By the time tags are processed, `createSortFns` has replaced any truthy non-function value
|
|
1739
1855
|
/** @type {(tag: string, attrs: HTMLAttribute[]) => void} */ (options.sortAttributes)(tag, attrs);
|
|
1740
1856
|
}
|
|
1857
|
+
if (tagFragment) {
|
|
1858
|
+
attrs.unshift(tagFragment);
|
|
1859
|
+
}
|
|
1741
1860
|
|
|
1742
1861
|
const attrResults = new Array(attrs.length);
|
|
1743
1862
|
let anyThenable = false;
|
|
1744
1863
|
for (let i = 0; i < attrs.length; i++) {
|
|
1745
|
-
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);
|
|
1746
1868
|
if (!anyThenable && isThenable(result)) {
|
|
1747
1869
|
anyThenable = true;
|
|
1748
1870
|
}
|
|
@@ -1760,11 +1882,18 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1760
1882
|
}
|
|
1761
1883
|
}
|
|
1762
1884
|
if (lastKeptIndex !== -1) {
|
|
1763
|
-
|
|
1885
|
+
if (!fragmentJoinsTag) {
|
|
1886
|
+
buffer.push(' ');
|
|
1887
|
+
}
|
|
1764
1888
|
for (let i = 0; i <= lastKeptIndex; i++) {
|
|
1765
1889
|
const normalizedAttr = normalizedAttrs[i];
|
|
1766
1890
|
if (normalizedAttr) {
|
|
1767
|
-
|
|
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);
|
|
1768
1897
|
}
|
|
1769
1898
|
}
|
|
1770
1899
|
} else if (optional && optionalStartTags.has(tag)) {
|
|
@@ -1790,7 +1919,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1790
1919
|
currentTag = '';
|
|
1791
1920
|
}
|
|
1792
1921
|
},
|
|
1793
|
-
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) {
|
|
1794
1923
|
// As in `start`: `lowerTag` stays '' when no foreign content is around
|
|
1795
1924
|
let lowerTag = '';
|
|
1796
1925
|
// Named as the start tag was: an `svg` or `math` element in its own context,
|
|
@@ -1808,6 +1937,18 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1808
1937
|
}
|
|
1809
1938
|
}
|
|
1810
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
|
+
}
|
|
1811
1952
|
|
|
1812
1953
|
// Check if current tag is in a whitespace stack
|
|
1813
1954
|
if (options.collapseWhitespace) {
|
|
@@ -1869,7 +2010,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1869
2010
|
if (autoGenerated && !options.includeAutoGeneratedTags) {
|
|
1870
2011
|
optionalEndTagEmitted = false;
|
|
1871
2012
|
} else {
|
|
1872
|
-
buffer.push(
|
|
2013
|
+
buffer.push(endTagText);
|
|
1873
2014
|
}
|
|
1874
2015
|
charsPrevTag = '/' + tag;
|
|
1875
2016
|
if (!inlineElements.has(tag)) {
|
|
@@ -1882,7 +2023,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1882
2023
|
if (autoGenerated && !options.includeAutoGeneratedTags) {
|
|
1883
2024
|
optionalEndTagEmitted = false;
|
|
1884
2025
|
} else {
|
|
1885
|
-
buffer.push(
|
|
2026
|
+
buffer.push(endTagText);
|
|
1886
2027
|
}
|
|
1887
2028
|
charsPrevTag = '/' + tag;
|
|
1888
2029
|
if (!inlineElements.has(tag)) {
|
|
@@ -1999,21 +2140,26 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1999
2140
|
|
|
2000
2141
|
return joinResultSegments(buffer, options, uidPattern
|
|
2001
2142
|
? function (/** @type {string} */ str) {
|
|
2002
|
-
return str.replace(/** @type {RegExp} */ (uidPattern), function (/** @type {string} */ match, /** @type {string} */ prefix, /** @type {string} */ index, /** @type {string} */ suffix) {
|
|
2003
|
-
|
|
2004
|
-
if (
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
chunk += suffix;
|
|
2010
|
-
}
|
|
2011
|
-
return collapseWhitespace(chunk, {
|
|
2012
|
-
preserveLineBreaks: options.preserveLineBreaks,
|
|
2013
|
-
conservativeCollapse: !options.trimCustomFragments
|
|
2014
|
-
}, /^[ \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];
|
|
2015
2150
|
}
|
|
2016
|
-
|
|
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);
|
|
2017
2163
|
});
|
|
2018
2164
|
}
|
|
2019
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: {
|
package/src/lib/options.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { createUrlMinifier } from './urls.js';
|
|
6
|
-
import { LRU, MAX_CACHE_ENTRY_SIZE, stableStringify, hashContent, identity, lowercase, paramCase, replaceAsync, parseRegExp, describeQuantifierRisk, lostFlag } from './utils.js';
|
|
6
|
+
import { LRU, MAX_CACHE_ENTRY_SIZE, stableStringify, hashContent, identity, isThenable, lowercase, paramCase, replaceAsync, parseRegExp, describeQuantifierRisk, lostFlag } from './utils.js';
|
|
7
7
|
import { RE_TRAILING_SEMICOLON, MISSING_DEPENDENCY, svgoPluginsInline } from './constants.js';
|
|
8
8
|
import { canCollapseWhitespace, canTrimWhitespace } from './whitespace.js';
|
|
9
9
|
import { wrapCSS, unwrapCSS } from './content.js';
|
|
@@ -47,6 +47,9 @@ import { optionDefinitions, optionDefaults } from './option-definitions.js';
|
|
|
47
47
|
* minifyJS: (text: string, inline?: boolean, isModule?: boolean) => string | Promise<string>,
|
|
48
48
|
* minifyURLs: (text: string) => string | Promise<string>,
|
|
49
49
|
* minifySVG: ((svgContent: string) => string | Promise<string>) | null,
|
|
50
|
+
* canMinifyCSS: ((text: string, type?: 'inline' | 'media') => boolean) | null,
|
|
51
|
+
* canMinifyJS: ((text: string, inline?: boolean) => boolean) | null,
|
|
52
|
+
* canMinifySVG: ((text: string) => boolean) | null,
|
|
50
53
|
* removeUnusedCSS: {safelist: Array<string | RegExp>, scripts: boolean} | null,
|
|
51
54
|
* cssContext?: CSSContext,
|
|
52
55
|
* parallelJS?: boolean,
|
|
@@ -97,7 +100,7 @@ function getUrlMinifyCache(site) {
|
|
|
97
100
|
}
|
|
98
101
|
|
|
99
102
|
// User-facing option keys that are valid but not listed in `optionDefinitions`
|
|
100
|
-
const optionKeysExtra = new Set(['preset', 'log', 'canCollapseWhitespace', 'canTrimWhitespace']);
|
|
103
|
+
const optionKeysExtra = new Set(['preset', 'log', 'canCollapseWhitespace', 'canTrimWhitespace', 'canMinifyCSS', 'canMinifyJS', 'canMinifySVG']);
|
|
101
104
|
|
|
102
105
|
// Unknown option keys and preset names already warned about—warn once per
|
|
103
106
|
// key per process, so repeated `minify` calls (e.g., batch runs) don’t flood STDERR
|
|
@@ -112,6 +115,10 @@ const unusedCSSWarned = new Set();
|
|
|
112
115
|
// Object-valued options handed a string, warned about once per distinct value
|
|
113
116
|
const stringValuesWarned = new Set();
|
|
114
117
|
|
|
118
|
+
// Clause for options that only the built-in minifier behind `key` reads
|
|
119
|
+
const replacedByOwnFunction = (/** @type {string} */ key) =>
|
|
120
|
+
(/** @type {Record<string, any>} */ input) => typeof input[key] === 'function' && 'which a function of your own replaces';
|
|
121
|
+
|
|
115
122
|
/**
|
|
116
123
|
* Options that do nothing on their own, and the option each one needs to take effect
|
|
117
124
|
*
|
|
@@ -127,6 +134,9 @@ const stringValuesWarned = new Set();
|
|
|
127
134
|
* @type {OptionDependency[]}
|
|
128
135
|
*/
|
|
129
136
|
const optionDependencies = [
|
|
137
|
+
{ option: 'canMinifyCSS', requires: 'minifyCSS', unusable: replacedByOwnFunction('minifyCSS'), clear: true },
|
|
138
|
+
{ option: 'canMinifyJS', requires: 'minifyJS', unusable: replacedByOwnFunction('minifyJS'), clear: true },
|
|
139
|
+
{ option: 'canMinifySVG', requires: 'minifySVG', unusable: replacedByOwnFunction('minifySVG'), clear: true },
|
|
130
140
|
{ option: 'collapseInlineTagWhitespace', requires: 'collapseWhitespace' },
|
|
131
141
|
{ option: 'conservativeCollapse', requires: 'collapseWhitespace' },
|
|
132
142
|
{ option: 'customEventAttributes', requires: 'minifyJS' },
|
|
@@ -138,7 +148,7 @@ const optionDependencies = [
|
|
|
138
148
|
option: 'removeUnusedCSS',
|
|
139
149
|
requires: 'minifyCSS',
|
|
140
150
|
// Removal rides along with Lightning CSS, which a function of one’s own replaces
|
|
141
|
-
unusable: (
|
|
151
|
+
unusable: replacedByOwnFunction('minifyCSS'),
|
|
142
152
|
clear: true
|
|
143
153
|
},
|
|
144
154
|
{ option: 'trimCustomFragments', requires: 'collapseWhitespace' }
|
|
@@ -152,6 +162,34 @@ function isRequested(value) {
|
|
|
152
162
|
return Array.isArray(value) ? value.length > 0 : Boolean(value);
|
|
153
163
|
}
|
|
154
164
|
|
|
165
|
+
// A `canMinify…` hook that returns false leaves the content as is, as does one that
|
|
166
|
+
// throws under `continueOnMinifyError`; hooks are synchronous, so a promise counts as an error
|
|
167
|
+
/**
|
|
168
|
+
* @param {ProcessedOptions} options - Processed options
|
|
169
|
+
* @param {'canMinifyCSS' | 'canMinifyJS' | 'canMinifySVG'} name - Hook to consult
|
|
170
|
+
* @param {...any} args - Arguments for the hook
|
|
171
|
+
* @returns {boolean}
|
|
172
|
+
*/
|
|
173
|
+
function declinesMinify(options, name, ...args) {
|
|
174
|
+
const hook = /** @type {((...args: any[]) => unknown) | null} */ (options[name]);
|
|
175
|
+
if (!hook) return false;
|
|
176
|
+
try {
|
|
177
|
+
const result = hook(...args);
|
|
178
|
+
if (isThenable(result)) {
|
|
179
|
+
// Keep a later rejection from surfacing as unhandled
|
|
180
|
+
/** @type {Promise<unknown>} */ (result).catch(() => {});
|
|
181
|
+
throw new TypeError(`\`${name}\` must return a boolean, not a promise`);
|
|
182
|
+
}
|
|
183
|
+
return !result;
|
|
184
|
+
} catch (err) {
|
|
185
|
+
if (!options.continueOnMinifyError) {
|
|
186
|
+
throw err;
|
|
187
|
+
}
|
|
188
|
+
options.log && options.log(err);
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
155
193
|
/** @param {unknown} err - Error raised while minifying */
|
|
156
194
|
function isMissingDependency(err) {
|
|
157
195
|
return Boolean(err) && /** @type {any} */ (err).code === MISSING_DEPENDENCY;
|
|
@@ -198,6 +236,9 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, getS
|
|
|
198
236
|
minifyJS: identity,
|
|
199
237
|
minifyURLs: identity,
|
|
200
238
|
minifySVG: null,
|
|
239
|
+
canMinifyCSS: null,
|
|
240
|
+
canMinifyJS: null,
|
|
241
|
+
canMinifySVG: null,
|
|
201
242
|
removeUnusedCSS: null
|
|
202
243
|
};
|
|
203
244
|
|
|
@@ -291,6 +332,10 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, getS
|
|
|
291
332
|
if (typeof option === 'function') {
|
|
292
333
|
options.log = option;
|
|
293
334
|
}
|
|
335
|
+
} else if (key === 'canMinifyCSS' || key === 'canMinifyJS' || key === 'canMinifySVG') {
|
|
336
|
+
if (typeof option === 'function') {
|
|
337
|
+
optionsDynamic[key] = option;
|
|
338
|
+
}
|
|
294
339
|
} else if (key === 'minifyCSS' && typeof option !== 'function') {
|
|
295
340
|
if (!option || !getLightningCSS || !cssMinifyCache) {
|
|
296
341
|
return;
|
|
@@ -307,6 +352,10 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, getS
|
|
|
307
352
|
return text;
|
|
308
353
|
}
|
|
309
354
|
|
|
355
|
+
if (declinesMinify(options, 'canMinifyCSS', text, type)) {
|
|
356
|
+
return text;
|
|
357
|
+
}
|
|
358
|
+
|
|
310
359
|
// Warnings are stored with the minified result and replayed on every hit, so a
|
|
311
360
|
// second document with the same defect hears about it, too; `context.warned`
|
|
312
361
|
// then keeps one document from repeating itself. Reporting from the cache
|
|
@@ -501,6 +550,10 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, getS
|
|
|
501
550
|
return '';
|
|
502
551
|
}
|
|
503
552
|
|
|
553
|
+
if (declinesMinify(options, 'canMinifyJS', text, inline)) {
|
|
554
|
+
return text;
|
|
555
|
+
}
|
|
556
|
+
|
|
504
557
|
// Hybrid strategy: Always use Terser for inline JS (needs bare returns support)
|
|
505
558
|
// Use user’s chosen engine for script blocks
|
|
506
559
|
const useEngine = inline ? 'terser' : engine;
|
|
@@ -639,6 +692,10 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, getS
|
|
|
639
692
|
return svgContent;
|
|
640
693
|
}
|
|
641
694
|
|
|
695
|
+
if (declinesMinify(options, 'canMinifySVG', svgContent)) {
|
|
696
|
+
return svgContent;
|
|
697
|
+
}
|
|
698
|
+
|
|
642
699
|
const isCacheable = svgContent.length <= MAX_CACHE_ENTRY_SIZE;
|
|
643
700
|
const svgKey = isCacheable
|
|
644
701
|
? (svgContent.length > 2048
|
|
@@ -754,6 +811,7 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, getS
|
|
|
754
811
|
|
|
755
812
|
export {
|
|
756
813
|
optionDependencies,
|
|
814
|
+
optionKeysExtra,
|
|
757
815
|
shouldMinifyInnerHTML,
|
|
758
816
|
processOptions
|
|
759
817
|
};
|