html-minifier-next 6.2.11 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -21
- package/cli.js +21 -27
- package/dist/types/htmlminifier.d.ts +20 -43
- package/dist/types/htmlminifier.d.ts.map +1 -1
- package/dist/types/lib/attributes.d.ts +19 -12
- package/dist/types/lib/attributes.d.ts.map +1 -1
- package/dist/types/lib/content.d.ts +8 -10
- package/dist/types/lib/content.d.ts.map +1 -1
- package/dist/types/lib/elements.d.ts +6 -11
- package/dist/types/lib/elements.d.ts.map +1 -1
- package/dist/types/lib/option-definitions.d.ts +11 -17
- package/dist/types/lib/options.d.ts +50 -6
- package/dist/types/lib/options.d.ts.map +1 -1
- package/dist/types/lib/utils.d.ts +5 -2
- package/dist/types/lib/utils.d.ts.map +1 -1
- package/dist/types/presets.d.ts +3 -6
- package/dist/types/presets.d.ts.map +1 -1
- package/package.json +16 -23
- package/src/htmlminifier.js +65 -66
- package/src/htmlparser.js +1 -1
- package/src/lib/attributes.js +45 -17
- package/src/lib/content.js +5 -3
- package/src/lib/elements.js +3 -8
- package/src/lib/option-definitions.js +4 -8
- package/src/lib/options.js +67 -22
- package/src/lib/utils.js +4 -1
- package/src/lib/whitespace.js +1 -1
- package/src/presets.js +2 -4
- package/dist/htmlminifier.cjs +0 -5142
|
@@ -1,13 +1,55 @@
|
|
|
1
|
-
export type MinifierOptions = Record<string, any>;
|
|
2
1
|
/**
|
|
3
|
-
*
|
|
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
|
-
|
|
6
|
-
|
|
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 {
|
|
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
|
-
}):
|
|
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":"
|
|
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
|
-
/**
|
|
28
|
-
|
|
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
|
|
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"}
|
package/dist/types/presets.d.ts
CHANGED
|
@@ -17,8 +17,7 @@ export namespace presets {
|
|
|
17
17
|
let conservativeCollapse: boolean;
|
|
18
18
|
let preserveLineBreaks: boolean;
|
|
19
19
|
let removeComments: boolean;
|
|
20
|
-
let
|
|
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":"
|
|
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
|
|
15
|
+
"description": "Super-configurable and well-tested web page minifier (enhanced successor to HTML Minifier)",
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"@commitlint/cli": "^21.0
|
|
17
|
+
"@commitlint/cli": "^21.2.0",
|
|
18
18
|
"@eslint/js": "^10.0.1",
|
|
19
|
-
"@
|
|
20
|
-
"@
|
|
21
|
-
"
|
|
22
|
-
"@swc/core": "^1.15.40",
|
|
23
|
-
"@types/node": "^25.9.1",
|
|
24
|
-
"eslint": "^10.4.0",
|
|
25
|
-
"rollup": "^4.60.4",
|
|
26
|
-
"rollup-plugin-polyfill-node": "^0.13.0",
|
|
19
|
+
"@swc/core": "^1.15.41",
|
|
20
|
+
"@types/node": "^26.1.0",
|
|
21
|
+
"eslint": "^10.6.0",
|
|
27
22
|
"typescript": "^6.0.3",
|
|
28
|
-
"vite": "^8.0.
|
|
23
|
+
"vite": "^8.0.16"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=22"
|
|
29
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
|
|
88
|
-
"build:docs": "vite build --base /html-minifier-next/
|
|
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
|
|
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": "
|
|
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
|
-
"
|
|
101
|
-
"version": "6.2.11"
|
|
94
|
+
"version": "7.1.0"
|
|
102
95
|
}
|
package/src/htmlminifier.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 {
|
|
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 =
|
|
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 `
|
|
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 `
|
|
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 {
|
|
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 =
|
|
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 || [],
|
|
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
|
-
|
|
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
|
-
})(
|
|
1098
|
+
})(options.minifyCSS);
|
|
1109
1099
|
}
|
|
1110
1100
|
|
|
1111
1101
|
if (options.minifyJS !== identity) {
|
|
1112
|
-
options.minifyJS = (function (/** @type {
|
|
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
|
-
})(
|
|
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
|
|
1121
|
+
return options.canCollapseWhitespace(tag, attrs, defaultCanCollapseWhitespace);
|
|
1132
1122
|
}
|
|
1133
1123
|
|
|
1134
1124
|
function canTrimWhitespace(/** @type {string} */ tag, /** @type {HTMLAttribute[]} */ attrs) {
|
|
1135
|
-
return
|
|
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
|
|
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
|
|
1213
|
+
const nameParent = options.name;
|
|
1220
1214
|
options = Object.create(options);
|
|
1221
1215
|
options.caseSensitive = false;
|
|
1222
1216
|
options.keepClosingSlash = false;
|
|
1223
|
-
options.
|
|
1224
|
-
options.name =
|
|
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
|
-
|
|
1223
|
+
useNameParentForTag = true;
|
|
1230
1224
|
}
|
|
1231
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1309
|
-
|
|
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 =
|
|
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
|
|
1605
|
+
text = await options.minifyJS(text, false, isModuleScript);
|
|
1608
1606
|
}
|
|
1609
1607
|
if (needsMinifyCSS) {
|
|
1610
|
-
text = await
|
|
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,
|
|
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
|
-
|
|
1747
|
+
const minifySVG = options.minifySVG;
|
|
1748
|
+
if (minifySVG && svgBlocks.length) {
|
|
1750
1749
|
const optimized = await Promise.all(
|
|
1751
1750
|
svgBlocks.map(({ start, end }) =>
|
|
1752
|
-
|
|
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 {
|
|
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
|
-
|
|
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,
|
|
1916
|
+
let result = await minifyHTML(value, processedOptions);
|
|
1918
1917
|
|
|
1919
1918
|
// Post-processing: Merge consecutive inline scripts if enabled
|
|
1920
|
-
if (
|
|
1919
|
+
if (processedOptions.mergeScripts) {
|
|
1921
1920
|
result = mergeConsecutiveScripts(result);
|
|
1922
1921
|
}
|
|
1923
1922
|
|
|
1924
|
-
|
|
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
|
|
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;
|