html-minifier-next 8.2.2 → 8.3.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 +28 -6
- package/cli.js +1 -4
- 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 +27 -1
- package/dist/types/lib/options.d.ts.map +1 -1
- package/dist/types/lib/utils.d.ts +3 -1
- package/dist/types/lib/utils.d.ts.map +1 -1
- package/html-minifier-next.schema.json +5 -5
- package/package.json +5 -5
- package/src/htmlminifier.js +26 -4
- package/src/htmlparser.js +183 -136
- package/src/lib/option-definitions.js +5 -5
- package/src/lib/options.js +63 -14
- package/src/lib/utils.js +7 -0
package/README.md
CHANGED
|
@@ -145,12 +145,12 @@ Options can be used in config files (camelCase) or via CLI flags (kebab-case wit
|
|
|
145
145
|
| `customAttrAssign`<br>`--custom-attr-assign` | Array of regexes that allow to support custom attribute assign expressions (e.g., `<div flex?="{{mode != cover}}"></div>`) | `[]` |
|
|
146
146
|
| `customAttrCollapse`<br>`--custom-attr-collapse` | Regex that specifies custom attribute to strip newlines from (e.g., `/ng-class/`) | `undefined` |
|
|
147
147
|
| `customAttrSurround`<br>`--custom-attr-surround` | Array of regexes that allow to support custom attribute surround expressions (e.g., `<input {{#if value}}checked="checked"{{/if}}>`) | `[]` |
|
|
148
|
-
| `customEventAttributes`<br>`--custom-event-attributes` | Array of regexes that allow to support custom event attributes
|
|
148
|
+
| `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,}$/ ]` |
|
|
149
149
|
| `decodeEntities`<br>`--decode-entities` | Use direct Unicode characters whenever possible | `false` |
|
|
150
150
|
| `ignoreCustomComments`<br>`--ignore-custom-comments` | Array of regexes that allow to ignore matching comments | `[ /^!/, /^\s*#/ ]` |
|
|
151
151
|
| `ignoreCustomFragments`<br>`--ignore-custom-fragments` | Array of regexes that allow to ignore certain fragments, when matched (e.g., `<?php … ?>`, `{{ … }}`, etc.) | `[ /<%[\s\S]*?%>/, /<\?[\s\S]*?\?>/ ]` |
|
|
152
152
|
| `includeAutoGeneratedTags`<br>`--include-auto-generated-tags` | Insert elements generated by HTML parser | `false` |
|
|
153
|
-
| `inlineCustomElements`<br>`--inline-custom-elements` | Array of names of custom elements which are inline, for whitespace handling | `[]` |
|
|
153
|
+
| `inlineCustomElements`<br>`--inline-custom-elements` | Array of names of custom elements which are inline, for whitespace handling—use with `collapseWhitespace` | `[]` |
|
|
154
154
|
| `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` |
|
|
155
155
|
| `maxInputLength`<br>`--max-input-length` | Maximum input length to prevent ReDoS attacks (disabled by default) | `undefined` |
|
|
156
156
|
| `maxLineLength`<br>`--max-line-length` | Specify a maximum line length; compressed output will be split by newlines at valid HTML split-points | `undefined` |
|
|
@@ -159,7 +159,7 @@ Options can be used in config files (camelCase) or via CLI flags (kebab-case wit
|
|
|
159
159
|
| `minifyJS`<br>`--minify-js` | Minify JavaScript in `script` elements and event attributes (uses [Terser](https://terser.org/) or [SWC](https://swc.rs/)) | `false` (could be `true`, `Object`, `Function(text, inline)`) |
|
|
160
160
|
| `minifySVG`<br>`--minify-svg` | Minify SVG elements (uses [SVGO](https://svgo.dev/)) | `false` (could be `true`, `Object`) |
|
|
161
161
|
| `minifyURLs`<br>`--minify-urls` | Minify URLs in various attributes | `false` (could be `true`, `String`, `Object`, `Function(text)`) |
|
|
162
|
-
| `noNewlinesBeforeTagClose`<br>`--no-newlines-before-tag-close` | Never add a newline before a tag that closes an element | `false` |
|
|
162
|
+
| `noNewlinesBeforeTagClose`<br>`--no-newlines-before-tag-close` | Never add a newline before a tag that closes an element—use with `maxLineLength` | `false` |
|
|
163
163
|
| `partialMarkup`<br>`--partial-markup` | Treat input as a partial HTML fragment, preserving stray end tags (closing tags without opening tags) and preventing auto-closing of unclosed tags at end of input | `false` |
|
|
164
164
|
| `preserveLineBreaks`<br>`--preserve-line-breaks` | Always collapse to one line break (never remove it entirely) when whitespace between tags includes a line break—use with [`collapseWhitespace`](#combining-whitespace-options) | `false` |
|
|
165
165
|
| `preventAttributesEscaping`<br>`--prevent-attributes-escaping` | Prevents the escaping of the values of attributes | `false` |
|
|
@@ -170,7 +170,7 @@ Options can be used in config files (camelCase) or via CLI flags (kebab-case wit
|
|
|
170
170
|
| `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` |
|
|
171
171
|
| `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)`) |
|
|
172
172
|
| `removeEmptyElements`<br>`--remove-empty-elements` | [Remove all elements with empty contents](https://perfectionkills.com/experimenting-with-html-minifier/#remove_empty_elements) | `false` |
|
|
173
|
-
| `removeEmptyElementsExcept`<br>`--remove-empty-elements-except` | Array of elements to preserve
|
|
173
|
+
| `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 | `[]` |
|
|
174
174
|
| `removeOptionalTags`<br>`--remove-optional-tags` | [Remove optional tags](https://perfectionkills.com/experimenting-with-html-minifier/#remove_optional_tags) | `false` |
|
|
175
175
|
| `removeRedundantAttributes`<br>`--remove-redundant-attributes` | [Remove attributes when value matches default](https://meiert.com/blog/optional-html/#toc-attribute-values) | `false` |
|
|
176
176
|
| `removeTagWhitespace`<br>`--remove-tag-whitespace` | Remove space between attributes whenever possible; **note that this will result in invalid HTML** | `false` |
|
|
@@ -178,7 +178,7 @@ Options can be used in config files (camelCase) or via CLI flags (kebab-case wit
|
|
|
178
178
|
| `sortAttributes`<br>`--sort-attributes` | [Sort attributes by frequency](#sorting-attributes-and-style-classes) | `false` |
|
|
179
179
|
| `sortClassNames`<br>`--sort-class-names` | [Sort style classes by frequency](#sorting-attributes-and-style-classes) | `false` |
|
|
180
180
|
| `strictCustomFragments`<br>`--strict-custom-fragments` | [Reject `ignoreCustomFragments` patterns that risk catastrophic backtracking](#redos-protection) (rather than warning about them) | `false` |
|
|
181
|
-
| `trimCustomFragments`<br>`--trim-custom-fragments` | Trim whitespace around custom fragments (`ignoreCustomFragments`) | `false` |
|
|
181
|
+
| `trimCustomFragments`<br>`--trim-custom-fragments` | Trim whitespace around custom fragments (`ignoreCustomFragments`)—use with `collapseWhitespace` | `false` |
|
|
182
182
|
| `useShortDoctype`<br>`--use-short-doctype` | [Replaces the doctype with the short HTML doctype](https://perfectionkills.com/experimenting-with-html-minifier/#use_short_doctype) | `false` |
|
|
183
183
|
|
|
184
184
|
### API-only options
|
|
@@ -191,6 +191,28 @@ A few options take functions and are therefore only available programmatically,
|
|
|
191
191
|
| `canTrimWhitespace` | `Function(tag, attrs, defaultFn)` that determines whether leading and trailing whitespace around an element may be trimmed | Built-in handling |
|
|
192
192
|
| `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) |
|
|
193
193
|
|
|
194
|
+
### Options that rely on another option
|
|
195
|
+
|
|
196
|
+
Some options modify what another option does, and do nothing when that other option is off. Setting one on its own is reported through [the `log` hook](#api-only-options) (and, in the CLI, on STDERR), once per message per run:
|
|
197
|
+
|
|
198
|
+
```text
|
|
199
|
+
HTML Minifier Next: Ignoring `conservativeCollapse`—use with `collapseWhitespace` (`--collapse-whitespace`)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
| Option | Needs |
|
|
203
|
+
| --- | --- |
|
|
204
|
+
| `collapseInlineTagWhitespace` | `collapseWhitespace` |
|
|
205
|
+
| `conservativeCollapse` | `collapseWhitespace` |
|
|
206
|
+
| `customEventAttributes` | `minifyJS` |
|
|
207
|
+
| `inlineCustomElements` | `collapseWhitespace` |
|
|
208
|
+
| `noNewlinesBeforeTagClose` | `maxLineLength` |
|
|
209
|
+
| `preserveLineBreaks` | `collapseWhitespace` |
|
|
210
|
+
| `removeEmptyElementsExcept` | `removeEmptyElements` |
|
|
211
|
+
| `removeUnusedCSS` | `minifyCSS`, and not [a function of your own](#unused-css-removal) |
|
|
212
|
+
| `trimCustomFragments` | `collapseWhitespace` |
|
|
213
|
+
|
|
214
|
+
Passing the option `false`, or an empty array, asks for nothing and is not reported. `cacheCSS`, `cacheJS`, and `cacheSVG` are not listed: They size a cache rather than transform markup, and don’t change output.
|
|
215
|
+
|
|
194
216
|
### Combining whitespace options
|
|
195
217
|
|
|
196
218
|
`collapseInlineTagWhitespace`, `conservativeCollapse`, and `preserveLineBreaks` are modifiers: They do nothing on their own, and only take effect when `collapseWhitespace` is enabled.
|
|
@@ -673,7 +695,7 @@ For CLI usage, using a config file is strongly recommended to avoid complex shel
|
|
|
673
695
|
|
|
674
696
|
**[Web demo:](https://j9t.github.io/html-minifier-next/)**
|
|
675
697
|
|
|
676
|
-
```
|
|
698
|
+
```text
|
|
677
699
|
\{%[\s\S]{0,1000}?%\} \{\{[\s\S]{0,500}?\}\}
|
|
678
700
|
```
|
|
679
701
|
|
package/cli.js
CHANGED
|
@@ -35,9 +35,6 @@ import os from 'os';
|
|
|
35
35
|
import readline from 'readline';
|
|
36
36
|
import { Command, Option } from 'commander';
|
|
37
37
|
|
|
38
|
-
// Simple case conversion for CLI option names (ASCII-only, no Unicode needed)
|
|
39
|
-
/** @param {string} str */
|
|
40
|
-
const paramCase = (str) => str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
|
41
38
|
/** @param {string} str */
|
|
42
39
|
const camelCase = (str) => paramCase(str).replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
43
40
|
|
|
@@ -54,7 +51,7 @@ const commanderOptionKey = (key) => {
|
|
|
54
51
|
|
|
55
52
|
// Lazy-load HMN to reduce CLI cold-start overhead
|
|
56
53
|
import { getPreset, getPresetNames } from './src/presets.js';
|
|
57
|
-
import { parseRegExp } from './src/lib/utils.js';
|
|
54
|
+
import { paramCase, parseRegExp } from './src/lib/utils.js';
|
|
58
55
|
import { optionDefinitions } from './src/lib/option-definitions.js';
|
|
59
56
|
|
|
60
57
|
const pkg = JSON.parse(fs.readFileSync(new URL('./package.json', import.meta.url), 'utf8'));
|
|
@@ -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;
|
|
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,kBAwyBV;CACF"}
|
|
@@ -71,6 +71,32 @@ export type ProcessedOptions = Omit<MinifierOptions, 'preset' | 'canCollapseWhit
|
|
|
71
71
|
*/
|
|
72
72
|
/** @param {ProcessedOptions} options */
|
|
73
73
|
declare function shouldMinifyInnerHTML(options: ProcessedOptions): boolean;
|
|
74
|
+
export type OptionDependency = OptionDependency[];
|
|
75
|
+
/**
|
|
76
|
+
* Options that do nothing on their own, and the option each one needs to take effect
|
|
77
|
+
*
|
|
78
|
+
* Cache sizes are deliberately absent: They never change output, so they are
|
|
79
|
+
* configuration for a subsystem rather than a transform that silently fails.
|
|
80
|
+
*
|
|
81
|
+
* @typedef {object} OptionDependency
|
|
82
|
+
* @property {string} option
|
|
83
|
+
* @property {string} requires
|
|
84
|
+
* @property {(input: Record<string, any>) => string | false} [unusable] Clause saying why the prerequisite does not count even though it is set
|
|
85
|
+
* @property {boolean} [clear] Whether to null the option out, where leaving it set would cost work downstream
|
|
86
|
+
*
|
|
87
|
+
* @type {OptionDependency[]}
|
|
88
|
+
*/
|
|
89
|
+
declare const optionDependencies: ({
|
|
90
|
+
option: string;
|
|
91
|
+
requires: string;
|
|
92
|
+
unusable?: never;
|
|
93
|
+
clear?: never;
|
|
94
|
+
} | {
|
|
95
|
+
option: string;
|
|
96
|
+
requires: string;
|
|
97
|
+
unusable: (/** @type {Record<string, any>} */ input: Record<string, any>) => "which a function of your own replaces" | false;
|
|
98
|
+
clear: boolean;
|
|
99
|
+
})[];
|
|
74
100
|
/**
|
|
75
101
|
* @param {MinifierOptions} inputOptions - User-provided options
|
|
76
102
|
* @param {{getLightningCSS?: Function | undefined, getTerser?: Function | undefined, getSwc?: Function | undefined, getSvgo?: Function | undefined, cssMinifyCache?: LRU | undefined, jsMinifyCache?: LRU | undefined, svgMinifyCache?: LRU | undefined}} [deps] - Dependencies from htmlminifier.js
|
|
@@ -85,5 +111,5 @@ declare const processOptions: (inputOptions: MinifierOptions, { getLightningCSS,
|
|
|
85
111
|
jsMinifyCache?: LRU | undefined;
|
|
86
112
|
svgMinifyCache?: LRU | undefined;
|
|
87
113
|
}) => ProcessedOptions;
|
|
88
|
-
export { shouldMinifyInnerHTML, processOptions };
|
|
114
|
+
export { optionDependencies, shouldMinifyInnerHTML, processOptions };
|
|
89
115
|
//# sourceMappingURL=options.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../../src/lib/options.js"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../../src/lib/options.js"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAmJ,MAAM,YAAY,CAAC;AAQ9K,OAAQ,KAAA,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AASrE,YAA4D,UAAU,GAA5D;IAAC,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAC,CAAY;AAOtE,YAA6C,SAAS,GAA5C;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAC,CAAW;AAUtD,YAiBG,gBAAgB,GAjBT,IAAI,CAAC,eAAe,EAAE,QAAQ,GAAG,uBAAuB,GAAG,mBAAmB,GAAG,sBAAsB,GAAG,KAAK,GAAG,WAAW,GAAG,UAAU,GAAG,YAAY,GAAG,WAAW,GAAG,iBAAiB,CAAC,GAAG;IACvM,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/B,GAAG,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,OAAO,CAAC;IAC/B,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,qBAAqB,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,KAAK,OAAO,CAAC;IAC7G,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,KAAK,OAAO,CAAC;IACzG,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3F,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,OAAO,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3F,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvD,SAAS,EAAE,CAAC,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC;IACrE,eAAe,EAAE;QAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAC,GAAG,IAAI,CAAC;IAC7E,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACtC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B,CAAkB;AA3CtB,2EAA2E;AAI3E;;;;;;GAMG;AAEH;;;;;GAKG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,wCAAwC;AACxC,iBAAS,qBAAqB,CAAC,OAAO,EAD1B,gBAC0B,WAUrC;AA+CE,YAAkB,gBAAgB,GAM3B,gBAAgB,EAAE,CAAC;AAZ7B;;;;;;;;;;;;;GAaG;AACH,QAAA,MAAM,kBAAkB;;;;;;;;eAYT,kCAAkC,QAAvB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;;IAI5C,CAAC;AAYF;;;;GAIG;AACH,QAAA,MAAM,cAAc,iBAJT,eAAe,mGACf;IAAC,eAAe,CAAC,EAAE,WAAW,SAAS,CAAC;IAAC,SAAS,CAAC,EAAE,WAAW,SAAS,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,SAAS,CAAC;IAAC,OAAO,CAAC,EAAE,WAAW,SAAS,CAAC;IAAC,cAAc,CAAC,EAAE,GAAG,GAAG,SAAS,CAAC;IAAC,aAAa,CAAC,EAAE,GAAG,GAAG,SAAS,CAAC;IAAC,cAAc,CAAC,EAAE,GAAG,GAAG,SAAS,CAAA;CAAC,KAC5O,gBA2iBZ,CAAC;AAIF,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,cAAc,EACf,CAAC"}
|
|
@@ -41,6 +41,8 @@ declare function identity(value: string): string;
|
|
|
41
41
|
declare function isThenable(value: unknown): value is PromiseLike<any>;
|
|
42
42
|
/** @param {string} value */
|
|
43
43
|
declare function lowercase(value: string): string;
|
|
44
|
+
/** @param {string} value */
|
|
45
|
+
declare function paramCase(value: string): string;
|
|
44
46
|
/**
|
|
45
47
|
* Asynchronously replace matches in a string.
|
|
46
48
|
* @param {string} str - Input string
|
|
@@ -93,5 +95,5 @@ declare function describeQuantifierRisk(pattern: RegExp | string): string | null
|
|
|
93
95
|
* @returns {number} Index of the closing `>`, or -1 if not found
|
|
94
96
|
*/
|
|
95
97
|
declare function findTagEnd(html: string, pos: number, limit?: number): number;
|
|
96
|
-
export { stableStringify, findTagEnd, LRU, MAX_CACHE_ENTRY_SIZE, hashContent, uniqueId, identity, isThenable, lowercase, replaceAsync, parseRegExp, embedSource, lostFlag, describeQuantifierRisk };
|
|
98
|
+
export { stableStringify, findTagEnd, LRU, MAX_CACHE_ENTRY_SIZE, hashContent, uniqueId, identity, isThenable, lowercase, paramCase, replaceAsync, parseRegExp, embedSource, lostFlag, describeQuantifierRisk };
|
|
97
99
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/lib/utils.js"],"names":[],"mappings":"AAEA;;;GAGG;AACH,iBAAS,eAAe,CAAC,GAAG,EAHjB,OAGiB,GAFf,MAAM,CAYlB;AAID,cAAM,GAAG;IAEA,KAAK;IACL,IAAI;IACJ,IAAI;IACT,mCAAmC;IAC9B,GAAG,EADG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IAJjC,YAAY,KAAK,SAAM,EAMtB;IACD,0BAA0B;IAC1B,GAAG,CAAC,GAAG,EADK,MACL,WAUN;IACD;;;OAGG;IACH,GAAG,CAAC,GAAG,EAHI,MAGJ,EAAE,KAAK,EAFH,OAEG,QAOb;IACD,0BAA0B;IAC1B,MAAM,CAAC,GAAG,EADE,MACF,QAA2B;IACrC,6EAA6E;IAC7E,KAAK,IADS;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAGxE;CACF;AAKD,QAAA,MAAM,oBAAoB,QAAc,CAAC;AAIzC,0BAA0B;AAC1B,iBAAS,WAAW,CAAC,GAAG,EADZ,MACY,UAOvB;AAID,4BAA4B;AAC5B,iBAAS,QAAQ,CAAC,KAAK,EADX,MACW,UAMtB;AAID,4BAA4B;AAC5B,iBAAS,QAAQ,CAAC,KAAK,EADX,MACW,UAEtB;AAED;;;GAGG;AACH,iBAAS,UAAU,CAAC,KAAK,EAHd,OAGc,GAFZ,KAAK,IAAI,WAAW,CAAC,GAAG,CAAC,CAIrC;AAED,4BAA4B;AAC5B,iBAAS,SAAS,CAAC,KAAK,EADZ,MACY,UAEvB;AAID;;;;;;GAMG;AACH,iBAAe,YAAY,CAAC,GAAG,EALpB,MAKoB,EAAE,KAAK,EAJ3B,MAI2B,EAAE,OAAO,UAAA,GAFlC,OAAO,CAAC,MAAM,CAAC,CAe3B;AAID,qCAAqC;AACrC,iBAAS,WAAW,CAAC,KAAK,EADd,MAAM,GAAG,MACK,sBAUzB;AAyYD;;;;;;;;;GASG;AACH,iBAAS,QAAQ,CAAC,OAAO,EAJd,MAIc,GAHZ,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAqDlC;AAED;;;;;;;;;GASG;AACH,iBAAS,WAAW,CAAC,OAAO,EAHjB,MAGiB,GAFf,MAAM,CAOlB;AAGE,YAA6D,MAAM,GAAzD;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,IAAI,CAAA;CAAC,CAE7D;AAqKD;;;;;GAKG;AACH,iBAAS,sBAAsB,CAAC,OAAO,EAL5B,MAAM,GAAG,MAKmB,GAH1B,MAAM,GAAG,IAAI,CAqBzB;AAED;;;;;;;GAOG;AACH,iBAAS,UAAU,CAAC,IAAI,EALb,MAKa,EAAE,GAAG,EAJlB,MAIkB,EAAE,KAAK,AAHjC,CACA,EADQ,MAGuC,GAFrC,MAAM,CAelB;AAID,OAAO,EACL,eAAe,EACf,UAAU,EACV,GAAG,EACH,oBAAoB,EACpB,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,SAAS,EACT,YAAY,EACZ,WAAW,EACX,WAAW,EACX,QAAQ,EACR,sBAAsB,EACvB,CAAC"}
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/lib/utils.js"],"names":[],"mappings":"AAEA;;;GAGG;AACH,iBAAS,eAAe,CAAC,GAAG,EAHjB,OAGiB,GAFf,MAAM,CAYlB;AAID,cAAM,GAAG;IAEA,KAAK;IACL,IAAI;IACJ,IAAI;IACT,mCAAmC;IAC9B,GAAG,EADG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IAJjC,YAAY,KAAK,SAAM,EAMtB;IACD,0BAA0B;IAC1B,GAAG,CAAC,GAAG,EADK,MACL,WAUN;IACD;;;OAGG;IACH,GAAG,CAAC,GAAG,EAHI,MAGJ,EAAE,KAAK,EAFH,OAEG,QAOb;IACD,0BAA0B;IAC1B,MAAM,CAAC,GAAG,EADE,MACF,QAA2B;IACrC,6EAA6E;IAC7E,KAAK,IADS;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAGxE;CACF;AAKD,QAAA,MAAM,oBAAoB,QAAc,CAAC;AAIzC,0BAA0B;AAC1B,iBAAS,WAAW,CAAC,GAAG,EADZ,MACY,UAOvB;AAID,4BAA4B;AAC5B,iBAAS,QAAQ,CAAC,KAAK,EADX,MACW,UAMtB;AAID,4BAA4B;AAC5B,iBAAS,QAAQ,CAAC,KAAK,EADX,MACW,UAEtB;AAED;;;GAGG;AACH,iBAAS,UAAU,CAAC,KAAK,EAHd,OAGc,GAFZ,KAAK,IAAI,WAAW,CAAC,GAAG,CAAC,CAIrC;AAED,4BAA4B;AAC5B,iBAAS,SAAS,CAAC,KAAK,EADZ,MACY,UAEvB;AAGD,4BAA4B;AAC5B,iBAAS,SAAS,CAAC,KAAK,EADZ,MACY,UAEvB;AAID;;;;;;GAMG;AACH,iBAAe,YAAY,CAAC,GAAG,EALpB,MAKoB,EAAE,KAAK,EAJ3B,MAI2B,EAAE,OAAO,UAAA,GAFlC,OAAO,CAAC,MAAM,CAAC,CAe3B;AAID,qCAAqC;AACrC,iBAAS,WAAW,CAAC,KAAK,EADd,MAAM,GAAG,MACK,sBAUzB;AAyYD;;;;;;;;;GASG;AACH,iBAAS,QAAQ,CAAC,OAAO,EAJd,MAIc,GAHZ,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAqDlC;AAED;;;;;;;;;GASG;AACH,iBAAS,WAAW,CAAC,OAAO,EAHjB,MAGiB,GAFf,MAAM,CAOlB;AAGE,YAA6D,MAAM,GAAzD;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,IAAI,CAAA;CAAC,CAE7D;AAqKD;;;;;GAKG;AACH,iBAAS,sBAAsB,CAAC,OAAO,EAL5B,MAAM,GAAG,MAKmB,GAH1B,MAAM,GAAG,IAAI,CAqBzB;AAED;;;;;;;GAOG;AACH,iBAAS,UAAU,CAAC,IAAI,EALb,MAKa,EAAE,GAAG,EAJlB,MAIkB,EAAE,KAAK,AAHjC,CACA,EADQ,MAGuC,GAFrC,MAAM,CAelB;AAID,OAAO,EACL,eAAe,EACf,UAAU,EACV,GAAG,EACH,oBAAoB,EACpB,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,SAAS,EACT,SAAS,EACT,YAAY,EACZ,WAAW,EACX,WAAW,EACX,QAAQ,EACR,sBAAsB,EACvB,CAAC"}
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
}
|
|
109
109
|
},
|
|
110
110
|
"customEventAttributes": {
|
|
111
|
-
"description": "Array of regexes that allow to support custom event attributes
|
|
111
|
+
"description": "Array of regexes that allow to support custom event attributes (e.g., `ng-click`)—use with `--minify-js`",
|
|
112
112
|
"type": [
|
|
113
113
|
"string",
|
|
114
114
|
"array"
|
|
@@ -146,7 +146,7 @@
|
|
|
146
146
|
"type": "boolean"
|
|
147
147
|
},
|
|
148
148
|
"inlineCustomElements": {
|
|
149
|
-
"description": "Array of names of custom elements which are inline, for whitespace handling",
|
|
149
|
+
"description": "Array of names of custom elements which are inline, for whitespace handling—use with `--collapse-whitespace`",
|
|
150
150
|
"type": [
|
|
151
151
|
"string",
|
|
152
152
|
"array"
|
|
@@ -203,7 +203,7 @@
|
|
|
203
203
|
]
|
|
204
204
|
},
|
|
205
205
|
"noNewlinesBeforeTagClose": {
|
|
206
|
-
"description": "Never add a newline before a tag that closes an element",
|
|
206
|
+
"description": "Never add a newline before a tag that closes an element—use with `--max-line-length`",
|
|
207
207
|
"type": "boolean"
|
|
208
208
|
},
|
|
209
209
|
"partialMarkup": {
|
|
@@ -253,7 +253,7 @@
|
|
|
253
253
|
"type": "boolean"
|
|
254
254
|
},
|
|
255
255
|
"removeEmptyElementsExcept": {
|
|
256
|
-
"description": "Array of elements to preserve
|
|
256
|
+
"description": "Array of elements to preserve (e.g., `td`, `<span aria-hidden=\"true\">`)—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`)",
|
|
293
|
+
"description": "Trim whitespace around custom fragments (`--ignore-custom-fragments`)—use with `--collapse-whitespace`",
|
|
294
294
|
"type": "boolean"
|
|
295
295
|
},
|
|
296
296
|
"strictCustomFragments": {
|
package/package.json
CHANGED
|
@@ -9,19 +9,19 @@
|
|
|
9
9
|
"commander": "^15.0.0",
|
|
10
10
|
"entities": "^8.0.0",
|
|
11
11
|
"lightningcss": "^1.33.0",
|
|
12
|
-
"svgo": "^4.0
|
|
13
|
-
"terser": "^5.
|
|
12
|
+
"svgo": "^4.1.0",
|
|
13
|
+
"terser": "^5.51.2"
|
|
14
14
|
},
|
|
15
15
|
"description": "Highly effective, super-configurable, well-tested web page minifier (enhanced successor to HTML Minifier)",
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"@commitlint/cli": "^21.2.2",
|
|
18
18
|
"@eslint/js": "^10.0.1",
|
|
19
19
|
"@swc/core": "^1.16.1",
|
|
20
|
-
"@types/node": "^26.
|
|
20
|
+
"@types/node": "^26.4.0",
|
|
21
21
|
"eslint": "^10.9.1",
|
|
22
22
|
"globals": "^17.11.0",
|
|
23
23
|
"typescript": "^7.0.2",
|
|
24
|
-
"vite": "^8.2.
|
|
24
|
+
"vite": "^8.2.2"
|
|
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.3.1"
|
|
99
99
|
}
|
package/src/htmlminifier.js
CHANGED
|
@@ -956,6 +956,27 @@ async function createSortFns(value, options, uidIgnore, uidAttr, ignoredMarkupCh
|
|
|
956
956
|
}
|
|
957
957
|
}
|
|
958
958
|
|
|
959
|
+
// Match `/^<\/([\w:-]+)>$/` by hand—the regex engine’s per-call setup costs more
|
|
960
|
+
// than the scan on these short buffer entries
|
|
961
|
+
/**
|
|
962
|
+
* @param {string} str
|
|
963
|
+
* @returns {string | null} The tag name, or `null` where the string is not a plain end tag
|
|
964
|
+
*/
|
|
965
|
+
function matchPlainEndTag(str) {
|
|
966
|
+
const end = str.length;
|
|
967
|
+
if (end < 4 || str.charCodeAt(0) !== 60 /* < */ || str.charCodeAt(1) !== 47 /* / */ || str.charCodeAt(end - 1) !== 62 /* > */) {
|
|
968
|
+
return null;
|
|
969
|
+
}
|
|
970
|
+
for (let i = 2; i < end - 1; i++) {
|
|
971
|
+
const code = str.charCodeAt(i);
|
|
972
|
+
// `[\w:-]`: Letters, digits, `_`, `:`, `-`
|
|
973
|
+
if (!((code >= 48 && code <= 57) || (code >= 65 && code <= 90) || (code >= 97 && code <= 122) || code === 95 || code === 58 || code === 45)) {
|
|
974
|
+
return null;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
return str.slice(2, end - 1);
|
|
978
|
+
}
|
|
979
|
+
|
|
959
980
|
/**
|
|
960
981
|
* @param {string} value - HTML content to minify
|
|
961
982
|
* @param {ProcessedOptions} options - Normalized minification options
|
|
@@ -1198,10 +1219,11 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1198
1219
|
function trimTrailingWhitespace(/** @type {number} */ index, /** @type {string} */ nextTag) {
|
|
1199
1220
|
for (let prevTag = ''; index >= 0 && canTrimWhitespace(prevTag, emptyAttrs); index--) {
|
|
1200
1221
|
const str = buffer[index] ?? '';
|
|
1201
|
-
const
|
|
1202
|
-
if (
|
|
1203
|
-
prevTag =
|
|
1204
|
-
} else if (
|
|
1222
|
+
const endTagName = matchPlainEndTag(str);
|
|
1223
|
+
if (endTagName !== null) {
|
|
1224
|
+
prevTag = endTagName;
|
|
1225
|
+
} else if (str.charCodeAt(str.length - 1) === 62 /* > */ ||
|
|
1226
|
+
(buffer[index] = collapseWhitespaceSmart(str, '', nextTag, emptyAttrs, emptyAttrs, options, inlineElements, inlineTextSet))) {
|
|
1205
1227
|
break;
|
|
1206
1228
|
}
|
|
1207
1229
|
}
|
package/src/htmlparser.js
CHANGED
|
@@ -103,6 +103,31 @@ const nonPhrasing = new Set(['address', 'article', 'aside', 'base', 'blockquote'
|
|
|
103
103
|
const endsTagName = (/** @type {string} */ character) =>
|
|
104
104
|
character === '' || character === '/' || character === '>' || /\s/.test(character);
|
|
105
105
|
|
|
106
|
+
// Fast path for tag names
|
|
107
|
+
/**
|
|
108
|
+
* @param {string} html
|
|
109
|
+
* @param {number} start - Position of the first name character (after `<` or `</`)
|
|
110
|
+
* @returns {number} Name length from `start`, or -1 to fall back to the qname regex
|
|
111
|
+
*/
|
|
112
|
+
function scanAsciiTagName(html, start) {
|
|
113
|
+
const first = html.charCodeAt(start);
|
|
114
|
+
// First char: Letter or `_` (as for ncname)
|
|
115
|
+
if (!((first >= 65 && first <= 90) || (first >= 97 && first <= 122) || first === 95)) {
|
|
116
|
+
return -1;
|
|
117
|
+
}
|
|
118
|
+
let i = start + 1;
|
|
119
|
+
for (;;) {
|
|
120
|
+
const code = html.charCodeAt(i);
|
|
121
|
+
if ((code >= 65 && code <= 90) || (code >= 97 && code <= 122) || (code >= 48 && code <= 57) || code === 95 || code === 45 || code === 46) {
|
|
122
|
+
i++;
|
|
123
|
+
} else if (code === 58 || code >= 128) { // `:` or non-ASCII
|
|
124
|
+
return -1;
|
|
125
|
+
} else {
|
|
126
|
+
return i - start;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
106
131
|
// The characters `\s` matches, by code: Tab through carriage return, space, and the
|
|
107
132
|
// Unicode whitespace and line terminators
|
|
108
133
|
const isWhitespaceCode = (/** @type {number} */ code) =>
|
|
@@ -374,8 +399,7 @@ export class HTMLParser {
|
|
|
374
399
|
cachedNextStartTag = null;
|
|
375
400
|
cachedNextEndTag = null;
|
|
376
401
|
advance(startTagMatch.advance);
|
|
377
|
-
await handleStartTag(startTagMatch);
|
|
378
|
-
prevTag = startTagMatch.tagName.toLowerCase();
|
|
402
|
+
prevTag = await handleStartTag(startTagMatch);
|
|
379
403
|
continue;
|
|
380
404
|
}
|
|
381
405
|
if (cachedNextEndTag && cachedNextEndTag.pos === pos) {
|
|
@@ -383,8 +407,7 @@ export class HTMLParser {
|
|
|
383
407
|
cachedNextStartTag = null;
|
|
384
408
|
cachedNextEndTag = null;
|
|
385
409
|
advance(endTagMatch.text.length);
|
|
386
|
-
await parseEndTag(endTagMatch.text, endTagMatch.name);
|
|
387
|
-
prevTag = '/' + endTagMatch.name.toLowerCase();
|
|
410
|
+
prevTag = '/' + await parseEndTag(endTagMatch.text, endTagMatch.name);
|
|
388
411
|
prevAttrs = [];
|
|
389
412
|
continue;
|
|
390
413
|
}
|
|
@@ -452,8 +475,7 @@ export class HTMLParser {
|
|
|
452
475
|
const endTagMatch = matchEndTag(pos);
|
|
453
476
|
if (endTagMatch) {
|
|
454
477
|
advance(endTagMatch.text.length);
|
|
455
|
-
await parseEndTag(endTagMatch.text, endTagMatch.name);
|
|
456
|
-
prevTag = '/' + endTagMatch.name.toLowerCase();
|
|
478
|
+
prevTag = '/' + await parseEndTag(endTagMatch.text, endTagMatch.name);
|
|
457
479
|
prevAttrs = [];
|
|
458
480
|
continue;
|
|
459
481
|
}
|
|
@@ -463,8 +485,7 @@ export class HTMLParser {
|
|
|
463
485
|
const startTagMatch = parseStartTag(pos);
|
|
464
486
|
if (startTagMatch) {
|
|
465
487
|
advance(startTagMatch.advance);
|
|
466
|
-
await handleStartTag(startTagMatch);
|
|
467
|
-
prevTag = startTagMatch.tagName.toLowerCase();
|
|
488
|
+
prevTag = await handleStartTag(startTagMatch);
|
|
468
489
|
continue;
|
|
469
490
|
}
|
|
470
491
|
}
|
|
@@ -614,17 +635,29 @@ export class HTMLParser {
|
|
|
614
635
|
* where none is complete
|
|
615
636
|
*/
|
|
616
637
|
function matchEndTag(startPos) {
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
638
|
+
let name;
|
|
639
|
+
let nameEnd;
|
|
640
|
+
const scanned = scanAsciiTagName(fullHtml, startPos + 2);
|
|
641
|
+
if (scanned !== -1) {
|
|
642
|
+
nameEnd = startPos + 2 + scanned;
|
|
643
|
+
name = fullHtml.slice(startPos + 2, nameEnd);
|
|
644
|
+
} else {
|
|
645
|
+
endTagOpenY.lastIndex = startPos;
|
|
646
|
+
const open = endTagOpenY.exec(fullHtml);
|
|
647
|
+
if (!open) {
|
|
648
|
+
return null;
|
|
649
|
+
}
|
|
650
|
+
nameEnd = startPos + open[0].length;
|
|
651
|
+
name = open[1] ?? '';
|
|
621
652
|
}
|
|
622
|
-
const close = findEndTagClose(fullHtml,
|
|
623
|
-
return close === -1 ? null : { text: fullHtml.slice(startPos, close + 1), name
|
|
653
|
+
const close = findEndTagClose(fullHtml, nameEnd);
|
|
654
|
+
return close === -1 ? null : { text: fullHtml.slice(startPos, close + 1), name };
|
|
624
655
|
}
|
|
625
656
|
|
|
626
657
|
// Where a tag ends: After optional whitespace, at `>` or `/>`. Scanned by hand in a
|
|
627
|
-
// single pass, where a regex did the same with one execution per attribute.
|
|
658
|
+
// single pass, where a regex did the same with one execution per attribute. The
|
|
659
|
+
// result packs length and slash into one number (`length << 1 | hasSlash`, 0 for no
|
|
660
|
+
// match) so the per-attribute loop allocates nothing.
|
|
628
661
|
function matchTagClose(/** @type {number} */ currentPos) {
|
|
629
662
|
let scan = currentPos;
|
|
630
663
|
let code = fullHtml.charCodeAt(scan);
|
|
@@ -632,132 +665,89 @@ export class HTMLParser {
|
|
|
632
665
|
code = fullHtml.charCodeAt(++scan);
|
|
633
666
|
}
|
|
634
667
|
if (code === 62) { // `>`
|
|
635
|
-
return
|
|
668
|
+
return (scan - currentPos + 1) << 1;
|
|
636
669
|
}
|
|
637
670
|
if (code === 47 && fullHtml.charCodeAt(scan + 1) === 62) { // `/>`
|
|
638
|
-
return
|
|
671
|
+
return ((scan - currentPos + 2) << 1) | 1;
|
|
639
672
|
}
|
|
640
|
-
return
|
|
673
|
+
return 0;
|
|
641
674
|
}
|
|
642
675
|
|
|
643
676
|
function parseStartTag(/** @type {number} */ startPos) {
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
677
|
+
let tagName;
|
|
678
|
+
let consumed;
|
|
679
|
+
const scanned = scanAsciiTagName(fullHtml, startPos + 1);
|
|
680
|
+
if (scanned !== -1) {
|
|
681
|
+
tagName = fullHtml.slice(startPos + 1, startPos + 1 + scanned);
|
|
682
|
+
consumed = 1 + scanned;
|
|
683
|
+
} else {
|
|
684
|
+
startTagOpenY.lastIndex = startPos;
|
|
685
|
+
const start = startTagOpenY.exec(fullHtml);
|
|
686
|
+
if (!start) {
|
|
687
|
+
return undefined;
|
|
688
|
+
}
|
|
689
|
+
tagName = start[1] ?? '';
|
|
690
|
+
consumed = start[0].length;
|
|
691
|
+
}
|
|
692
|
+
/** @type {{tagName: string, attrs: Array<Array<string|undefined>>, advance: number, unarySlash?: string}} */
|
|
693
|
+
const match = {
|
|
694
|
+
tagName,
|
|
695
|
+
attrs: [],
|
|
696
|
+
advance: 0
|
|
697
|
+
};
|
|
698
|
+
let currentPos = startPos + consumed;
|
|
699
|
+
let close, attr;
|
|
700
|
+
|
|
701
|
+
// Safety limit: Max length of input to check for attributes
|
|
702
|
+
// Protects against catastrophic backtracking on massive attribute values
|
|
703
|
+
const MAX_ATTR_PARSE_LENGTH = 20000; // 20 KB should be enough for any reasonable tag
|
|
704
|
+
|
|
705
|
+
while (true) {
|
|
706
|
+
// Check for closing tag first (manual scan, regex only for the unusual)
|
|
707
|
+
close = matchTagClose(currentPos);
|
|
708
|
+
if (close) {
|
|
709
|
+
break;
|
|
710
|
+
}
|
|
671
711
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
attr = attributeY.exec(fullHtml);
|
|
676
|
-
} else {
|
|
677
|
-
const extractEndPos = currentPos + MAX_ATTR_PARSE_LENGTH;
|
|
678
|
-
|
|
679
|
-
// Create a temporary substring only for attribute parsing (limited for safety)
|
|
680
|
-
const searchStr = fullHtml.substring(currentPos, extractEndPos);
|
|
681
|
-
attr = searchStr.match(attribute);
|
|
682
|
-
|
|
683
|
-
// If input was limited and there’s a match, check if the value might be truncated
|
|
684
|
-
if (attr) {
|
|
685
|
-
// Check if the attribute value extends beyond our search window
|
|
686
|
-
const attrEnd = attr[0].length;
|
|
687
|
-
// If the match ends near the limit, the value might be truncated
|
|
688
|
-
if (attrEnd > MAX_ATTR_PARSE_LENGTH - 100) {
|
|
689
|
-
// Manually extract this attribute to handle potentially huge value
|
|
690
|
-
const manualMatch = searchStr.match(/^\s*([^\s"'<>/=]+)\s*=\s*/);
|
|
691
|
-
if (manualMatch) {
|
|
692
|
-
const quoteChar = searchStr[manualMatch[0].length];
|
|
693
|
-
if (quoteChar === '"' || quoteChar === "'") {
|
|
694
|
-
const closeQuote = searchStr.indexOf(quoteChar, manualMatch[0].length + 1);
|
|
695
|
-
if (closeQuote !== -1) {
|
|
696
|
-
const fullAttrLen = closeQuote + 1;
|
|
697
|
-
const numCustomParts = handler.customAttrSurround
|
|
698
|
-
? handler.customAttrSurround.length * NCP
|
|
699
|
-
: 0;
|
|
700
|
-
const baseIndex = 1 + numCustomParts;
|
|
701
|
-
|
|
702
|
-
attr = [];
|
|
703
|
-
attr[0] = searchStr.substring(0, fullAttrLen);
|
|
704
|
-
attr[baseIndex] = manualMatch[1]; // Attribute name
|
|
705
|
-
attr[baseIndex + 1] = '='; // `customAssign` (falls back to "=" for huge attributes)
|
|
706
|
-
const value = searchStr.substring(manualMatch[0].length + 1, closeQuote);
|
|
707
|
-
// Place value at correct index based on quote type
|
|
708
|
-
if (quoteChar === '"') {
|
|
709
|
-
attr[baseIndex + 2] = value; // Double-quoted value
|
|
710
|
-
} else {
|
|
711
|
-
attr[baseIndex + 3] = value; // Single-quoted value
|
|
712
|
-
}
|
|
713
|
-
currentPos += fullAttrLen;
|
|
714
|
-
consumed += fullAttrLen;
|
|
715
|
-
match.attrs.push(attr);
|
|
716
|
-
continue;
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
// Note: Unquoted attribute values are intentionally not handled here
|
|
720
|
-
// Per HTML spec, unquoted values cannot contain spaces or special chars,
|
|
721
|
-
// making a 20 KB+ unquoted value practically impossible; if encountered,
|
|
722
|
-
// it’s malformed HTML and using the truncated regex match is acceptable
|
|
723
|
-
}
|
|
724
|
-
} else {
|
|
725
|
-
// If attr has no value assign but `=` follows in `fullHtml`,
|
|
726
|
-
// the value would be cut off—reset to trigger manual extraction below
|
|
727
|
-
const numCustomParts = handler.customAttrSurround
|
|
728
|
-
? handler.customAttrSurround.length * NCP
|
|
729
|
-
: 0;
|
|
730
|
-
const baseIndex = 1 + numCustomParts;
|
|
731
|
-
if (attr[baseIndex + 1] === undefined) {
|
|
732
|
-
const posAfterName = currentPos + attrEnd;
|
|
733
|
-
if (/^\s*=/.test(fullHtml.slice(posAfterName, posAfterName + 50))) {
|
|
734
|
-
attr = null;
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
}
|
|
712
|
+
// Limit the input length to pass to the regex to prevent catastrophic backtracking
|
|
713
|
+
const remainingLen = fullLength - currentPos;
|
|
714
|
+
const isLimited = remainingLen > MAX_ATTR_PARSE_LENGTH;
|
|
739
715
|
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
716
|
+
if (!isLimited) {
|
|
717
|
+
// Common case: Use sticky regex directly on full string (no slicing)
|
|
718
|
+
attributeY.lastIndex = currentPos;
|
|
719
|
+
attr = attributeY.exec(fullHtml);
|
|
720
|
+
} else {
|
|
721
|
+
const extractEndPos = currentPos + MAX_ATTR_PARSE_LENGTH;
|
|
722
|
+
|
|
723
|
+
// Create a temporary substring only for attribute parsing (limited for safety)
|
|
724
|
+
const searchStr = fullHtml.substring(currentPos, extractEndPos);
|
|
725
|
+
attr = searchStr.match(attribute);
|
|
726
|
+
|
|
727
|
+
// If input was limited and there’s a match, check if the value might be truncated
|
|
728
|
+
if (attr) {
|
|
729
|
+
// Check if the attribute value extends beyond our search window
|
|
730
|
+
const attrEnd = attr[0].length;
|
|
731
|
+
// If the match ends near the limit, the value might be truncated
|
|
732
|
+
if (attrEnd > MAX_ATTR_PARSE_LENGTH - 100) {
|
|
733
|
+
// Manually extract this attribute to handle potentially huge value
|
|
743
734
|
const manualMatch = searchStr.match(/^\s*([^\s"'<>/=]+)\s*=\s*/);
|
|
744
735
|
if (manualMatch) {
|
|
745
736
|
const quoteChar = searchStr[manualMatch[0].length];
|
|
746
737
|
if (quoteChar === '"' || quoteChar === "'") {
|
|
747
|
-
|
|
748
|
-
const closeQuote = fullHtml.indexOf(quoteChar, currentPos + manualMatch[0].length + 1);
|
|
738
|
+
const closeQuote = searchStr.indexOf(quoteChar, manualMatch[0].length + 1);
|
|
749
739
|
if (closeQuote !== -1) {
|
|
750
|
-
const fullAttrLen = closeQuote
|
|
740
|
+
const fullAttrLen = closeQuote + 1;
|
|
751
741
|
const numCustomParts = handler.customAttrSurround
|
|
752
742
|
? handler.customAttrSurround.length * NCP
|
|
753
743
|
: 0;
|
|
754
744
|
const baseIndex = 1 + numCustomParts;
|
|
755
745
|
|
|
756
746
|
attr = [];
|
|
757
|
-
attr[0] =
|
|
747
|
+
attr[0] = searchStr.substring(0, fullAttrLen);
|
|
758
748
|
attr[baseIndex] = manualMatch[1]; // Attribute name
|
|
759
|
-
attr[baseIndex + 1] = '='; // customAssign
|
|
760
|
-
const value =
|
|
749
|
+
attr[baseIndex + 1] = '='; // `customAssign` (falls back to "=" for huge attributes)
|
|
750
|
+
const value = searchStr.substring(manualMatch[0].length + 1, closeQuote);
|
|
761
751
|
// Place value at correct index based on quote type
|
|
762
752
|
if (quoteChar === '"') {
|
|
763
753
|
attr[baseIndex + 2] = value; // Double-quoted value
|
|
@@ -770,30 +760,83 @@ export class HTMLParser {
|
|
|
770
760
|
continue;
|
|
771
761
|
}
|
|
772
762
|
}
|
|
763
|
+
// Note: Unquoted attribute values are intentionally not handled here
|
|
764
|
+
// Per HTML spec, unquoted values cannot contain spaces or special chars,
|
|
765
|
+
// making a 20 KB+ unquoted value practically impossible; if encountered,
|
|
766
|
+
// it’s malformed HTML and using the truncated regex match is acceptable
|
|
767
|
+
}
|
|
768
|
+
} else {
|
|
769
|
+
// If attr has no value assign but `=` follows in `fullHtml`,
|
|
770
|
+
// the value would be cut off—reset to trigger manual extraction below
|
|
771
|
+
const numCustomParts = handler.customAttrSurround
|
|
772
|
+
? handler.customAttrSurround.length * NCP
|
|
773
|
+
: 0;
|
|
774
|
+
const baseIndex = 1 + numCustomParts;
|
|
775
|
+
if (attr[baseIndex + 1] === undefined) {
|
|
776
|
+
const posAfterName = currentPos + attrEnd;
|
|
777
|
+
if (/^\s*=/.test(fullHtml.slice(posAfterName, posAfterName + 50))) {
|
|
778
|
+
attr = null;
|
|
779
|
+
}
|
|
773
780
|
}
|
|
774
781
|
}
|
|
775
782
|
}
|
|
776
783
|
|
|
777
784
|
if (!attr) {
|
|
778
|
-
|
|
785
|
+
// If input was limited and there’s no match, try manual extraction
|
|
786
|
+
// This handles cases where quoted attributes exceed `MAX_ATTR_PARSE_LENGTH`
|
|
787
|
+
const manualMatch = searchStr.match(/^\s*([^\s"'<>/=]+)\s*=\s*/);
|
|
788
|
+
if (manualMatch) {
|
|
789
|
+
const quoteChar = searchStr[manualMatch[0].length];
|
|
790
|
+
if (quoteChar === '"' || quoteChar === "'") {
|
|
791
|
+
// Search in the full HTML (not limited substring) for closing quote
|
|
792
|
+
const closeQuote = fullHtml.indexOf(quoteChar, currentPos + manualMatch[0].length + 1);
|
|
793
|
+
if (closeQuote !== -1) {
|
|
794
|
+
const fullAttrLen = closeQuote - currentPos + 1;
|
|
795
|
+
const numCustomParts = handler.customAttrSurround
|
|
796
|
+
? handler.customAttrSurround.length * NCP
|
|
797
|
+
: 0;
|
|
798
|
+
const baseIndex = 1 + numCustomParts;
|
|
799
|
+
|
|
800
|
+
attr = [];
|
|
801
|
+
attr[0] = fullHtml.substring(currentPos, closeQuote + 1);
|
|
802
|
+
attr[baseIndex] = manualMatch[1]; // Attribute name
|
|
803
|
+
attr[baseIndex + 1] = '='; // customAssign
|
|
804
|
+
const value = fullHtml.substring(currentPos + manualMatch[0].length + 1, closeQuote);
|
|
805
|
+
// Place value at correct index based on quote type
|
|
806
|
+
if (quoteChar === '"') {
|
|
807
|
+
attr[baseIndex + 2] = value; // Double-quoted value
|
|
808
|
+
} else {
|
|
809
|
+
attr[baseIndex + 3] = value; // Single-quoted value
|
|
810
|
+
}
|
|
811
|
+
currentPos += fullAttrLen;
|
|
812
|
+
consumed += fullAttrLen;
|
|
813
|
+
match.attrs.push(attr);
|
|
814
|
+
continue;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
779
818
|
}
|
|
780
|
-
|
|
781
|
-
const attrLen = attr[0].length;
|
|
782
|
-
currentPos += attrLen;
|
|
783
|
-
consumed += attrLen;
|
|
784
|
-
match.attrs.push(attr);
|
|
785
819
|
}
|
|
786
820
|
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
close = matchTagClose(currentPos);
|
|
790
|
-
}
|
|
791
|
-
if (close) {
|
|
792
|
-
match.unarySlash = close.slash;
|
|
793
|
-
consumed += close.len;
|
|
794
|
-
match.advance = consumed;
|
|
795
|
-
return match;
|
|
821
|
+
if (!attr) {
|
|
822
|
+
break;
|
|
796
823
|
}
|
|
824
|
+
|
|
825
|
+
const attrLen = attr[0].length;
|
|
826
|
+
currentPos += attrLen;
|
|
827
|
+
consumed += attrLen;
|
|
828
|
+
match.attrs.push(attr);
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// Check for closing tag (manual scan, regex only for the unusual)
|
|
832
|
+
if (!close) {
|
|
833
|
+
close = matchTagClose(currentPos);
|
|
834
|
+
}
|
|
835
|
+
if (close) {
|
|
836
|
+
match.unarySlash = (close & 1) ? '/' : '';
|
|
837
|
+
consumed += close >> 1;
|
|
838
|
+
match.advance = consumed;
|
|
839
|
+
return match;
|
|
797
840
|
}
|
|
798
841
|
return undefined;
|
|
799
842
|
}
|
|
@@ -964,6 +1007,8 @@ export class HTMLParser {
|
|
|
964
1007
|
if (handler.start) {
|
|
965
1008
|
await handler.start(tagName, attrs, unary, unarySlash);
|
|
966
1009
|
}
|
|
1010
|
+
// Returned so the parse loop can skip lowercasing the name again
|
|
1011
|
+
return lowerTagName;
|
|
967
1012
|
}
|
|
968
1013
|
|
|
969
1014
|
// `needle` must already be lowercase
|
|
@@ -1017,6 +1062,8 @@ export class HTMLParser {
|
|
|
1017
1062
|
handler.end(tagName, []);
|
|
1018
1063
|
}
|
|
1019
1064
|
}
|
|
1065
|
+
// Returned so the parse loop can skip lowercasing the name again
|
|
1066
|
+
return lowerTagName;
|
|
1020
1067
|
}
|
|
1021
1068
|
}
|
|
1022
1069
|
}
|
|
@@ -67,7 +67,7 @@ const optionDefinitions = {
|
|
|
67
67
|
type: 'regexpArray'
|
|
68
68
|
},
|
|
69
69
|
customEventAttributes: {
|
|
70
|
-
description: 'Array of regexes that allow to support custom event attributes
|
|
70
|
+
description: 'Array of regexes that allow to support custom event attributes (e.g., `ng-click`)—use with `--minify-js`',
|
|
71
71
|
type: 'regexpArray'
|
|
72
72
|
},
|
|
73
73
|
decodeEntities: {
|
|
@@ -87,7 +87,7 @@ const optionDefinitions = {
|
|
|
87
87
|
type: 'boolean'
|
|
88
88
|
},
|
|
89
89
|
inlineCustomElements: {
|
|
90
|
-
description: 'Array of names of custom elements which are inline, for whitespace handling',
|
|
90
|
+
description: 'Array of names of custom elements which are inline, for whitespace handling—use with `--collapse-whitespace`',
|
|
91
91
|
type: 'jsonArray'
|
|
92
92
|
},
|
|
93
93
|
keepClosingSlash: {
|
|
@@ -123,7 +123,7 @@ const optionDefinitions = {
|
|
|
123
123
|
type: 'json'
|
|
124
124
|
},
|
|
125
125
|
noNewlinesBeforeTagClose: {
|
|
126
|
-
description: 'Never add a newline before a tag that closes an element',
|
|
126
|
+
description: 'Never add a newline before a tag that closes an element—use with `--max-line-length`',
|
|
127
127
|
type: 'boolean'
|
|
128
128
|
},
|
|
129
129
|
partialMarkup: {
|
|
@@ -167,7 +167,7 @@ const optionDefinitions = {
|
|
|
167
167
|
type: 'boolean'
|
|
168
168
|
},
|
|
169
169
|
removeEmptyElementsExcept: {
|
|
170
|
-
description: 'Array of elements to preserve
|
|
170
|
+
description: 'Array of elements to preserve (e.g., `td`, `<span aria-hidden="true">`)—use with `--remove-empty-elements`',
|
|
171
171
|
type: 'jsonArray'
|
|
172
172
|
},
|
|
173
173
|
removeOptionalTags: {
|
|
@@ -195,7 +195,7 @@ const optionDefinitions = {
|
|
|
195
195
|
type: 'boolean'
|
|
196
196
|
},
|
|
197
197
|
trimCustomFragments: {
|
|
198
|
-
description: 'Trim whitespace around custom fragments (`--ignore-custom-fragments`)',
|
|
198
|
+
description: 'Trim whitespace around custom fragments (`--ignore-custom-fragments`)—use with `--collapse-whitespace`',
|
|
199
199
|
type: 'boolean'
|
|
200
200
|
},
|
|
201
201
|
strictCustomFragments: {
|
package/src/lib/options.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createUrlMinifier } from './urls.js';
|
|
2
|
-
import { LRU, MAX_CACHE_ENTRY_SIZE, stableStringify, hashContent, identity, lowercase, replaceAsync, parseRegExp, describeQuantifierRisk, lostFlag } from './utils.js';
|
|
2
|
+
import { LRU, MAX_CACHE_ENTRY_SIZE, stableStringify, hashContent, identity, lowercase, paramCase, replaceAsync, parseRegExp, describeQuantifierRisk, lostFlag } from './utils.js';
|
|
3
3
|
import { RE_TRAILING_SEMICOLON } from './constants.js';
|
|
4
4
|
import { canCollapseWhitespace, canTrimWhitespace } from './whitespace.js';
|
|
5
5
|
import { wrapCSS, unwrapCSS } from './content.js';
|
|
@@ -100,10 +100,53 @@ const optionKeysWarned = new Set();
|
|
|
100
100
|
const presetNamesWarned = new Set();
|
|
101
101
|
// Custom fragments whose shape risks ReDoS, warned about once per pattern per process
|
|
102
102
|
const customFragmentsWarned = new Set();
|
|
103
|
+
// Options set without the option they need, warned about once per message per process
|
|
104
|
+
const dependenciesWarned = new Set();
|
|
105
|
+
// Unused-CSS configuration that does not hold up, warned about once per message per process
|
|
103
106
|
const unusedCSSWarned = new Set();
|
|
104
107
|
// Object-valued options handed a string, warned about once per distinct value
|
|
105
108
|
const stringValuesWarned = new Set();
|
|
106
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Options that do nothing on their own, and the option each one needs to take effect
|
|
112
|
+
*
|
|
113
|
+
* Cache sizes are deliberately absent: They never change output, so they are
|
|
114
|
+
* configuration for a subsystem rather than a transform that silently fails.
|
|
115
|
+
*
|
|
116
|
+
* @typedef {object} OptionDependency
|
|
117
|
+
* @property {string} option
|
|
118
|
+
* @property {string} requires
|
|
119
|
+
* @property {(input: Record<string, any>) => string | false} [unusable] Clause saying why the prerequisite does not count even though it is set
|
|
120
|
+
* @property {boolean} [clear] Whether to null the option out, where leaving it set would cost work downstream
|
|
121
|
+
*
|
|
122
|
+
* @type {OptionDependency[]}
|
|
123
|
+
*/
|
|
124
|
+
const optionDependencies = [
|
|
125
|
+
{ option: 'collapseInlineTagWhitespace', requires: 'collapseWhitespace' },
|
|
126
|
+
{ option: 'conservativeCollapse', requires: 'collapseWhitespace' },
|
|
127
|
+
{ option: 'customEventAttributes', requires: 'minifyJS' },
|
|
128
|
+
{ option: 'inlineCustomElements', requires: 'collapseWhitespace' },
|
|
129
|
+
{ option: 'noNewlinesBeforeTagClose', requires: 'maxLineLength' },
|
|
130
|
+
{ option: 'preserveLineBreaks', requires: 'collapseWhitespace' },
|
|
131
|
+
{ option: 'removeEmptyElementsExcept', requires: 'removeEmptyElements' },
|
|
132
|
+
{
|
|
133
|
+
option: 'removeUnusedCSS',
|
|
134
|
+
requires: 'minifyCSS',
|
|
135
|
+
// Removal rides along with Lightning CSS, which a function of one’s own replaces
|
|
136
|
+
unusable: (/** @type {Record<string, any>} */ input) => typeof input.minifyCSS === 'function' && 'which a function of your own replaces',
|
|
137
|
+
clear: true
|
|
138
|
+
},
|
|
139
|
+
{ option: 'trimCustomFragments', requires: 'collapseWhitespace' }
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
// An empty array is a value the user supplied but did not populate, and so asks for
|
|
143
|
+
// nothing; `identity` is what a minifier that could not be loaded resolves to
|
|
144
|
+
/** @param {unknown} value */
|
|
145
|
+
function isRequested(value) {
|
|
146
|
+
if (value === identity) return false;
|
|
147
|
+
return Array.isArray(value) ? value.length > 0 : Boolean(value);
|
|
148
|
+
}
|
|
149
|
+
|
|
107
150
|
// Main options processor
|
|
108
151
|
|
|
109
152
|
/**
|
|
@@ -642,19 +685,24 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, getS
|
|
|
642
685
|
}
|
|
643
686
|
}
|
|
644
687
|
|
|
645
|
-
//
|
|
646
|
-
//
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
688
|
+
// Options that silently do nothing without the option they build on say so rather
|
|
689
|
+
// than let them pass. The option is read from `effectiveInput` (user options
|
|
690
|
+
// over preset), so that a default filled in above doesn’t count as the user asking
|
|
691
|
+
// for it; the option it needs is read from the settled options, so that a minifier
|
|
692
|
+
// asked for but not loadable counts as absent rather than as present.
|
|
693
|
+
const requestedOptions = /** @type {Record<string, any>} */ (effectiveInput);
|
|
694
|
+
const settledOptions = /** @type {Record<string, any>} */ (options);
|
|
695
|
+
for (const { option, requires, unusable, clear } of optionDependencies) {
|
|
696
|
+
if (!isRequested(requestedOptions[option])) continue;
|
|
697
|
+
const aside = unusable && unusable(requestedOptions);
|
|
698
|
+
if (!aside && isRequested(settledOptions[requires])) continue;
|
|
699
|
+
const message = `HTML Minifier Next: Ignoring \`${option}\`—use with \`${requires}\` (\`--${paramCase(requires)}\`)${aside ? `, ${aside}` : ''}`;
|
|
700
|
+
if (!dependenciesWarned.has(message)) {
|
|
701
|
+
dependenciesWarned.add(message);
|
|
702
|
+
warn(message);
|
|
703
|
+
}
|
|
704
|
+
if (clear) {
|
|
705
|
+
settledOptions[option] = null;
|
|
658
706
|
}
|
|
659
707
|
}
|
|
660
708
|
|
|
@@ -664,6 +712,7 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, getS
|
|
|
664
712
|
// Exports
|
|
665
713
|
|
|
666
714
|
export {
|
|
715
|
+
optionDependencies,
|
|
667
716
|
shouldMinifyInnerHTML,
|
|
668
717
|
processOptions
|
|
669
718
|
};
|
package/src/lib/utils.js
CHANGED
|
@@ -106,6 +106,12 @@ function lowercase(value) {
|
|
|
106
106
|
return value.toLowerCase();
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
// Option name to CLI flag name (ASCII-only, no Unicode needed)
|
|
110
|
+
/** @param {string} value */
|
|
111
|
+
function paramCase(value) {
|
|
112
|
+
return value.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
|
113
|
+
}
|
|
114
|
+
|
|
109
115
|
// Replace async helper
|
|
110
116
|
|
|
111
117
|
/**
|
|
@@ -844,6 +850,7 @@ export {
|
|
|
844
850
|
identity,
|
|
845
851
|
isThenable,
|
|
846
852
|
lowercase,
|
|
853
|
+
paramCase,
|
|
847
854
|
replaceAsync,
|
|
848
855
|
parseRegExp,
|
|
849
856
|
embedSource,
|