html-minifier-next 6.2.11 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,55 @@
1
- export type MinifierOptions = Record<string, any>;
2
1
  /**
3
- * @typedef {Record<string, any>} MinifierOptions
2
+ * Options object produced by `processOptions` and consumed by `minifyHTML` and
3
+ * the `lib/` helpers; normalization guarantees that the function-valued options
4
+ * below are always present (defaulting to identity/built-in functions), and
5
+ * minification adds writable internal state on top of the public options
6
+ * (set on prototype-chain forks during SVG/MathML namespace transitions)
4
7
  */
5
- /** @param {MinifierOptions} options */
6
- export function shouldMinifyInnerHTML(options: MinifierOptions): boolean;
8
+ export type ProcessedOptions = Omit<MinifierOptions, "preset" | "canCollapseWhitespace" | "canTrimWhitespace" | "ignoreCustomComments" | "log" | "minifyCSS" | "minifyJS" | "minifyURLs" | "minifySVG"> & {
9
+ name: (name: string) => string;
10
+ log: (message: any) => unknown;
11
+ ignoreCustomComments: RegExp[];
12
+ canCollapseWhitespace: (tag: string, attrs: HTMLAttribute[], defaultFn: (tag: string) => boolean) => boolean;
13
+ canTrimWhitespace: (tag: string, attrs: HTMLAttribute[], defaultFn: (tag: string) => boolean) => boolean;
14
+ minifyCSS: (text: string, type?: string) => string | Promise<string>;
15
+ minifyJS: (text: string, inline?: boolean, isModule?: boolean) => string | Promise<string>;
16
+ minifyURLs: (text: string) => string | Promise<string>;
17
+ minifySVG: ((svgContent: string) => string | Promise<string>) | null;
18
+ nameParent?: (name: string) => string;
19
+ nameHTML?: (name: string) => string;
20
+ insideSVG?: boolean;
21
+ insideForeignContent?: boolean;
22
+ };
23
+ /** @import { MinifierOptions, HTMLAttribute } from '../htmlminifier.js' */
24
+ /**
25
+ * Options object produced by `processOptions` and consumed by `minifyHTML` and
26
+ * the `lib/` helpers; normalization guarantees that the function-valued options
27
+ * below are always present (defaulting to identity/built-in functions), and
28
+ * minification adds writable internal state on top of the public options
29
+ * (set on prototype-chain forks during SVG/MathML namespace transitions)
30
+ *
31
+ * @typedef {Omit<MinifierOptions, 'preset' | 'canCollapseWhitespace' | 'canTrimWhitespace' | 'ignoreCustomComments' | 'log' | 'minifyCSS' | 'minifyJS' | 'minifyURLs' | 'minifySVG'> & {
32
+ * name: (name: string) => string,
33
+ * log: (message: any) => unknown,
34
+ * ignoreCustomComments: RegExp[],
35
+ * canCollapseWhitespace: (tag: string, attrs: HTMLAttribute[], defaultFn: (tag: string) => boolean) => boolean,
36
+ * canTrimWhitespace: (tag: string, attrs: HTMLAttribute[], defaultFn: (tag: string) => boolean) => boolean,
37
+ * minifyCSS: (text: string, type?: string) => string | Promise<string>,
38
+ * minifyJS: (text: string, inline?: boolean, isModule?: boolean) => string | Promise<string>,
39
+ * minifyURLs: (text: string) => string | Promise<string>,
40
+ * minifySVG: ((svgContent: string) => string | Promise<string>) | null,
41
+ * nameParent?: (name: string) => string,
42
+ * nameHTML?: (name: string) => string,
43
+ * insideSVG?: boolean,
44
+ * insideForeignContent?: boolean
45
+ * }} ProcessedOptions
46
+ */
47
+ /** @param {ProcessedOptions} options */
48
+ export function shouldMinifyInnerHTML(options: ProcessedOptions): boolean;
7
49
  /**
8
50
  * @param {MinifierOptions} inputOptions - User-provided options
9
51
  * @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
10
- * @returns {MinifierOptions} Normalized options with defaults applied
52
+ * @returns {ProcessedOptions} Normalized options with defaults applied
11
53
  */
12
54
  export function processOptions(inputOptions: MinifierOptions, { getLightningCSS, getTerser, getSwc, getSvgo, cssMinifyCache, jsMinifyCache, svgMinifyCache }?: {
13
55
  getLightningCSS?: Function | undefined;
@@ -17,6 +59,8 @@ export function processOptions(inputOptions: MinifierOptions, { getLightningCSS,
17
59
  cssMinifyCache?: LRU | undefined;
18
60
  jsMinifyCache?: LRU | undefined;
19
61
  svgMinifyCache?: LRU | undefined;
20
- }): MinifierOptions;
62
+ }): ProcessedOptions;
63
+ import type { MinifierOptions } from '../htmlminifier.js';
64
+ import type { HTMLAttribute } from '../htmlminifier.js';
21
65
  import { LRU } from './utils.js';
