html-minifier-next 7.1.0 → 7.3.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 +59 -22
- package/cli.js +29 -4
- package/dist/types/htmlminifier.d.ts +77 -20
- package/dist/types/htmlminifier.d.ts.map +1 -1
- package/dist/types/htmlparser.d.ts +11 -11
- package/dist/types/htmlparser.d.ts.map +1 -1
- package/dist/types/lib/attributes.d.ts +36 -38
- package/dist/types/lib/attributes.d.ts.map +1 -1
- package/dist/types/lib/constants.d.ts +97 -100
- package/dist/types/lib/constants.d.ts.map +1 -1
- package/dist/types/lib/content.d.ts +7 -6
- package/dist/types/lib/content.d.ts.map +1 -1
- package/dist/types/lib/elements.d.ts +13 -18
- package/dist/types/lib/elements.d.ts.map +1 -1
- package/dist/types/lib/option-definitions.d.ts +202 -281
- package/dist/types/lib/option-definitions.d.ts.map +1 -1
- package/dist/types/lib/options.d.ts +7 -14
- package/dist/types/lib/options.d.ts.map +1 -1
- package/dist/types/lib/urls.d.ts +10 -2
- package/dist/types/lib/urls.d.ts.map +1 -1
- package/dist/types/lib/utils.d.ts +30 -10
- package/dist/types/lib/utils.d.ts.map +1 -1
- package/dist/types/lib/whitespace.d.ts +7 -6
- package/dist/types/lib/whitespace.d.ts.map +1 -1
- package/dist/types/presets.d.ts +39 -37
- package/dist/types/presets.d.ts.map +1 -1
- package/dist/types/tokenchain.d.ts +13 -11
- package/dist/types/tokenchain.d.ts.map +1 -1
- package/html-minifier-next.schema.json +303 -0
- package/package.json +8 -6
- package/src/htmlminifier.js +34 -1
- package/src/lib/option-definitions.js +12 -0
- package/src/lib/options.js +44 -32
- package/src/lib/urls.js +1 -1
- package/src/lib/utils.js +14 -0
package/src/lib/options.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createUrlMinifier } from './urls.js';
|
|
2
|
-
import { LRU, stableStringify, hashContent, identity, lowercase, replaceAsync, parseRegExp } from './utils.js';
|
|
2
|
+
import { LRU, MAX_CACHE_ENTRY_SIZE, stableStringify, hashContent, identity, lowercase, replaceAsync, parseRegExp } 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';
|
|
@@ -50,7 +50,7 @@ function shouldMinifyInnerHTML(options) {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
// User-facing option keys that are valid but not listed in `optionDefinitions`
|
|
53
|
-
const optionKeysExtra = new Set(['preset', 'log', 'canCollapseWhitespace', 'canTrimWhitespace'
|
|
53
|
+
const optionKeysExtra = new Set(['preset', 'log', 'canCollapseWhitespace', 'canTrimWhitespace']);
|
|
54
54
|
|
|
55
55
|
// Unknown option keys and preset names already warned about—warn once per
|
|
56
56
|
// key per process, so repeated `minify` calls (e.g., batch runs) don’t flood STDERR
|
|
@@ -183,15 +183,20 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, getS
|
|
|
183
183
|
// Cache key: Content + type + options signature; large inputs are hashed to avoid huge Map keys
|
|
184
184
|
const inputCSS = wrapCSS(text, type);
|
|
185
185
|
const cssSig = stableStringify({ type, opts: lightningCssOptions, cont: !!options.continueOnMinifyError });
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
186
|
+
const isCacheable = inputCSS.length <= MAX_CACHE_ENTRY_SIZE;
|
|
187
|
+
const cssKey = isCacheable
|
|
188
|
+
? (inputCSS.length > 2048
|
|
189
|
+
? (hashContent(inputCSS) + '|' + type + '|' + cssSig)
|
|
190
|
+
: (inputCSS + '|' + type + '|' + cssSig))
|
|
191
|
+
: undefined;
|
|
189
192
|
|
|
190
193
|
try {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
194
|
+
if (cssKey !== undefined) {
|
|
195
|
+
const cached = /** @type {string | Promise<string> | undefined} */ (cssCache.get(cssKey));
|
|
196
|
+
if (cached !== undefined) {
|
|
197
|
+
// Support both resolved values and in-flight promises
|
|
198
|
+
return await cached;
|
|
199
|
+
}
|
|
195
200
|
}
|
|
196
201
|
|
|
197
202
|
// In-flight promise caching: Prevent duplicate concurrent minifications
|
|
@@ -224,12 +229,12 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, getS
|
|
|
224
229
|
return (text.trim() && !outputCSS.trim() && (looksLikeTemplate || hasUID)) ? text : outputCSS;
|
|
225
230
|
})();
|
|
226
231
|
|
|
227
|
-
cssCache.set(cssKey, inFlight);
|
|
232
|
+
if (cssKey !== undefined) cssCache.set(cssKey, inFlight);
|
|
228
233
|
const resolved = await inFlight;
|
|
229
|
-
cssCache.set(cssKey, resolved);
|
|
234
|
+
if (cssKey !== undefined) cssCache.set(cssKey, resolved);
|
|
230
235
|
return resolved;
|
|
231
236
|
} catch (err) {
|
|
232
|
-
cssCache.delete(cssKey);
|
|
237
|
+
if (cssKey !== undefined) cssCache.delete(cssKey);
|
|
233
238
|
if (!options.continueOnMinifyError) {
|
|
234
239
|
throw err;
|
|
235
240
|
}
|
|
@@ -293,19 +298,22 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, getS
|
|
|
293
298
|
// Hybrid strategy: Always use Terser for inline JS (needs bare returns support)
|
|
294
299
|
// Use user’s chosen engine for script blocks
|
|
295
300
|
const useEngine = inline ? 'terser' : engine;
|
|
296
|
-
|
|
297
301
|
let jsKey;
|
|
302
|
+
const isCacheable = code.length <= MAX_CACHE_ENTRY_SIZE;
|
|
303
|
+
|
|
298
304
|
try {
|
|
299
305
|
// Select pre-computed signature based on engine
|
|
300
306
|
const optsSig = useEngine === 'terser' ? terserSig : swcSig;
|
|
301
307
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
308
|
+
if (isCacheable) {
|
|
309
|
+
// For large inputs, hash the full content to avoid storing huge strings as Map keys
|
|
310
|
+
jsKey = (code.length > 2048 ? (hashContent(code) + '|') : (code + '|'))
|
|
311
|
+
+ (inline ? '1' : '0') + '|' + (isModule ? 'm' : '') + '|' + useEngine + '|' + optsSig;
|
|
305
312
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
313
|
+
const cached = /** @type {string | Promise<string> | undefined} */ (jsCache.get(jsKey));
|
|
314
|
+
if (cached !== undefined) {
|
|
315
|
+
return await cached;
|
|
316
|
+
}
|
|
309
317
|
}
|
|
310
318
|
|
|
311
319
|
const inFlight = (async () => {
|
|
@@ -337,12 +345,12 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, getS
|
|
|
337
345
|
throw new Error(`Unknown JS minifier engine: ${useEngine}`);
|
|
338
346
|
})();
|
|
339
347
|
|
|
340
|
-
jsCache.set(jsKey, inFlight);
|
|
348
|
+
if (jsKey !== undefined) jsCache.set(jsKey, inFlight);
|
|
341
349
|
const resolved = await inFlight;
|
|
342
|
-
jsCache.set(jsKey, resolved);
|
|
350
|
+
if (jsKey !== undefined) jsCache.set(jsKey, resolved);
|
|
343
351
|
return resolved;
|
|
344
352
|
} catch (err) {
|
|
345
|
-
if (jsKey) jsCache.delete(jsKey);
|
|
353
|
+
if (jsKey !== undefined) jsCache.delete(jsKey);
|
|
346
354
|
if (!options.continueOnMinifyError) {
|
|
347
355
|
throw err;
|
|
348
356
|
}
|
|
@@ -416,15 +424,19 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, getS
|
|
|
416
424
|
return svgContent;
|
|
417
425
|
}
|
|
418
426
|
|
|
419
|
-
|
|
420
|
-
const svgKey =
|
|
421
|
-
? (
|
|
422
|
-
|
|
427
|
+
const isCacheable = svgContent.length <= MAX_CACHE_ENTRY_SIZE;
|
|
428
|
+
const svgKey = isCacheable
|
|
429
|
+
? (svgContent.length > 2048
|
|
430
|
+
? (hashContent(svgContent) + '|' + svgSig)
|
|
431
|
+
: (svgContent + '|' + svgSig))
|
|
432
|
+
: undefined;
|
|
423
433
|
|
|
424
434
|
try {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
435
|
+
if (svgKey !== undefined) {
|
|
436
|
+
const cached = /** @type {string | Promise<string> | undefined} */ (svgCache.get(svgKey));
|
|
437
|
+
if (cached !== undefined) {
|
|
438
|
+
return await cached;
|
|
439
|
+
}
|
|
428
440
|
}
|
|
429
441
|
|
|
430
442
|
const inFlight = (async () => {
|
|
@@ -433,12 +445,12 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, getS
|
|
|
433
445
|
return result.data;
|
|
434
446
|
})();
|
|
435
447
|
|
|
436
|
-
svgCache.set(svgKey, inFlight);
|
|
448
|
+
if (svgKey !== undefined) svgCache.set(svgKey, inFlight);
|
|
437
449
|
const resolved = await inFlight;
|
|
438
|
-
svgCache.set(svgKey, resolved);
|
|
450
|
+
if (svgKey !== undefined) svgCache.set(svgKey, resolved);
|
|
439
451
|
return resolved;
|
|
440
452
|
} catch (err) {
|
|
441
|
-
svgCache.delete(svgKey);
|
|
453
|
+
if (svgKey !== undefined) svgCache.delete(svgKey);
|
|
442
454
|
if (!options.continueOnMinifyError) {
|
|
443
455
|
throw err;
|
|
444
456
|
}
|
package/src/lib/urls.js
CHANGED
|
@@ -79,7 +79,7 @@ function removeDirectoryIndex(pathname) {
|
|
|
79
79
|
/**
|
|
80
80
|
* Create a URL minifier function for the given site context
|
|
81
81
|
* @param {string} site - The site base URL (used to compute relative URLs)
|
|
82
|
-
* @returns {
|
|
82
|
+
* @returns {(url: string) => string} Minifier function that returns the shortest URL
|
|
83
83
|
*/
|
|
84
84
|
export function createUrlMinifier(site) {
|
|
85
85
|
let baseUrl;
|
package/src/lib/utils.js
CHANGED
|
@@ -21,12 +21,16 @@ function stableStringify(obj) {
|
|
|
21
21
|
class LRU {
|
|
22
22
|
constructor(limit = 200) {
|
|
23
23
|
this.limit = limit;
|
|
24
|
+
this.gets = 0;
|
|
25
|
+
this.hits = 0;
|
|
24
26
|
/** @type {Map<string, unknown>} */
|
|
25
27
|
this.map = new Map();
|
|
26
28
|
}
|
|
27
29
|
/** @param {string} key */
|
|
28
30
|
get(key) {
|
|
31
|
+
this.gets++;
|
|
29
32
|
if (this.map.has(key)) {
|
|
33
|
+
this.hits++;
|
|
30
34
|
const v = this.map.get(key);
|
|
31
35
|
this.map.delete(key);
|
|
32
36
|
this.map.set(key, v);
|
|
@@ -48,8 +52,17 @@ class LRU {
|
|
|
48
52
|
}
|
|
49
53
|
/** @param {string} key */
|
|
50
54
|
delete(key) { this.map.delete(key); }
|
|
55
|
+
/** @returns {{ gets: number, hits: number, size: number, limit: number }} */
|
|
56
|
+
stats() {
|
|
57
|
+
return { gets: this.gets, hits: this.hits, size: this.map.size, limit: this.limit };
|
|
58
|
+
}
|
|
51
59
|
}
|
|
52
60
|
|
|
61
|
+
// Content longer than this (in UTF-16 code units, roughly bytes for typical CSS/JS/SVG) is
|
|
62
|
+
// minified normally but never stored in a minification cache—caps worst-case cache memory
|
|
63
|
+
// (entry count × this size) without affecting realistically sized inline content
|
|
64
|
+
const MAX_CACHE_ENTRY_SIZE = 1024 * 1024; // 1 MB
|
|
65
|
+
|
|
53
66
|
// FNV-1a 32-bit hash for large-input cache keys
|
|
54
67
|
|
|
55
68
|
/** @param {string} str */
|
|
@@ -135,6 +148,7 @@ function parseRegExp(value) {
|
|
|
135
148
|
|
|
136
149
|
export { stableStringify };
|
|
137
150
|
export { LRU };
|
|
151
|
+
export { MAX_CACHE_ENTRY_SIZE };
|
|
138
152
|
export { hashContent };
|
|
139
153
|
export { uniqueId };
|
|
140
154
|
export { identity };
|