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.
Files changed (35) hide show
  1. package/README.md +59 -22
  2. package/cli.js +29 -4
  3. package/dist/types/htmlminifier.d.ts +77 -20
  4. package/dist/types/htmlminifier.d.ts.map +1 -1
  5. package/dist/types/htmlparser.d.ts +11 -11
  6. package/dist/types/htmlparser.d.ts.map +1 -1
  7. package/dist/types/lib/attributes.d.ts +36 -38
  8. package/dist/types/lib/attributes.d.ts.map +1 -1
  9. package/dist/types/lib/constants.d.ts +97 -100
  10. package/dist/types/lib/constants.d.ts.map +1 -1
  11. package/dist/types/lib/content.d.ts +7 -6
  12. package/dist/types/lib/content.d.ts.map +1 -1
  13. package/dist/types/lib/elements.d.ts +13 -18
  14. package/dist/types/lib/elements.d.ts.map +1 -1
  15. package/dist/types/lib/option-definitions.d.ts +202 -281
  16. package/dist/types/lib/option-definitions.d.ts.map +1 -1
  17. package/dist/types/lib/options.d.ts +7 -14
  18. package/dist/types/lib/options.d.ts.map +1 -1
  19. package/dist/types/lib/urls.d.ts +10 -2
  20. package/dist/types/lib/urls.d.ts.map +1 -1
  21. package/dist/types/lib/utils.d.ts +30 -10
  22. package/dist/types/lib/utils.d.ts.map +1 -1
  23. package/dist/types/lib/whitespace.d.ts +7 -6
  24. package/dist/types/lib/whitespace.d.ts.map +1 -1
  25. package/dist/types/presets.d.ts +39 -37
  26. package/dist/types/presets.d.ts.map +1 -1
  27. package/dist/types/tokenchain.d.ts +13 -11
  28. package/dist/types/tokenchain.d.ts.map +1 -1
  29. package/html-minifier-next.schema.json +303 -0
  30. package/package.json +8 -6
  31. package/src/htmlminifier.js +34 -1
  32. package/src/lib/option-definitions.js +12 -0
  33. package/src/lib/options.js +44 -32
  34. package/src/lib/urls.js +1 -1
  35. package/src/lib/utils.js +14 -0
@@ -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', 'cacheCSS', 'cacheJS', 'cacheSVG']);
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 cssKey = inputCSS.length > 2048
187
- ? (hashContent(inputCSS) + '|' + type + '|' + cssSig)
188
- : (inputCSS + '|' + type + '|' + cssSig);
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
- const cached = /** @type {string | Promise<string> | undefined} */ (cssCache.get(cssKey));
192
- if (cached !== undefined) {
193
- // Support both resolved values and in-flight promises
194
- return await cached;
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
- // For large inputs, hash the full content to avoid storing huge strings as Map keys
303
- jsKey = (code.length > 2048 ? (hashContent(code) + '|') : (code + '|'))
304
- + (inline ? '1' : '0') + '|' + (isModule ? 'm' : '') + '|' + useEngine + '|' + optsSig;
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
- const cached = /** @type {string | Promise<string> | undefined} */ (jsCache.get(jsKey));
307
- if (cached !== undefined) {
308
- return await cached;
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
- // Cache key: Large inputs are hashed to avoid huge Map keys
420
- const svgKey = svgContent.length > 2048
421
- ? (hashContent(svgContent) + '|' + svgSig)
422
- : (svgContent + '|' + svgSig);
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
- const cached = /** @type {string | Promise<string> | undefined} */ (svgCache.get(svgKey));
426
- if (cached !== undefined) {
427
- return await cached;
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 {function(string): string} Minifier function that returns the shortest URL
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 };