22
66
  //# sourceMappingURL=options.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../../src/lib/options.js"],"names":[],"mappings":"8BAWa,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;AADhC;;GAEG;AAMH,uCAAuC;AACvC,+CADY,eAAe,WAW1B;AAID;;;;GAIG;AACH,6CAJW,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,GAC5O,eAAe,CA6X3B;oBAhaiG,YAAY"}
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../../src/lib/options.js"],"names":[],"mappings":";;;;;;;+BAmBa,IAAI,CAAC,eAAe,EAAE,QAAQ,GAAG,uBAAuB,GAAG,mBAAmB,GAAG,sBAAsB,GAAG,KAAK,GAAG,WAAW,GAAG,UAAU,GAAG,YAAY,GAAG,WAAW,CAAC,GAAG;IACnL,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,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrE,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,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACtC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAzBJ,2EAA2E;AAI3E;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,wCAAwC;AACxC,+CADY,gBAAgB,WAW3B;AAYD;;;;GAIG;AACH,6CAJW,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,GAC5O,gBAAgB,CA8Y5B;qCAtcmD,oBAAoB;mCAApB,oBAAoB;oBAP0B,YAAY"}
@@ -24,8 +24,11 @@ export function hashContent(str: string): string;
24
24
  export function uniqueId(value: string): string;
25
25
  /** @param {string} value */
26
26
  export function identity(value: string): string;
27
- /** @param {unknown} value */
28
- export function isThenable(value: unknown): boolean;
27
+ /**
28
+ * @param {unknown} value
29
+ * @returns {value is PromiseLike<any>}
30
+ */
31
+ export function isThenable(value: unknown): value is PromiseLike<any>;
29
32
  /** @param {string} value */
30
33
  export function lowercase(value: string): string;
31
34
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/lib/utils.js"],"names":[],"mappings":"AAEA;;;GAGG;AACH,qCAHW,OAAO,GACL,MAAM,CAYlB;AAID;IACE,4BAIC;IAHC,cAAkB;IAClB,mCAAmC;IACnC,KADW,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CACX;IAEtB,0BAA0B;IAC1B,SADY,MAAM,WASjB;IACD;;;OAGG;IACH,SAHW,MAAM,SACN,OAAO,QASjB;IACD,0BAA0B;IAC1B,YADY,MAAM,QACmB;CACtC;AAID,0BAA0B;AAC1B,iCADY,MAAM,UAQjB;AAID,4BAA4B;AAC5B,gCADY,MAAM,UAOjB;AAID,4BAA4B;AAC5B,gCADY,MAAM,UAGjB;AAED,6BAA6B;AAC7B,kCADY,OAAO,WAGlB;AAED,4BAA4B;AAC5B,iCADY,MAAM,UAGjB;AAID;;;;;;GAMG;AACH,kCALW,MAAM,SACN,MAAM,sBAEJ,OAAO,CAAC,MAAM,CAAC,CAc3B;AAID,qCAAqC;AACrC,mCADY,MAAM,GAAG,MAAM,sBAW1B"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/lib/utils.js"],"names":[],"mappings":"AAEA;;;GAGG;AACH,qCAHW,OAAO,GACL,MAAM,CAYlB;AAID;IACE,4BAIC;IAHC,cAAkB;IAClB,mCAAmC;IACnC,KADW,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CACX;IAEtB,0BAA0B;IAC1B,SADY,MAAM,WASjB;IACD;;;OAGG;IACH,SAHW,MAAM,SACN,OAAO,QASjB;IACD,0BAA0B;IAC1B,YADY,MAAM,QACmB;CACtC;AAID,0BAA0B;AAC1B,iCADY,MAAM,UAQjB;AAID,4BAA4B;AAC5B,gCADY,MAAM,UAOjB;AAID,4BAA4B;AAC5B,gCADY,MAAM,UAGjB;AAED;;;GAGG;AACH,kCAHW,OAAO,GACL,KAAK,IAAI,WAAW,CAAC,GAAG,CAAC,CAIrC;AAED,4BAA4B;AAC5B,iCADY,MAAM,UAGjB;AAID;;;;;;GAMG;AACH,kCALW,MAAM,SACN,MAAM,sBAEJ,OAAO,CAAC,MAAM,CAAC,CAc3B;AAID,qCAAqC;AACrC,mCADY,MAAM,GAAG,MAAM,sBAW1B"}
@@ -17,8 +17,7 @@ export namespace presets {
17
17
  let conservativeCollapse: boolean;
18
18
  let preserveLineBreaks: boolean;
19
19
  let removeComments: boolean;
20
- let removeScriptTypeAttributes: boolean;
21
- let removeStyleLinkTypeAttributes: boolean;
20
+ let removeDefaultTypeAttributes: boolean;
22
21
  let useShortDoctype: boolean;
23
22
  }
