html-minifier-next 5.0.6 → 5.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.
@@ -56,6 +56,16 @@ export type MinifierOptions = {
56
56
  * Default: `500`
57
57
  */
58
58
  cacheJS?: number;
59
+ /**
60
+ * The maximum number of entries for the SVG minification cache. Higher
61
+ * values improve performance for inputs with repeated SVG content.
62
+ * - Cache is created on first `minify()` call and persists for the process lifetime
63
+ * - Cache size is locked after first call—subsequent calls reuse the same cache
64
+ * - Explicit `0` values are coerced to `1` (minimum functional cache size)
65
+ *
66
+ * Default: `500`
67
+ */
68
+ cacheSVG?: number;
59
69
  /**
60
70
  * When true, tag and attribute names are treated as case-sensitive.
61
71
  * Useful for custom HTML tags.
@@ -269,20 +279,14 @@ export type MinifierOptions = {
269
279
  site?: string;
270
280
  } | ((text: string) => Promise<string> | string);
271
281
  /**
272
- * When true, enables SVG-specific optimizations for SVG elements and attributes.
273
- * If an object is provided, it can include:
274
- * - `precision`: Number of decimal places for numeric values (coordinates, path data, etc.). Default: `3`
275
- * - `removeDefaults`: Remove attributes with default values (e.g., `fill="black"`). Default: `true`
276
- * - `minifyColors`: Minify color values (hex shortening, rgb to hex conversion). Default: `true`
282
+ * When true, enables SVG minification using [SVGO](https://github.com/svg/svgo).
283
+ * Complete SVG subtrees are extracted and optimized as a block.
284
+ * If an object is provided, it is passed to SVGO as configuration options.
277
285
  * If disabled, SVG content is minified using standard HTML rules only.
278
286
  *
279
287
  * Default: `false`
280
288
  */
281
- minifySVG?: boolean | {
282
- precision?: number;
283
- removeDefaults?: boolean;
284
- minifyColors?: boolean;
285
- };
289
+ minifySVG?: boolean | any;
286
290
  /**
287
291
  * Function used to normalise tag/attribute names. By default, this lowercases
288
292
  * names, unless `caseSensitive` is enabled.
@@ -1 +1 @@
1
- {"version":3,"file":"htmlminifier.d.ts","sourceRoot":"","sources":["../../src/htmlminifier.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"htmlminifier.d.ts","sourceRoot":"","sources":["../../src/htmlminifier.js"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"attributes.d.ts","sourceRoot":"","sources":["../../../src/lib/attributes.js"],"names":[],"mappings":"AA2BA,yDAEC;AAED,mEAOC;AAED,uEAWC;AAED,8DAGC;AAED,4EAOC;AAgCD,mGAuCC;AAED,mEAGC;AAED,qEAGC;AAED,kEAWC;AAED,sEAGC;AAED,8DAWC;AAED,2EAIC;AAED,qEAaC;AAED,wEAUC;AAED,sEAUC;AAED,2EAEC;AAED,2DAEC;AAED,8DAUC;AAED,uEAUC;AAED,oGASC;AAED,4DAOC;AAID,0IAgJC;AAsBD;;;;GAwCC;AAED,6GAuHC;AA1hBD;;;;;;;GAOG;AACH,mEAHW,OAAO,SAuBjB"}
1
+ {"version":3,"file":"attributes.d.ts","sourceRoot":"","sources":["../../../src/lib/attributes.js"],"names":[],"mappings":"AA0BA,yDAEC;AAED,mEAOC;AAED,uEAWC;AAED,8DAGC;AAED,4EAOC;AAgCD,mGAuCC;AAED,mEAGC;AAED,qEAGC;AAED,kEAWC;AAED,sEAGC;AAED,8DAWC;AAED,2EAIC;AAED,qEAaC;AAED,wEAUC;AAED,sEAUC;AAED,2EAEC;AAED,2DAEC;AAED,8DAUC;AAED,uEAUC;AAED,oGASC;AAED,4DAOC;AAID,0IAqIC;AAsBD;;;;GAsCC;AAED,6GAuHC;AA7gBD;;;;;;;GAOG;AACH,mEAHW,OAAO,SAuBjB"}
@@ -15,7 +15,6 @@ export const RE_ESCAPE_LT: RegExp;
15
15
  export const RE_ATTR_WS_CHECK: RegExp;
16
16
  export const RE_ATTR_WS_COLLAPSE: RegExp;
17
17
  export const RE_ATTR_WS_TRIM: RegExp;
18
- export const RE_NUMERIC_VALUE: RegExp;
19
18
  export const inlineElementsToKeepWhitespaceAround: Set<string>;
20
19
  export const inlineElementsToKeepWhitespaceWithin: Set<string>;
21
20
  export const inlineElementsToKeepWhitespace: Set<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/lib/constants.js"],"names":[],"mappings":"AAEA,iCAAoC;AACpC,+BAAkC;AAClC,oCAA2C;AAC3C,2CAAmD;AACnD,wCAA8C;AAC9C,4CAAkD;AAClD,4CAA2C;AAC3C,4CAA0D;AAC1D,2CAA8C;AAC9C,+CAA0D;AAC1D,2CAAmC;AACnC,mCAA4C;AAC5C,wCAAwqB;AACxqB,kCAA0B;AAC1B,sCAAuC;AACvC,yCAA4C;AAC5C,qCAAuD;AACvD,sCAAmE;AAKnE,+DAAgb;AAGhb,+DAA6O;AAG7O,yDAAmF;AAGnF,8CAA8G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0C9G,qDAWG;AAEH,+CAEG;AAmBH,0CAUG;AAzBH,0CAAwhB;AAExhB,yCAAkD;AAKlD,2CAAqE;AAIrE,yCAAkD;AAuBlD,4CAAiF;AAEjF,0CAAoM;AAEpM,yCAA4F;AAE5F,8CAAkD;AAElD,yCAAiT;AAEjT,0CAA0F;AAE1F,6CAA8D;AAE9D,gDAAqD;AAErD,yCAAuD;AAEvD,+CAAyD;AAEzD,+CAAkE;AAElE,uCAA2C;AAE3C,2CAA2D;AAE3D,0CAAkD;AAElD,wCAA+D;AAE/D,2CAAkD;AAElD,uCAAmxC;AAInxC,sCAEsD;AAItD,iDAA4D"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/lib/constants.js"],"names":[],"mappings":"AAEA,iCAAoC;AACpC,+BAAkC;AAClC,oCAA2C;AAC3C,2CAAmD;AACnD,wCAA8C;AAC9C,4CAAkD;AAClD,4CAA2C;AAC3C,4CAA0D;AAC1D,2CAA8C;AAC9C,+CAA0D;AAC1D,2CAAmC;AACnC,mCAA4C;AAC5C,wCAAwqB;AACxqB,kCAA0B;AAC1B,sCAAuC;AACvC,yCAA4C;AAC5C,qCAAuD;AAKvD,+DAAgb;AAGhb,+DAA6O;AAG7O,yDAAmF;AAGnF,8CAA8G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0C9G,qDAWG;AAEH,+CAEG;AAmBH,0CAUG;AAzBH,0CAAwhB;AAExhB,yCAAkD;AAKlD,2CAAqE;AAIrE,yCAAkD;AAuBlD,4CAAiF;AAEjF,0CAAoM;AAEpM,yCAA4F;AAE5F,8CAAkD;AAElD,yCAAiT;AAEjT,0CAA0F;AAE1F,6CAA8D;AAE9D,gDAAqD;AAErD,yCAAuD;AAEvD,+CAAyD;AAEzD,+CAAkE;AAElE,uCAA2C;AAE3C,2CAA2D;AAE3D,0CAAkD;AAElD,wCAA+D;AAE/D,2CAAkD;AAElD,uCAAmxC;AAInxC,sCAEsD;AAItD,iDAA4D"}
@@ -5,11 +5,13 @@ export function shouldMinifyInnerHTML(options: any): boolean;
5
5
  * @param {Function} deps.getLightningCSS - Function to lazily load Lightning CSS
6
6
  * @param {Function} deps.getTerser - Function to lazily load Terser
7
7
  * @param {Function} deps.getSwc - Function to lazily load @swc/core
8
+ * @param {Function} deps.getSvgo - Function to lazily load SVGO
8
9
  * @param {LRU} deps.cssMinifyCache - CSS minification cache
9
10
  * @param {LRU} deps.jsMinifyCache - JS minification cache
11
+ * @param {LRU} deps.svgMinifyCache - SVG minification cache
10
12
  * @returns {MinifierOptions} Normalized options with defaults applied
11
13
  */
12
- export function processOptions(inputOptions: Partial<MinifierOptions>, { getLightningCSS, getTerser, getSwc, cssMinifyCache, jsMinifyCache }?: {
14
+ export function processOptions(inputOptions: Partial<MinifierOptions>, { getLightningCSS, getTerser, getSwc, getSvgo, cssMinifyCache, jsMinifyCache, svgMinifyCache }?: {
13
15
  getLightningCSS: Function;
14
16
  getTerser: Function;
15
17
  getSwc: Function;
@@ -1 +1 @@
1
- {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../../src/lib/options.js"],"names":[],"mappings":"AAaA,6DAUC;AAID;;;;;;;;;GASG;AACH,6CATW,OAAO,CAAC,eAAe,CAAC,0EAEhC;IAAuB,eAAe;IACf,SAAS;IACT,MAAM;CAA2B,GAG9C,eAAe,CAkU3B"}
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,CA6W3B"}
package/package.json CHANGED
@@ -8,6 +8,7 @@
8
8
  "commander": "^14.0.2",
9
9
  "entities": "^7.0.1",
10
10
  "lightningcss": "^1.31.1",
11
+ "svgo": "^4.0.0",
11
12
  "terser": "^5.46.0"
12
13
  },
13
14
  "description": "Super-configurable and well-tested web page minifier (enhanced successor of HTML Minifier)",
@@ -94,5 +95,5 @@
94
95
  },
95
96
  "type": "module",
96
97
  "types": "./dist/types/htmlminifier.d.ts",
97
- "version": "5.0.6"
98
+ "version": "5.1.0"
98
99
  }
@@ -92,9 +92,18 @@ async function getSwc() {
92
92
  return swcPromise;
93
93
  }
94
94
 
95
+ let svgoPromise;
96
+ async function getSvgo() {
97
+ if (!svgoPromise) {
98
+ svgoPromise = import('svgo').then(m => m.optimize);
99
+ }
100
+ return svgoPromise;
101
+ }
102
+
95
103
  // Minification caches (initialized on first use with configurable sizes)
96
104
  let cssMinifyCache = null;
97
105
  let jsMinifyCache = null;
106
+ let svgMinifyCache = null;
98
107
 
99
108
  // Pre-compiled patterns for script merging (avoid repeated allocation in hot path)
100
109
  const RE_SCRIPT_ATTRS = /([^\s=]+)(?:=(?:"([^"]*)"|'([^']*)'|([^\s>]+)))?/g;
@@ -268,6 +277,15 @@ function mergeConsecutiveScripts(html) {
268
277
  *
269
278
  * Default: `500`
270
279
  *
280
+ * @prop {number} [cacheSVG]
281
+ * The maximum number of entries for the SVG minification cache. Higher
282
+ * values improve performance for inputs with repeated SVG content.
283
+ * - Cache is created on first `minify()` call and persists for the process lifetime
284
+ * - Cache size is locked after first call—subsequent calls reuse the same cache
285
+ * - Explicit `0` values are coerced to `1` (minimum functional cache size)
286
+ *
287
+ * Default: `500`
288
+ *
271
289
  * @prop {boolean} [caseSensitive]
272
290
  * When true, tag and attribute names are treated as case-sensitive.
273
291
  * Useful for custom HTML tags.
@@ -450,12 +468,10 @@ function mergeConsecutiveScripts(html) {
450
468
  *
451
469
  * Default: `false`
452
470
  *
453
- * @prop {boolean | {precision?: number, removeDefaults?: boolean, minifyColors?: boolean}} [minifySVG]
454
- * When true, enables SVG-specific optimizations for SVG elements and attributes.
455
- * If an object is provided, it can include:
456
- * - `precision`: Number of decimal places for numeric values (coordinates, path data, etc.). Default: `3`
457
- * - `removeDefaults`: Remove attributes with default values (e.g., `fill="black"`). Default: `true`
458
- * - `minifyColors`: Minify color values (hex shortening, rgb to hex conversion). Default: `true`
471
+ * @prop {boolean | Object} [minifySVG]
472
+ * When true, enables SVG minification using [SVGO](https://github.com/svg/svgo).
473
+ * Complete SVG subtrees are extracted and optimized as a block.
474
+ * If an object is provided, it is passed to SVGO as configuration options.
459
475
  * If disabled, SVG content is minified using standard HTML rules only.
460
476
  *
461
477
  * Default: `false`
@@ -1039,6 +1055,11 @@ async function minifyHTML(value, options, partialMarkup) {
1039
1055
  trimTrailingWhitespace(charsIndex, nextTag);
1040
1056
  }
1041
1057
 
1058
+ // SVG subtree capture: When SVGO is active, record buffer positions for post-processing
1059
+ const svgBlocks = []; // Array of { start, end } buffer indices
1060
+ let svgBufferStartIndex = -1;
1061
+ let svgDepth = 0;
1062
+
1042
1063
  const parser = new HTMLParser(value, {
1043
1064
  partialMarkup: partialMarkup ?? options.partialMarkup,
1044
1065
  continueOnParseError: options.continueOnParseError,
@@ -1056,6 +1077,10 @@ async function minifyHTML(value, options, partialMarkup) {
1056
1077
  options.name = identity;
1057
1078
  options.insideSVG = lowerTag === 'svg';
1058
1079
  options.insideForeignContent = true;
1080
+ // Disable HTML-specific options that produce invalid XML
1081
+ options.removeAttributeQuotes = false;
1082
+ options.removeTagWhitespace = false;
1083
+ options.decodeEntities = false;
1059
1084
  }
1060
1085
  // `foreignObject` in SVG and `annotation-xml` in MathML contain HTML content
1061
1086
  // Note: The element itself is in SVG/MathML namespace, only its children are HTML
@@ -1070,6 +1095,9 @@ async function minifyHTML(value, options, partialMarkup) {
1070
1095
  options.parentName = parentName; // Preserve for the element tag itself
1071
1096
  options.name = options.htmlName || lowercase;
1072
1097
  options.insideForeignContent = false;
1098
+ // Note: `removeAttributeQuotes`, `removeTagWhitespace`, and `decodeEntities`
1099
+ // stay disabled (inherited from SVG context) because the entire SVG block
1100
+ // must be valid XML for SVGO processing
1073
1101
  useParentNameForTag = true;
1074
1102
  }
1075
1103
  tag = (useParentNameForTag ? options.parentName : options.name)(tag);
@@ -1121,6 +1149,14 @@ async function minifyHTML(value, options, partialMarkup) {
1121
1149
  }
1122
1150
  }
1123
1151
 
1152
+ // Track SVG subtree for SVGO block processing
1153
+ if (lowerTag === 'svg' && options.minifySVG) {
1154
+ if (svgDepth === 0) {
1155
+ svgBufferStartIndex = buffer.length; // Record position before <svg> is pushed
1156
+ }
1157
+ svgDepth++;
1158
+ }
1159
+
1124
1160
  const openTag = '<' + tag;
1125
1161
  const hasUnarySlash = unarySlash && options.keepClosingSlash;
1126
1162
 
@@ -1251,6 +1287,15 @@ async function minifyHTML(value, options, partialMarkup) {
1251
1287
  currentChars += '|';
1252
1288
  }
1253
1289
  }
1290
+
1291
+ // SVG subtree capture: Record end position for post-processing with SVGO
1292
+ if (lowerTag === 'svg' && options.minifySVG && svgDepth > 0) {
1293
+ svgDepth--;
1294
+ if (svgDepth === 0 && svgBufferStartIndex >= 0) {
1295
+ svgBlocks.push({ start: svgBufferStartIndex, end: buffer.length });
1296
+ svgBufferStartIndex = -1;
1297
+ }
1298
+ }
1254
1299
  },
1255
1300
  chars: async function (text, prevTag, nextTag, prevAttrs, nextAttrs) {
1256
1301
  prevTag = prevTag === '' ? 'comment' : prevTag;
@@ -1475,6 +1520,19 @@ async function minifyHTML(value, options, partialMarkup) {
1475
1520
 
1476
1521
  await parser.parse();
1477
1522
 
1523
+ // Post-processing: Optimize SVG blocks with SVGO
1524
+ // Run all SVGO calls in parallel, then splice results in reverse to preserve indices
1525
+ if (options.minifySVG && svgBlocks.length) {
1526
+ const optimized = await Promise.all(
1527
+ svgBlocks.map(({ start, end }) =>
1528
+ options.minifySVG(buffer.slice(start, end).join(''))
1529
+ )
1530
+ );
1531
+ for (let i = svgBlocks.length - 1; i >= 0; i--) {
1532
+ buffer.splice(svgBlocks[i].start, svgBlocks[i].end - svgBlocks[i].start, optimized[i]);
1533
+ }
1534
+ }
1535
+
1478
1536
  if (options.removeOptionalTags) {
1479
1537
  // `<html>` may be omitted if first thing inside is not a comment
1480
1538
  // `<head>` or `<body>` may be omitted if empty
@@ -1561,7 +1619,7 @@ function joinResultSegments(results, options, restoreCustom, restoreIgnore) {
1561
1619
  * Important behavior notes:
1562
1620
  * - Caches are created on the first `minify()` call and persist for the lifetime of the process
1563
1621
  * - Cache sizes are locked after first initialization—subsequent calls use the same caches
1564
- * even if different `cacheCSS`/`cacheJS` options are provided
1622
+ * even if different `cacheCSS`/`cacheJS`/`cacheSVG` options are provided
1565
1623
  * - The first call’s options determine the cache sizes for subsequent calls
1566
1624
  * - Explicit `0` values are coerced to `1` (minimum functional cache size)
1567
1625
  */
@@ -1585,16 +1643,20 @@ function initCaches(options) {
1585
1643
  : (parseEnvCacheSize(process.env.HMN_CACHE_CSS) ?? defaultSize);
1586
1644
  const jsSize = options.cacheJS !== undefined ? options.cacheJS
1587
1645
  : (parseEnvCacheSize(process.env.HMN_CACHE_JS) ?? defaultSize);
1646
+ const svgSize = options.cacheSVG !== undefined ? options.cacheSVG
1647
+ : (parseEnvCacheSize(process.env.HMN_CACHE_SVG) ?? defaultSize);
1588
1648
 
1589
1649
  // Coerce `0` to `1` (minimum functional cache size) to avoid immediate eviction
1590
1650
  const cssFinalSize = cssSize === 0 ? 1 : cssSize;
1591
1651
  const jsFinalSize = jsSize === 0 ? 1 : jsSize;
1652
+ const svgFinalSize = svgSize === 0 ? 1 : svgSize;
1592
1653
 
1593
1654
  cssMinifyCache = new LRU(cssFinalSize);
1594
1655
  jsMinifyCache = new LRU(jsFinalSize);
1656
+ svgMinifyCache = new LRU(svgFinalSize);
1595
1657
  }
1596
1658
 
1597
- return { cssMinifyCache, jsMinifyCache };
1659
+ return { cssMinifyCache, jsMinifyCache, svgMinifyCache };
1598
1660
  }
1599
1661
 
1600
1662
  /**
@@ -1612,6 +1674,7 @@ export const minify = async function (value, options) {
1612
1674
  getLightningCSS,
1613
1675
  getTerser,
1614
1676
  getSwc,
1677
+ getSvgo,
1615
1678
  ...caches
1616
1679
  });
1617
1680
  let result = await minifyHTML(value, options);
@@ -21,7 +21,6 @@ import {
21
21
  } from './constants.js';
22
22
  import { trimWhitespace, collapseWhitespaceAll } from './whitespace.js';
23
23
  import { shouldMinifyInnerHTML } from './options.js';
24
- import { minifySVGAttributeValue, shouldRemoveSVGAttribute } from './svg.js';
25
24
 
26
25
  // Validators
27
26
 
@@ -407,17 +406,6 @@ async function cleanAttributeValue(tag, attrName, attrValue, options, attrs, min
407
406
  return attrValue;
408
407
  }
409
408
  return minifyHTMLSelf(attrValue, options, true);
410
- } else if (options.insideSVG && options.minifySVG) {
411
- // Apply SVG-specific attribute minification when inside SVG elements
412
- try {
413
- return minifySVGAttributeValue(attrName, attrValue, options.minifySVG);
414
- } catch (err) {
415
- if (!options.continueOnMinifyError) {
416
- throw err;
417
- }
418
- options.log && options.log(err);
419
- return attrValue;
420
- }
421
409
  }
422
410
  return attrValue;
423
411
  }
@@ -458,9 +446,7 @@ async function normalizeAttr(attr, attrs, tag, options, minifyHTML) {
458
446
  (options.removeScriptTypeAttributes && tag === 'script' &&
459
447
  attrName === 'type' && isScriptTypeAttribute(attrValue) && !keepScriptTypeAttribute(attrValue)) ||
460
448
  (options.removeStyleLinkTypeAttributes && (tag === 'style' || tag === 'link') &&
461
- attrName === 'type' && isStyleLinkTypeAttribute(attrValue)) ||
462
- (options.insideSVG && options.minifySVG &&
463
- shouldRemoveSVGAttribute(tag, attrName, attrValue, options.minifySVG))) {
449
+ attrName === 'type' && isStyleLinkTypeAttribute(attrValue))) {
464
450
  return;
465
451
  }
466
452
 
@@ -17,7 +17,6 @@ const RE_ESCAPE_LT = /</g;
17
17
  const RE_ATTR_WS_CHECK = /[ \n\r\t\f]/;
18
18
  const RE_ATTR_WS_COLLAPSE = /[ \n\r\t\f]+/g;
19
19
  const RE_ATTR_WS_TRIM = /^[ \n\r\t\f]+|[ \n\r\t\f]+$/g;
20
- const RE_NUMERIC_VALUE = /-?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/g;
21
20
 
22
21
  // Inline element sets for whitespace handling
23
22
 
@@ -191,8 +190,6 @@ export {
191
190
  RE_ATTR_WS_CHECK,
192
191
  RE_ATTR_WS_COLLAPSE,
193
192
  RE_ATTR_WS_TRIM,
194
- RE_NUMERIC_VALUE,
195
-
196
193
  // Inline element sets
197
194
  inlineElementsToKeepWhitespaceAround,
198
195
  inlineElementsToKeepWhitespaceWithin,
@@ -98,7 +98,7 @@ const optionDefinitions = {
98
98
  type: 'json'
99
99
  },
100
100
  minifySVG: {
101
- description: 'Minify SVG elements and attributes (numeric precision, default attributes, colors)',
101
+ description: 'Minify SVG elements (uses SVGO)',
102
102
  type: 'json'
103
103
  },
104
104
  minifyURLs: {
@@ -5,7 +5,6 @@ import { LRU, stableStringify, identity, lowercase, identityAsync, replaceAsync,
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';
8
- import { getSVGMinifierOptions } from './svg.js';
9
8
  import { getPreset, getPresetNames } from '../presets.js';
10
9
  import { optionDefaults } from './option-definitions.js';
11
10
 
@@ -31,11 +30,13 @@ function shouldMinifyInnerHTML(options) {
31
30
  * @param {Function} deps.getLightningCSS - Function to lazily load Lightning CSS
32
31
  * @param {Function} deps.getTerser - Function to lazily load Terser
33
32
  * @param {Function} deps.getSwc - Function to lazily load @swc/core
33
+ * @param {Function} deps.getSvgo - Function to lazily load SVGO
34
34
  * @param {LRU} deps.cssMinifyCache - CSS minification cache
35
35
  * @param {LRU} deps.jsMinifyCache - JS minification cache
36
+ * @param {LRU} deps.svgMinifyCache - SVG minification cache
36
37
  * @returns {MinifierOptions} Normalized options with defaults applied
37
38
  */
38
- const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, cssMinifyCache, jsMinifyCache } = {}) => {
39
+ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, getSvgo, cssMinifyCache, jsMinifyCache, svgMinifyCache } = {}) => {
39
40
  const options = {
40
41
  name: lowercase,
41
42
  canCollapseWhitespace,
@@ -336,11 +337,54 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, cssM
336
337
  return text;
337
338
  }
338
339
  };
339
- } else if (key === 'minifySVG') {
340
- // Process SVG minification options
341
- // Unlike `minifyCSS`/`minifyJS`, this is a simple options object, not a function
342
- // The actual minification is applied inline during attribute processing
343
- options.minifySVG = getSVGMinifierOptions(option);
340
+ } else if (key === 'minifySVG' && typeof option !== 'function') {
341
+ if (!option) {
342
+ return;
343
+ }
344
+
345
+ const svgoOptions = typeof option === 'object' ? option : {};
346
+
347
+ // Pre-compute option signature for cache keys
348
+ const svgSig = stableStringify({
349
+ ...svgoOptions,
350
+ cont: !!options.continueOnMinifyError
351
+ });
352
+
353
+ options.minifySVG = async function (svgContent) {
354
+ if (!svgContent || !svgContent.trim()) {
355
+ return svgContent;
356
+ }
357
+
358
+ // Cache key
359
+ const svgKey = svgContent.length > 2048
360
+ ? (svgContent.length + '|' + svgContent.slice(0, 50) + svgContent.slice(-50) + '|' + svgSig)
361
+ : (svgContent + '|' + svgSig);
362
+
363
+ try {
364
+ const cached = svgMinifyCache.get(svgKey);
365
+ if (cached) {
366
+ return await cached;
367
+ }
368
+
369
+ const inFlight = (async () => {
370
+ const optimize = await getSvgo();
371
+ const result = optimize(svgContent, svgoOptions);
372
+ return result.data;
373
+ })();
374
+
375
+ svgMinifyCache.set(svgKey, inFlight);
376
+ const resolved = await inFlight;
377
+ svgMinifyCache.set(svgKey, resolved);
378
+ return resolved;
379
+ } catch (err) {
380
+ svgMinifyCache.delete(svgKey);
381
+ if (!options.continueOnMinifyError) {
382
+ throw err;
383
+ }
384
+ options.log && options.log(err);
385
+ return svgContent;
386
+ }
387
+ };
344
388
  } else if (key === 'customAttrCollapse') {
345
389
  // Single regex pattern
346
390
  options[key] = parseRegExp(option);
@@ -1,24 +0,0 @@
1
- /**
2
- * Minify SVG attribute value based on attribute name
3
- * @param {string} name - Attribute name
4
- * @param {string} value - Attribute value
5
- * @param {Object} options - Minification options
6
- * @returns {string} Minified attribute value
7
- */
8
- export function minifySVGAttributeValue(name: string, value: string, options?: any): string;
9
- /**
10
- * Check if an SVG attribute can be removed
11
- * @param {string} tag - Element tag name (e.g., `svg`, `rect`, `path`)
12
- * @param {string} name - Attribute name
13
- * @param {string} value - Attribute value
14
- * @param {Object} options - Minification options
15
- * @returns {boolean} True if attribute should be removed
16
- */
17
- export function shouldRemoveSVGAttribute(tag: string, name: string, value: string, options?: any): boolean;
18
- /**
19
- * Get default SVG minification options
20
- * @param {Object} userOptions - User-provided options
21
- * @returns {Object} Complete options object with defaults
22
- */
23
- export function getSVGMinifierOptions(userOptions: any): any;
24
- //# sourceMappingURL=svg.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"svg.d.ts","sourceRoot":"","sources":["../../../src/lib/svg.js"],"names":[],"mappings":"AA0VA;;;;;;GAMG;AACH,8CALW,MAAM,SACN,MAAM,kBAEJ,MAAM,CA0BlB;AAED;;;;;;;GAOG;AACH,8CANW,MAAM,QACN,MAAM,SACN,MAAM,kBAEJ,OAAO,CAanB;AAED;;;;GAIG;AACH,6DAkBC"}