html-minifier-next 6.2.0 → 6.2.2
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 +1 -1
- package/dist/htmlminifier.cjs +29 -13
- package/dist/types/lib/attributes.d.ts.map +1 -1
- package/dist/types/lib/options.d.ts.map +1 -1
- package/dist/types/lib/utils.d.ts +1 -0
- package/dist/types/lib/utils.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/lib/attributes.js +8 -2
- package/src/lib/options.js +7 -8
- package/src/lib/utils.js +12 -0
- package/src/lib/whitespace.js +4 -4
package/README.md
CHANGED
|
@@ -603,7 +603,7 @@ Parameters:
|
|
|
603
603
|
|
|
604
604
|
## Acknowledgements
|
|
605
605
|
|
|
606
|
-
With many thanks to
|
|
606
|
+
With many thanks to the previous authors of and contributors to HTML Minifier, especially [Juriy “kangax” Zaytsev](https://github.com/kangax), and to everyone who helped make this new edition better, particularly [Daniel Ruf](https://github.com/DanielRuf), [Jonas Geiler](https://github.com/jonasgeiler), and [Chris Morgan](https://github.com/chris-morgan)!
|
|
607
607
|
|
|
608
608
|
***
|
|
609
609
|
|
package/dist/htmlminifier.cjs
CHANGED
|
@@ -43,6 +43,17 @@ class LRU {
|
|
|
43
43
|
delete(key) { this.map.delete(key); }
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
// FNV-1a 32-bit hash for large-input cache keys
|
|
47
|
+
|
|
48
|
+
function hashContent(str) {
|
|
49
|
+
let hash = 2166136261;
|
|
50
|
+
for (let i = 0; i < str.length; i++) {
|
|
51
|
+
hash ^= str.charCodeAt(i);
|
|
52
|
+
hash = Math.imul(hash, 16777619);
|
|
53
|
+
}
|
|
54
|
+
return (hash >>> 0).toString(36);
|
|
55
|
+
}
|
|
56
|
+
|
|
46
57
|
// Unique ID generator
|
|
47
58
|
|
|
48
59
|
function uniqueId(value) {
|
|
@@ -1429,15 +1440,15 @@ function collapseWhitespaceSmart(str, prevTag, nextTag, prevAttrs, nextAttrs, op
|
|
|
1429
1440
|
|
|
1430
1441
|
// Collapse/trim whitespace for given tag
|
|
1431
1442
|
|
|
1432
|
-
const
|
|
1433
|
-
const
|
|
1443
|
+
const noCollapseWhitespaceTags = new Set(['script', 'style', 'pre', 'textarea']);
|
|
1444
|
+
const noTrimWhitespaceTags = new Set(['pre', 'textarea']);
|
|
1434
1445
|
|
|
1435
1446
|
function canCollapseWhitespace(tag) {
|
|
1436
|
-
return !
|
|
1447
|
+
return !noCollapseWhitespaceTags.has(tag);
|
|
1437
1448
|
}
|
|
1438
1449
|
|
|
1439
1450
|
function canTrimWhitespace(tag) {
|
|
1440
|
-
return !
|
|
1451
|
+
return !noTrimWhitespaceTags.has(tag);
|
|
1441
1452
|
}
|
|
1442
1453
|
|
|
1443
1454
|
/**
|
|
@@ -1816,12 +1827,11 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, getS
|
|
|
1816
1827
|
);
|
|
1817
1828
|
}
|
|
1818
1829
|
|
|
1819
|
-
// Cache key:
|
|
1830
|
+
// Cache key: Content + type + options signature; large inputs are hashed to avoid huge Map keys
|
|
1820
1831
|
const inputCSS = wrapCSS(text, type);
|
|
1821
1832
|
const cssSig = stableStringify({ type, opts: lightningCssOptions, cont: !!options.continueOnMinifyError });
|
|
1822
|
-
// For large inputs, use length and content fingerprint (first/last 50 chars) to prevent collisions
|
|
1823
1833
|
const cssKey = inputCSS.length > 2048
|
|
1824
|
-
? (
|
|
1834
|
+
? (hashContent(inputCSS) + '|' + type + '|' + cssSig)
|
|
1825
1835
|
: (inputCSS + '|' + type + '|' + cssSig);
|
|
1826
1836
|
|
|
1827
1837
|
try {
|
|
@@ -1931,8 +1941,8 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, getS
|
|
|
1931
1941
|
// Select pre-computed signature based on engine
|
|
1932
1942
|
const optsSig = useEngine === 'terser' ? terserSig : swcSig;
|
|
1933
1943
|
|
|
1934
|
-
// For large inputs,
|
|
1935
|
-
jsKey = (code.length > 2048 ? (
|
|
1944
|
+
// For large inputs, hash the full content to avoid storing huge strings as Map keys
|
|
1945
|
+
jsKey = (code.length > 2048 ? (hashContent(code) + '|') : (code + '|'))
|
|
1936
1946
|
+ (inline ? '1' : '0') + '|' + (isModule ? 'm' : '') + '|' + useEngine + '|' + optsSig;
|
|
1937
1947
|
|
|
1938
1948
|
const cached = jsMinifyCache.get(jsKey);
|
|
@@ -2044,9 +2054,9 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, getS
|
|
|
2044
2054
|
return svgContent;
|
|
2045
2055
|
}
|
|
2046
2056
|
|
|
2047
|
-
// Cache key
|
|
2057
|
+
// Cache key: Large inputs are hashed to avoid huge Map keys
|
|
2048
2058
|
const svgKey = svgContent.length > 2048
|
|
2049
|
-
? (
|
|
2059
|
+
? (hashContent(svgContent) + '|' + svgSig)
|
|
2050
2060
|
: (svgContent + '|' + svgSig);
|
|
2051
2061
|
|
|
2052
2062
|
try {
|
|
@@ -2366,12 +2376,18 @@ function hasAttrName(name, attrs) {
|
|
|
2366
2376
|
|
|
2367
2377
|
// Cleaners
|
|
2368
2378
|
|
|
2379
|
+
const collapseAttributeWhitespaceExempt = new Set(['pattern', 'placeholder', 'title']);
|
|
2380
|
+
// `value` whitespace matters only on form-submission and machine-readable elements
|
|
2381
|
+
const valueWhitespaceExemptElements = new Set(['button', 'data', 'input', 'option', 'param']);
|
|
2382
|
+
|
|
2369
2383
|
// Returns the cleaned attribute value directly (sync) or as a Promise (async);
|
|
2370
2384
|
// callers must handle both cases—use `isThenable()` to distinguish
|
|
2371
2385
|
function cleanAttributeValue(tag, attrName, attrValue, options, attrs, minifyHTMLSelf) {
|
|
2386
|
+
const isEventAttr = isEventAttribute(attrName, options);
|
|
2387
|
+
|
|
2372
2388
|
// Apply early whitespace normalization if enabled
|
|
2373
2389
|
// Preserves special spaces (no-break space, hair space, etc.) for consistency with `collapseWhitespace`
|
|
2374
|
-
if (options.collapseAttributeWhitespace) {
|
|
2390
|
+
if (options.collapseAttributeWhitespace && !collapseAttributeWhitespaceExempt.has(attrName) && !(attrName === 'value' && valueWhitespaceExemptElements.has(tag)) && !isEventAttr) {
|
|
2375
2391
|
// Fast path: Only process if whitespace exists (avoids regex overhead on clean values)
|
|
2376
2392
|
if (RE_ATTR_WS_CHECK.test(attrValue)) {
|
|
2377
2393
|
// Two-pass approach (faster than single-pass with callback)
|
|
@@ -2381,7 +2397,7 @@ function cleanAttributeValue(tag, attrName, attrValue, options, attrs, minifyHTM
|
|
|
2381
2397
|
}
|
|
2382
2398
|
}
|
|
2383
2399
|
|
|
2384
|
-
if (
|
|
2400
|
+
if (isEventAttr) {
|
|
2385
2401
|
attrValue = trimWhitespace(attrValue).replace(/^javascript:\s*/i, '');
|
|
2386
2402
|
const result = options.minifyJS(attrValue, true);
|
|
2387
2403
|
if (isThenable(result)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attributes.d.ts","sourceRoot":"","sources":["../../../src/lib/attributes.js"],"names":[],"mappings":"AAmCA,mEAOC;AAED,uEAWC;AAED,8DAGC;AAED,4EAOC;AAgCD,mGAuCC;AAED,mEAGC;AAED,qEAGC;AAED,kEAWC;AAED,sEAGC;AAED,8DAWC;AAED,2EAIC;AAmBD,qEAGC;AAgBD,wEAGC;AAED,sEAUC;AAED,2EAEC;AAED,2DAEC;AAED,8DAUC;AAED,uEAUC;AAED,oGASC;AAED,4DAOC;
|
|
1
|
+
{"version":3,"file":"attributes.d.ts","sourceRoot":"","sources":["../../../src/lib/attributes.js"],"names":[],"mappings":"AAmCA,mEAOC;AAED,uEAWC;AAED,8DAGC;AAED,4EAOC;AAgCD,mGAuCC;AAED,mEAGC;AAED,qEAGC;AAED,kEAWC;AAED,sEAGC;AAED,8DAWC;AAED,2EAIC;AAmBD,qEAGC;AAgBD,wEAGC;AAED,sEAUC;AAED,2EAEC;AAED,2DAEC;AAED,8DAUC;AAED,uEAUC;AAED,oGASC;AAED,4DAOC;AAUD,iIA4KC;AAwBD,mGAYC;AA0CD,6GAuHC;AAxlBD;;;;;;;GAOG;AACH,mEAHW,OAAO,SAuBjB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../../src/lib/options.js"],"names":[],"mappings":"AAYA,6DAUC;AAID;;;;;;;;;;;GAWG;AACH,6CAXW,OAAO,CAAC,eAAe,CAAC,mGAEhC;IAAuB,eAAe;IACf,SAAS;IACT,MAAM;CAA2B,GAK9C,eAAe,
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../../src/lib/options.js"],"names":[],"mappings":"AAYA,6DAUC;AAID;;;;;;;;;;;GAWG;AACH,6CAXW,OAAO,CAAC,eAAe,CAAC,mGAEhC;IAAuB,eAAe;IACf,SAAS;IACT,MAAM;CAA2B,GAK9C,eAAe,CAgX3B"}
|
|
@@ -7,6 +7,7 @@ export class LRU {
|
|
|
7
7
|
set(key: any, value: any): void;
|
|
8
8
|
delete(key: any): void;
|
|
9
9
|
}
|
|
10
|
+
export function hashContent(str: any): string;
|
|
10
11
|
export function uniqueId(value: any): string;
|
|
11
12
|
export function identity(value: any): any;
|
|
12
13
|
export function isThenable(value: any): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/lib/utils.js"],"names":[],"mappings":"AAEA,+CAUC;AAID;IACE,4BAGC;IAFC,cAAkB;IAClB,mBAAoB;IAEtB,mBAQC;IACD,gCAOC;IACD,uBAAqC;CACtC;AAID,6CAMC;AAID,0CAEC;AAED,gDAEC;AAED,2CAEC;AAID;;;;;;GAMG;AACH,kCALW,MAAM,SACN,MAAM,sBAEJ,OAAO,CAAC,MAAM,CAAC,CAY3B;AAID,6CAUC"}
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/lib/utils.js"],"names":[],"mappings":"AAEA,+CAUC;AAID;IACE,4BAGC;IAFC,cAAkB;IAClB,mBAAoB;IAEtB,mBAQC;IACD,gCAOC;IACD,uBAAqC;CACtC;AAID,8CAOC;AAID,6CAMC;AAID,0CAEC;AAED,gDAEC;AAED,2CAEC;AAID;;;;;;GAMG;AACH,kCALW,MAAM,SACN,MAAM,sBAEJ,OAAO,CAAC,MAAM,CAAC,CAY3B;AAID,6CAUC"}
|
package/package.json
CHANGED
|
@@ -18,11 +18,11 @@
|
|
|
18
18
|
"@rollup/plugin-commonjs": "^29.0.2",
|
|
19
19
|
"@rollup/plugin-json": "^6.1.0",
|
|
20
20
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
21
|
-
"@swc/core": "^1.15.
|
|
21
|
+
"@swc/core": "^1.15.30",
|
|
22
22
|
"eslint": "^10.2.0",
|
|
23
|
-
"rollup": "^4.60.
|
|
23
|
+
"rollup": "^4.60.2",
|
|
24
24
|
"rollup-plugin-polyfill-node": "^0.13.0",
|
|
25
|
-
"typescript": "^6.0.
|
|
25
|
+
"typescript": "^6.0.3",
|
|
26
26
|
"vite": "^8.0.8"
|
|
27
27
|
},
|
|
28
28
|
"exports": {
|
|
@@ -96,5 +96,5 @@
|
|
|
96
96
|
},
|
|
97
97
|
"type": "module",
|
|
98
98
|
"types": "./dist/types/htmlminifier.d.ts",
|
|
99
|
-
"version": "6.2.
|
|
99
|
+
"version": "6.2.2"
|
|
100
100
|
}
|
package/src/lib/attributes.js
CHANGED
|
@@ -294,12 +294,18 @@ function hasAttrName(name, attrs) {
|
|
|
294
294
|
|
|
295
295
|
// Cleaners
|
|
296
296
|
|
|
297
|
+
const collapseAttributeWhitespaceExempt = new Set(['pattern', 'placeholder', 'title']);
|
|
298
|
+
// `value` whitespace matters only on form-submission and machine-readable elements
|
|
299
|
+
const valueWhitespaceExemptElements = new Set(['button', 'data', 'input', 'option', 'param']);
|
|
300
|
+
|
|
297
301
|
// Returns the cleaned attribute value directly (sync) or as a Promise (async);
|
|
298
302
|
// callers must handle both cases—use `isThenable()` to distinguish
|
|
299
303
|
function cleanAttributeValue(tag, attrName, attrValue, options, attrs, minifyHTMLSelf) {
|
|
304
|
+
const isEventAttr = isEventAttribute(attrName, options);
|
|
305
|
+
|
|
300
306
|
// Apply early whitespace normalization if enabled
|
|
301
307
|
// Preserves special spaces (no-break space, hair space, etc.) for consistency with `collapseWhitespace`
|
|
302
|
-
if (options.collapseAttributeWhitespace) {
|
|
308
|
+
if (options.collapseAttributeWhitespace && !collapseAttributeWhitespaceExempt.has(attrName) && !(attrName === 'value' && valueWhitespaceExemptElements.has(tag)) && !isEventAttr) {
|
|
303
309
|
// Fast path: Only process if whitespace exists (avoids regex overhead on clean values)
|
|
304
310
|
if (RE_ATTR_WS_CHECK.test(attrValue)) {
|
|
305
311
|
// Two-pass approach (faster than single-pass with callback)
|
|
@@ -309,7 +315,7 @@ function cleanAttributeValue(tag, attrName, attrValue, options, attrs, minifyHTM
|
|
|
309
315
|
}
|
|
310
316
|
}
|
|
311
317
|
|
|
312
|
-
if (
|
|
318
|
+
if (isEventAttr) {
|
|
313
319
|
attrValue = trimWhitespace(attrValue).replace(/^javascript:\s*/i, '');
|
|
314
320
|
const result = options.minifyJS(attrValue, true);
|
|
315
321
|
if (isThenable(result)) {
|
package/src/lib/options.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Imports
|
|
2
2
|
|
|
3
3
|
import { createUrlMinifier } from './urls.js';
|
|
4
|
-
import { LRU, stableStringify, identity, lowercase, replaceAsync, parseRegExp } from './utils.js';
|
|
4
|
+
import { LRU, stableStringify, hashContent, identity, lowercase, replaceAsync, parseRegExp } from './utils.js';
|
|
5
5
|
import { RE_TRAILING_SEMICOLON } from './constants.js';
|
|
6
6
|
import { canCollapseWhitespace, canTrimWhitespace } from './whitespace.js';
|
|
7
7
|
import { wrapCSS, unwrapCSS } from './content.js';
|
|
@@ -131,12 +131,11 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, getS
|
|
|
131
131
|
);
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
// Cache key:
|
|
134
|
+
// Cache key: Content + type + options signature; large inputs are hashed to avoid huge Map keys
|
|
135
135
|
const inputCSS = wrapCSS(text, type);
|
|
136
136
|
const cssSig = stableStringify({ type, opts: lightningCssOptions, cont: !!options.continueOnMinifyError });
|
|
137
|
-
// For large inputs, use length and content fingerprint (first/last 50 chars) to prevent collisions
|
|
138
137
|
const cssKey = inputCSS.length > 2048
|
|
139
|
-
? (
|
|
138
|
+
? (hashContent(inputCSS) + '|' + type + '|' + cssSig)
|
|
140
139
|
: (inputCSS + '|' + type + '|' + cssSig);
|
|
141
140
|
|
|
142
141
|
try {
|
|
@@ -246,8 +245,8 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, getS
|
|
|
246
245
|
// Select pre-computed signature based on engine
|
|
247
246
|
const optsSig = useEngine === 'terser' ? terserSig : swcSig;
|
|
248
247
|
|
|
249
|
-
// For large inputs,
|
|
250
|
-
jsKey = (code.length > 2048 ? (
|
|
248
|
+
// For large inputs, hash the full content to avoid storing huge strings as Map keys
|
|
249
|
+
jsKey = (code.length > 2048 ? (hashContent(code) + '|') : (code + '|'))
|
|
251
250
|
+ (inline ? '1' : '0') + '|' + (isModule ? 'm' : '') + '|' + useEngine + '|' + optsSig;
|
|
252
251
|
|
|
253
252
|
const cached = jsMinifyCache.get(jsKey);
|
|
@@ -359,9 +358,9 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, getS
|
|
|
359
358
|
return svgContent;
|
|
360
359
|
}
|
|
361
360
|
|
|
362
|
-
// Cache key
|
|
361
|
+
// Cache key: Large inputs are hashed to avoid huge Map keys
|
|
363
362
|
const svgKey = svgContent.length > 2048
|
|
364
|
-
? (
|
|
363
|
+
? (hashContent(svgContent) + '|' + svgSig)
|
|
365
364
|
: (svgContent + '|' + svgSig);
|
|
366
365
|
|
|
367
366
|
try {
|
package/src/lib/utils.js
CHANGED
|
@@ -39,6 +39,17 @@ class LRU {
|
|
|
39
39
|
delete(key) { this.map.delete(key); }
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
// FNV-1a 32-bit hash for large-input cache keys
|
|
43
|
+
|
|
44
|
+
function hashContent(str) {
|
|
45
|
+
let hash = 2166136261;
|
|
46
|
+
for (let i = 0; i < str.length; i++) {
|
|
47
|
+
hash ^= str.charCodeAt(i);
|
|
48
|
+
hash = Math.imul(hash, 16777619);
|
|
49
|
+
}
|
|
50
|
+
return (hash >>> 0).toString(36);
|
|
51
|
+
}
|
|
52
|
+
|
|
42
53
|
// Unique ID generator
|
|
43
54
|
|
|
44
55
|
function uniqueId(value) {
|
|
@@ -102,6 +113,7 @@ function parseRegExp(value) {
|
|
|
102
113
|
|
|
103
114
|
export { stableStringify };
|
|
104
115
|
export { LRU };
|
|
116
|
+
export { hashContent };
|
|
105
117
|
export { uniqueId };
|
|
106
118
|
export { identity };
|
|
107
119
|
export { isThenable };
|
package/src/lib/whitespace.js
CHANGED
|
@@ -211,15 +211,15 @@ function collapseWhitespaceSmart(str, prevTag, nextTag, prevAttrs, nextAttrs, op
|
|
|
211
211
|
|
|
212
212
|
// Collapse/trim whitespace for given tag
|
|
213
213
|
|
|
214
|
-
const
|
|
215
|
-
const
|
|
214
|
+
const noCollapseWhitespaceTags = new Set(['script', 'style', 'pre', 'textarea']);
|
|
215
|
+
const noTrimWhitespaceTags = new Set(['pre', 'textarea']);
|
|
216
216
|
|
|
217
217
|
function canCollapseWhitespace(tag) {
|
|
218
|
-
return !
|
|
218
|
+
return !noCollapseWhitespaceTags.has(tag);
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
function canTrimWhitespace(tag) {
|
|
222
|
-
return !
|
|
222
|
+
return !noTrimWhitespaceTags.has(tag);
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
// Exports
|