24
23
  namespace comprehensive {
@@ -36,13 +35,11 @@ export namespace presets {
36
35
  export let removeAttributeQuotes: boolean;
37
36
  let removeComments_1: boolean;
38
37
  export { removeComments_1 as removeComments };
38
+ let removeDefaultTypeAttributes_1: boolean;
39
+ export { removeDefaultTypeAttributes_1 as removeDefaultTypeAttributes };
39
40
  export let removeEmptyAttributes: boolean;
40
41
  export let removeOptionalTags: boolean;
41
42
  export let removeRedundantAttributes: boolean;
42
- let removeScriptTypeAttributes_1: boolean;
43
- export { removeScriptTypeAttributes_1 as removeScriptTypeAttributes };
44
- let removeStyleLinkTypeAttributes_1: boolean;
45
- export { removeStyleLinkTypeAttributes_1 as removeStyleLinkTypeAttributes };
46
43
  let useShortDoctype_1: boolean;
47
44
  export { useShortDoctype_1 as useShortDoctype };
48
45
  }
@@ -1 +1 @@
1
- {"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../src/presets.js"],"names":[],"mappings":"AAyCA;;;;GAIG;AACH,gCAHW,MAAM,GACJ,MAAM,GAAC,IAAI,CAMvB;AAED;;;GAGG;AACH,kCAFa,MAAM,EAAE,CAIpB"}
1
+ {"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../src/presets.js"],"names":[],"mappings":"AAuCA;;;;GAIG;AACH,gCAHW,MAAM,GACJ,MAAM,GAAC,IAAI,CAMvB;AAED;;;GAGG;AACH,kCAFa,MAAM,EAAE,CAIpB"}
package/package.json CHANGED
@@ -12,32 +12,28 @@
12
12
  "svgo": "^4.0.1",
13
13
  "terser": "^5.47.1"
14
14
  },
15
- "description": "Super-configurable and well-tested web page minifier (enhanced successor of HTML Minifier)",
15
+ "description": "Super-configurable and well-tested web page minifier (enhanced successor to HTML Minifier)",
16
16
  "devDependencies": {
17
17
  "@commitlint/cli": "^21.0.1",
18
18
  "@eslint/js": "^10.0.1",
19
- "@rollup/plugin-commonjs": "^29.0.3",
20
- "@rollup/plugin-json": "^6.1.0",
21
- "@rollup/plugin-node-resolve": "^16.0.3",
22
19
  "@swc/core": "^1.15.40",
23
20
  "@types/node": "^25.9.1",
24
21
  "eslint": "^10.4.0",
25
- "rollup": "^4.60.4",
26
- "rollup-plugin-polyfill-node": "^0.13.0",
27
22
  "typescript": "^6.0.3",
28
23
  "vite": "^8.0.14"
29
24
  },
25
+ "engines": {
26
+ "node": ">=22"
27
+ },
30
28
  "exports": {
31
29
  ".": {
32
30
  "types": "./dist/types/htmlminifier.d.ts",
33
- "import": "./src/htmlminifier.js",
34
- "require": "./dist/htmlminifier.cjs"
31
+ "import": "./src/htmlminifier.js"
35
32
  },
36
33
  "./package.json": "./package.json"
37
34
  },
38
35
  "files": [
39
36
  "cli.js",
40
- "dist/htmlminifier.cjs",
41
37
  "dist/types/",
42
38
  "src/"
43
39
  ],
@@ -68,8 +64,6 @@
68
64
  "uglify"
69
65
  ],
70
66
  "license": "MIT",
71
- "main": "./dist/htmlminifier.cjs",
72
- "module": "./src/htmlminifier.js",
73
67
  "name": "html-minifier-next",
74
68
  "peerDependencies": {
75
69
  "@swc/core": "^1.15.7"
@@ -84,19 +78,18 @@
84
78
  "url": "git+https://github.com/j9t/html-minifier-next.git"
85
79
  },
86
80
  "scripts": {
87
- "build": "tsc && rollup -c",
88
- "build:docs": "vite build --base /html-minifier-next/ --outDir build",
81
+ "build": "tsc",
82
+ "build:docs": "vite build --base /html-minifier-next/",
89
83
  "deploy": "npm run build && npm run build:docs",
90
84
  "lint": "eslint .",
91
- "prebuild": "node --eval='require(`fs`).rmSync(`dist`,{recursive:true,force:true})'",
85
+ "prebuild": "node --input-type=module --eval \"import {rmSync} from 'fs';rmSync('dist',{recursive:true,force:true})\"",
92
86
  "prepack": "npm run build",
93
87
  "prepare": "git config core.hooksPath .githooks || true",
94
- "serve": "npm run build && vite",
88
+ "serve": "vite",
95
89
  "test": "npm run test:types && node --test tests/*.spec.js",
96
- "test:types": "tsc --project tsconfig.test.json",
90
+ "test:types": "tsc --noEmit && tsc --project tsconfig.test.json",
97
91
  "test:watch": "node --test --watch tests/*.spec.js"
98
92
  },
