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.
- package/README.md +48 -66
- package/cli.js +1 -0
- package/dist/htmlminifier.cjs +123 -450
- package/dist/htmlminifier.esm.bundle.js +123 -450
- package/dist/types/htmlminifier.d.ts +14 -10
- package/dist/types/htmlminifier.d.ts.map +1 -1
- package/dist/types/lib/attributes.d.ts.map +1 -1
- package/dist/types/lib/constants.d.ts +0 -1
- package/dist/types/lib/constants.d.ts.map +1 -1
- package/dist/types/lib/options.d.ts +3 -1
- package/dist/types/lib/options.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/htmlminifier.js +71 -8
- package/src/lib/attributes.js +1 -15
- package/src/lib/constants.js +0 -3
- package/src/lib/option-definitions.js +1 -1
- package/src/lib/options.js +51 -7
- package/dist/types/lib/svg.d.ts +0 -24
- package/dist/types/lib/svg.d.ts.map +0 -1
- package/src/lib/svg.js +0 -424
|
@@ -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
|
|
273
|
-
*
|
|
274
|
-
*
|
|
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":"
|
|
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":"
|
|
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;
|
|
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":"
|
|
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
|
|
98
|
+
"version": "5.1.0"
|
|
98
99
|
}
|
package/src/htmlminifier.js
CHANGED
|
@@ -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 |
|
|
454
|
-
* When true, enables SVG
|
|
455
|
-
*
|
|
456
|
-
*
|
|
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);
|
package/src/lib/attributes.js
CHANGED
|
@@ -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
|
|
package/src/lib/constants.js
CHANGED
|
@@ -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
|
|
101
|
+
description: 'Minify SVG elements (uses SVGO)',
|
|
102
102
|
type: 'json'
|
|
103
103
|
},
|
|
104
104
|
minifyURLs: {
|
package/src/lib/options.js
CHANGED
|
@@ -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
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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);
|
package/dist/types/lib/svg.d.ts
DELETED
|
@@ -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"}
|