99
93
  "type": "module",
100
- "types": "./dist/types/htmlminifier.d.ts",
101
- "version": "6.2.11"
94
+ "version": "7.0.0"
102
95
  }
@@ -54,6 +54,8 @@ import {
54
54
 
55
55
  import { processOptions } from './lib/options.js';
56
56
 
57
+ /** @import { ProcessedOptions } from './lib/options.js' */
58
+
57
59
  // Type definitions
58
60
 
59
61
  /**
@@ -171,7 +173,7 @@ import { processOptions } from './lib/options.js';
171
173
  *
172
174
  * @prop {RegExp[]} [customAttrAssign]
173
175
  * Array of regexes used to recognise custom attribute assignment
174
- * operators (e.g. `'<div flex?="{{mode != cover}}"></div>'`).
176
+ * operators (e.g., `'<div flex?="{{mode != cover}}"></div>'`).
175
177
  * These are concatenated with the built-in assignment patterns.
176
178
  *
177
179
  * Default: `[]`
@@ -179,16 +181,16 @@ import { processOptions } from './lib/options.js';
179
181
  * @prop {RegExp} [customAttrCollapse]
180
182
  * Regex matching attribute names whose values should be collapsed.
181
183
  * Basically used to remove newlines and excess spaces inside attribute values,
182
- * e.g. `/ng-class/`.
184
+ * e.g., `/ng-class/`.
183
185
  *
184
186
  * @prop {[RegExp, RegExp][]} [customAttrSurround]
185
187
  * Array of `[openRegExp, closeRegExp]` pairs used by the parser to
186
188
  * detect custom attribute surround patterns (for non-standard syntaxes,
187
- * e.g. `<input {{#if value}}checked="checked"{{/if}}>`).
189
+ * e.g., `<input {{#if value}}checked="checked"{{/if}}>`).
188
190
  *
189
191
  * @prop {RegExp[]} [customEventAttributes]
190
192
  * Array of regexes used to detect event handler attributes for `minifyJS`
191
- * (e.g. `ng-click`). The default matches standard `on…` event attributes.
193
+ * (e.g., `ng-click`). The default matches standard `on…` event attributes.
192
194
  *
193
195
  * Default: `[/^on[a-z]{3,}$/]`
194
196
  *
@@ -303,13 +305,6 @@ import { processOptions } from './lib/options.js';
303
305
  *
304
306
  * Default: `false`
305
307
  *
306
- * @prop {(name: string) => string} [name]
307
- * Function used to normalise tag/attribute names. By default, this lowercases
308
- * names, unless `caseSensitive` is enabled.
309
- *
310
- * Default: `(name) => name.toLowerCase()`,
311
- * or `(name) => name` (no-op function) if `caseSensitive` is enabled.
312
- *
313
308
  * @prop {boolean} [noNewlinesBeforeTagClose]
314
309
  * When wrapping lines, prevent inserting a newline directly before a
315
310
  * closing tag (useful to keep tags like `</a>` on the same line).
@@ -332,6 +327,13 @@ import { processOptions } from './lib/options.js';
332
327
  *
333
328
  * Default: `false`
334
329
  *
330
+ * @prop {string} [preset]
331
+ * Name of a preset configuration to use as a base (`conservative` or
332
+ * `comprehensive`). Explicitly provided options take precedence over
333
+ * preset values. Unknown preset names are ignored (with a warning).
334
+ *
335
+ * Default: No preset
336
+ *
335
337
  * @prop {boolean} [preventAttributesEscaping]
336
338
  * When true, attribute values will not be HTML-escaped (dangerous for
337
339
  * untrusted input). By default, attributes are escaped.
@@ -341,7 +343,7 @@ import { processOptions } from './lib/options.js';
341
343
  * @prop {string[]} [processScripts]
342
344
  * Array of `type` attribute values for `<script>` elements whose contents
343
345
  * should be processed as HTML
344
- * (e.g. `text/ng-template`, `text/x-handlebars-template`, etc.).
346
+ * (e.g., `text/ng-template`, `text/x-handlebars-template`, etc.).
345
347
  * When present, the contents of matching script tags are recursively minified,
346
348
  * like normal HTML content.
347
349
  *
@@ -366,6 +368,12 @@ import { processOptions } from './lib/options.js';
366
368
  *
367
369
  * Default: `false`
368
370
  *
371
+ * @prop {boolean} [removeDefaultTypeAttributes]
372
+ * Remove default `type` attributes from `<style>`/`<link>` (e.g., `type="text/css"`)
373
+ * and `<script>` (e.g., `type="text/javascript"`) elements.
374
+ *
375
+ * Default: `false`
376
+ *
369
377
  * @prop {boolean | ((attrName: string, tag: string) => boolean)} [removeEmptyAttributes]
370
378
  * If true, removes attributes whose values are empty (some attributes
371
379
  * are excluded by name). Can also be a function to customise which empty
@@ -420,18 +428,6 @@ import { processOptions } from './lib/options.js';
420
428
  *
421
429
  * Default: `false`
422
430
  *
423
- * @prop {boolean} [removeScriptTypeAttributes]
424
- * Remove `type` attributes from `<script>` when they are unnecessary
425
- * (e.g. `type="text/javascript"`).
426
- *
427
- * Default: `false`
428
- *
429
- * @prop {boolean} [removeStyleLinkTypeAttributes]
430
- * Remove `type` attributes from `<style>` and `<link>` elements when
431
- * they are unnecessary (e.g. `type="text/css"`).
432
- *
433
- * Default: `false`
434
- *
435
431
  * @prop {boolean} [removeTagWhitespace]
436
432
  * **Note that this will result in invalid HTML!**
437
433
  *
@@ -469,11 +465,6 @@ import { processOptions } from './lib/options.js';
469
465
  * See also: https://perfectionkills.com/experimenting-with-html-minifier/#use_short_doctype
470
466
  *
471
467
  * Default: `false`
472
- *
473
- * @prop {boolean} [insideSVG] - Internal: Set when inside SVG/MathML context
474
- * @prop {boolean} [insideForeignContent] - Internal: Set when inside SVG `foreignObject`
475
- * @prop {Function} [parentName] - Internal: Preserved name function during namespace transitions
476
- * @prop {Function} [htmlName] - Internal: HTML name function preserved from outer context
477
468
  */
478
469
 
479
470
  // Lazy-load heavy dependencies only when needed
@@ -717,7 +708,7 @@ function mergeConsecutiveScripts(html) {
717
708
 
718
709
  /**
719
710
  * @param {string} value
720
- * @param {MinifierOptions} options
711
+ * @param {ProcessedOptions} options
721
712
  * @param {string} uidIgnore
722
713
  * @param {string} uidAttr
723
714
  * @param {string[]} ignoredMarkupChunks
@@ -725,7 +716,7 @@ function mergeConsecutiveScripts(html) {
725
716
  async function createSortFns(value, options, uidIgnore, uidAttr, ignoredMarkupChunks) {
726
717
  const attrChains = options.sortAttributes && typeof options.sortAttributes !== 'function' && Object.create(null);
727
718
  const classChain = options.sortClassNames && typeof options.sortClassNames !== 'function' && new TokenChain();
728
- const resolveName = /** @type {(name: string) => string} */ (options.name);
719
+ const resolveName = options.name;
729
720
 
730
721
  function attrNames(/** @type {HTMLAttribute[]} */ attrs) {
731
722
  return attrs.map(function (attr) {
@@ -804,7 +795,7 @@ async function createSortFns(value, options, uidIgnore, uidAttr, ignoredMarkupCh
804
795
  }
805
796
 
806
797
  // For the first pass, create a copy of options and disable aggressive minification.
807
- // Keep attribute transformations (like `removeStyleLinkTypeAttributes`) for accurate analysis.
798
+ // Keep attribute transformations (like `removeDefaultTypeAttributes`) for accurate analysis.
808
799
  // This is safe because `createSortFns` is called before custom fragment UID markers (`uidAttr`) are added.
809
800
  // Note: `htmlmin:ignore` UID markers (`uidIgnore`) already exist and are expanded for analysis.
810
801
  const firstPassOptions = Object.assign({}, options, {
@@ -818,7 +809,6 @@ async function createSortFns(value, options, uidIgnore, uidAttr, ignoredMarkupCh
818
809
  decodeEntities: false,
819
810
  processScripts: false,
820
811
  // Keep `ignoreCustomFragments` to handle template syntax correctly
821
- // This is safe because `createSortFns` is now called before UID markers are added
822
812
  // Continue on parse errors during analysis (e.g., template syntax)
823
813
  continueOnParseError: true,
824
814
  log: identity
@@ -848,7 +838,7 @@ async function createSortFns(value, options, uidIgnore, uidAttr, ignoredMarkupCh
848
838
  uidReplacePattern.lastIndex = 0;
849
839
  }
850
840
 
851
- // First pass minification applies attribute transformations like `removeStyleLinkTypeAttributes` for accurate frequency analysis
841
+ // First pass minification applies attribute transformations like `removeDefaultTypeAttributes` for accurate frequency analysis
852
842
  const firstPassOutput = await minifyHTML(expandedValue, firstPassOptions);
853
843
 
854
844
  // For frequency analysis, remove custom fragments temporarily
@@ -944,7 +934,7 @@ async function createSortFns(value, options, uidIgnore, uidAttr, ignoredMarkupCh
944
934
 
945
935
  /**
946
936
  * @param {string} value - HTML content to minify
947
- * @param {MinifierOptions} options - Normalized minification options
937
+ * @param {ProcessedOptions} options - Normalized minification options
948
938
  * @param {boolean} [partialMarkup] - Whether treating input as partial markup
949
939
  * @returns {Promise<string>} Minified HTML
950
940
  */
@@ -958,7 +948,7 @@ async function minifyHTML(value, options, partialMarkup) {
958
948
  value = collapseWhitespace(value, options, true, true);
959
949
  }
960
950
 
961
- const resolveName = /** @type {(name: string) => string} */ (options.name);
951
+ const resolveName = options.name;
962
952
  /** @type {HTMLAttribute[]} */
963
953
  const emptyAttrs = [];
964
954
 
@@ -1015,7 +1005,7 @@ async function minifyHTML(value, options, partialMarkup) {
1015
1005
  }
1016
1006
  removeEmptyElementsExcept = [];
1017
1007
  } else {
1018
- removeEmptyElementsExcept = parseRemoveEmptyElementsExcept(options.removeEmptyElementsExcept || [], /** @type {any} */ (options)) || [];
1008
+ removeEmptyElementsExcept = parseRemoveEmptyElementsExcept(options.removeEmptyElementsExcept || [], options) || [];
1019
1009
  }
1020
1010
 
1021
1011
  // Temporarily replace ignored chunks with comments, so that there’s no need to worry what’s there;
@@ -1096,8 +1086,8 @@ async function minifyHTML(value, options, partialMarkup) {
1096
1086
  uidAttrLeadingPattern = new RegExp('^\\s*' + uidAttr + '(\\d+)' + uidAttr);
1097
1087
 
1098
1088
  if (options.minifyCSS !== identity) {
1099
- /** @type {any} */ (options).minifyCSS = (function (/** @type {Function} */ fn) {
1100
- return function (/** @type {string} */ text, /** @type {string} */ type) {
1089
+ options.minifyCSS = (function (/** @type {ProcessedOptions['minifyCSS']} */ fn) {
1090
+ return function (/** @type {string} */ text, /** @type {string | undefined} */ type) {
1101
1091
  text = text.replace(/** @type {RegExp} */ (uidPattern), function (/** @type {string} */ _match, /** @type {string} */ _prefix, /** @type {string} */ index) {
1102
1092
  const chunks = ignoredCustomMarkupChunks[+index];
1103
1093
  return (chunks?.[1] ?? '') + uidAttr + index + uidAttr + (chunks?.[2] ?? '');
@@ -1105,18 +1095,18 @@ async function minifyHTML(value, options, partialMarkup) {
1105
1095
 
1106
1096
  return fn(text, type);
1107
1097
  };
1108
- })(/** @type {Function} */ (options.minifyCSS));
1098
+ })(options.minifyCSS);
1109
1099
  }
1110
1100
 
1111
1101
  if (options.minifyJS !== identity) {
1112
- options.minifyJS = (function (/** @type {Function} */ fn) {
1113
- return function (/** @type {string} */ text, /** @type {boolean} */ inline, /** @type {boolean} */ isModule) {
1102
+ options.minifyJS = (function (/** @type {ProcessedOptions['minifyJS']} */ fn) {
1103
+ return function (/** @type {string} */ text, /** @type {boolean | undefined} */ inline, /** @type {boolean | undefined} */ isModule) {
1114
1104
  return fn(text.replace(/** @type {RegExp} */ (uidPattern), function (/** @type {string} */ _match, /** @type {string} */ _prefix, /** @type {string} */ index) {
1115
1105
  const chunks = ignoredCustomMarkupChunks[+index];
1116
1106
  return (chunks?.[1] ?? '') + uidAttr + index + uidAttr + (chunks?.[2] ?? '');
1117
1107
  }), inline, isModule);
1118
1108
  };
1119
- })(/** @type {Function} */ (options.minifyJS));
1109
+ })(options.minifyJS);
1120
1110
  }
1121
1111
  }
1122
1112
 
@@ -1128,11 +1118,11 @@ async function minifyHTML(value, options, partialMarkup) {
1128
1118
  }
1129
1119
 
1130
1120
  function canCollapseWhitespace(/** @type {string} */ tag, /** @type {HTMLAttribute[]} */ attrs) {
1131
- return /** @type {Function} */ (options.canCollapseWhitespace)(tag, attrs, defaultCanCollapseWhitespace);
1121
+ return options.canCollapseWhitespace(tag, attrs, defaultCanCollapseWhitespace);
1132
1122
  }
1133
1123
 
1134
1124
  function canTrimWhitespace(/** @type {string} */ tag, /** @type {HTMLAttribute[]} */ attrs) {
1135
- return /** @type {Function} */ (options.canTrimWhitespace)(tag, attrs, defaultCanTrimWhitespace);
1125
+ return options.canTrimWhitespace(tag, attrs, defaultCanTrimWhitespace);
1136
1126
  }
1137
1127
 
1138
1128
  function removeStartTag() {
@@ -1195,10 +1185,14 @@ async function minifyHTML(value, options, partialMarkup) {
1195
1185
  start: async function (/** @type {string} */ tag, /** @type {HTMLAttribute[]} */ attrs, /** @type {boolean} */ unary, /** @type {string} */ unarySlash, /** @type {boolean} */ autoGenerated) {
1196
1186
  const lowerTag = tag.toLowerCase();
1197
1187
  if (lowerTag === 'svg' || lowerTag === 'math') {
1188
+ // Preserve the surrounding HTML context’s name function (e.g., `identity`
1189
+ // under `caseSensitive`) so `foreignObject`/`annotation-xml` can restore it
1190
+ const nameHTML = options.name;
1198
1191
  options = Object.create(options);
1199
1192
  options.caseSensitive = true;
1200
1193
  options.keepClosingSlash = true;
1201
1194
  options.name = identity;
1195
+ options.nameHTML = nameHTML;
1202
1196
  options.insideSVG = lowerTag === 'svg';
1203
1197
  options.insideForeignContent = true;
1204
1198
  // Disable HTML-specific options that produce invalid XML:
@@ -1212,23 +1206,24 @@ async function minifyHTML(value, options, partialMarkup) {
1212
1206
  }
1213
1207
  // `foreignObject` in SVG and `annotation-xml` in MathML contain HTML content
1214
1208
  // Note: The element itself is in SVG/MathML namespace, only its children are HTML
1215
- let useParentNameForTag = false;
1209
+ let useNameParentForTag = false;
1216
1210
  if (options.insideForeignContent && (lowerTag === 'foreignobject' ||
1217
1211
  (lowerTag === 'annotation-xml' && attrs.some((/** @type {HTMLAttribute} */ a) => a.name.toLowerCase() === 'encoding' &&
1218
1212
  RE_HTML_ENCODING.test(a.value ?? ''))))) {
1219
- const parentName = /** @type {(name: string) => string} */ (options.name);
1213
+ const nameParent = options.name;
1220
1214
  options = Object.create(options);
1221
1215
  options.caseSensitive = false;
1222
1216
  options.keepClosingSlash = false;
1223
- options.parentName = parentName; // Preserve for the element tag itself
1224
- options.name = /** @type {(name: string) => string} */ (options.htmlName ?? lowercase);
1217
+ options.nameParent = nameParent; // Preserve for the element tag itself
1218
+ options.name = options.nameHTML ?? lowercase;
1225
1219
  options.insideForeignContent = false;
1226
1220
  // Note: `removeAttributeQuotes`, `removeTagWhitespace`, and `decodeEntities`
1227
1221
  // stay disabled (inherited from SVG context) because the entire SVG block
1228
1222
  // must be valid XML for SVGO processing
1229
- useParentNameForTag = true;
1223
+ useNameParentForTag = true;
1230
1224
  }
1231
- tag = /** @type {(name: string) => string} */ (useParentNameForTag ? options.parentName : options.name)(tag);
1225
+ // `nameParent` is always set when `useNameParentForTag` is true; the extra check only narrows the type
1226
+ tag = (useNameParentForTag && options.nameParent ? options.nameParent : options.name)(tag);
1232
1227
  currentTag = tag;
1233
1228
  charsPrevTag = tag;
1234
1229
  if (!inlineTextSet.has(tag)) {
@@ -1297,16 +1292,19 @@ async function minifyHTML(value, options, partialMarkup) {
1297
1292
  deduplicateAttributes(attrs, Boolean(options.caseSensitive));
1298
1293
 
1299
1294
  if (options.sortAttributes) {
1300
- /** @type {Function} */ (options.sortAttributes)(tag, attrs);
1295
+ // By the time tags are processed, `createSortFns` has replaced any truthy non-function value
1296
+ /** @type {(tag: string, attrs: HTMLAttribute[]) => void} */ (options.sortAttributes)(tag, attrs);
1301
1297
  }
1302
1298
 
1303
1299
  const attrResults = attrs.map(attr => normalizeAttr(attr, attrs, tag, options, minifyHTML));
1304
- const normalizedAttrs = attrResults.some(isThenable) ? await Promise.all(attrResults) : attrResults;
1300
+ // The `isThenable` probe guarantees the sync branch holds no promises
1301
+ const normalizedAttrs = /** @type {Array<{name: string, value: string | undefined, attr: HTMLAttribute} | undefined>} */ (attrResults.some(isThenable) ? await Promise.all(attrResults) : attrResults);
1305
1302
  const parts = [];
1306
1303
  let isLast = true;
1307
1304
  for (let i = normalizedAttrs.length - 1; i >= 0; i--) {
1308
- if (normalizedAttrs[i]) {
1309
- parts.push(buildAttr(normalizedAttrs[i], hasUnarySlash, options, isLast, uidAttr));
1305
+ const normalizedAttr = normalizedAttrs[i];
1306
+ if (normalizedAttr) {
1307
+ parts.push(buildAttr(normalizedAttr, hasUnarySlash, options, isLast, uidAttr));
1310
1308
  isLast = false;
1311
1309
  }
1312
1310
  }
@@ -1336,7 +1334,7 @@ async function minifyHTML(value, options, partialMarkup) {
1336
1334
  !options.insideForeignContent && Object.getPrototypeOf(options).insideForeignContent) {
1337
1335
  options = Object.getPrototypeOf(options);
1338
1336
  }
1339
- tag = /** @type {Function} */ (options.name)(tag);
1337
+ tag = options.name(tag);
1340
1338
 
1341
1339
  // Check if current tag is in a whitespace stack
1342
1340
  if (options.collapseWhitespace) {
@@ -1604,10 +1602,10 @@ async function minifyHTML(value, options, partialMarkup) {
1604
1602
  text = await processScript(text, options, currentAttrs, minifyHTML);
1605
1603
  }
1606
1604
  if (needsMinifyJS) {
1607
- text = await /** @type {Function} */ (options.minifyJS)(text, false, isModuleScript);
1605
+ text = await options.minifyJS(text, false, isModuleScript);
1608
1606
  }
1609
1607
  if (needsMinifyCSS) {
1610
- text = await /** @type {Function} */ (options.minifyCSS)(text);
1608
+ text = await options.minifyCSS(text);
1611
1609
  }
1612
1610
  charsFinalize(text);
1613
1611
  })();
@@ -1724,7 +1722,7 @@ async function minifyHTML(value, options, partialMarkup) {
1724
1722
  }
1725
1723
 
1726
1724
  if (options.removeComments) {
1727
- if (isIgnoredComment(text, /** @type {any} */ (options))) {
1725
+ if (isIgnoredComment(text, options)) {
1728
1726
  text = prefix + text + suffix;
1729
1727
  } else {
1730
1728
  text = '';
@@ -1746,10 +1744,11 @@ async function minifyHTML(value, options, partialMarkup) {
1746
1744
 
1747
1745
  // Post-processing: Optimize SVG blocks with SVGO
1748
1746
  // Run all SVGO calls in parallel, then splice results in reverse to preserve indices
1749
- if (options.minifySVG && svgBlocks.length) {
1747
+ const minifySVG = options.minifySVG;
1748
+ if (minifySVG && svgBlocks.length) {
1750
1749
  const optimized = await Promise.all(
1751
1750
  svgBlocks.map(({ start, end }) =>
1752
- /** @type {Function} */ (options.minifySVG)(buffer.slice(start, end).join(''))
1751
+ minifySVG(buffer.slice(start, end).join(''))
1753
1752
  )
1754
1753
  );
1755
1754
  for (let i = svgBlocks.length - 1; i >= 0; i--) {
@@ -1803,7 +1802,7 @@ async function minifyHTML(value, options, partialMarkup) {
1803
1802
 
1804
1803
  /**
1805
1804
  * @param {string[]} results
1806
- * @param {MinifierOptions} options
1805
+ * @param {ProcessedOptions} options
1807
1806
  * @param {Function} restoreCustom
1808
1807
  * @param {Function} restoreIgnore
1809
1808
  */
@@ -1905,7 +1904,7 @@ export const minify = async function (value, options) {
1905
1904
  // Initialize caches on first use with configurable sizes
1906
1905
  const caches = initCaches(options || {});
1907
1906
 
1908
- options = processOptions(options || {}, {
1907
+ const processedOptions = processOptions(options || {}, {
1909
1908
  getLightningCSS,
1910
1909
  getTerser,
1911
1910
  getSwc,
@@ -1914,14 +1913,14 @@ export const minify = async function (value, options) {
1914
1913
  jsMinifyCache: caches.jsMinifyCache ?? undefined,
1915
1914
  svgMinifyCache: caches.svgMinifyCache ?? undefined
1916
1915
  });
1917
- let result = await minifyHTML(value, options);
1916
+ let result = await minifyHTML(value, processedOptions);
1918
1917
 
1919
1918
  // Post-processing: Merge consecutive inline scripts if enabled
1920
- if (options.mergeScripts) {
1919
+ if (processedOptions.mergeScripts) {
1921
1920
  result = mergeConsecutiveScripts(result);
1922
1921
  }
1923
1922
 
1924
- /** @type {Function} */ (options.log)('minified in: ' + (Date.now() - start) + 'ms');
1923
+ processedOptions.log('minified in: ' + (Date.now() - start) + 'ms');
1925
1924
  return result;
1926
1925
  };
1927
1926
 
package/src/htmlparser.js CHANGED
@@ -428,7 +428,7 @@ export class HTMLParser {
428
428
  advance(m[0].length);
429
429
  await parseEndTag('</' + stackedTag + '>', stackedTag);
430
430
  } else {
431
- // No closing tag found; to avoid infinite loop, break similarly to previous behavior
431
+ // No closing tag found; break to avoid an infinite loop
432
432
  if (handler.continueOnParseError && handler.chars && pos < fullLength) {
433
433
  const result = handler.chars(fullHtml[pos], prevTag, '', prevAttrs, []);
434
434
  if (isThenable(result)) await result;