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
package/src/lib/svg.js
DELETED
|
@@ -1,424 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Lightweight SVG optimizations:
|
|
3
|
-
* - Numeric precision reduction for coordinates and path data
|
|
4
|
-
* - Whitespace removal in attribute values (numeric sequences)
|
|
5
|
-
* - Default attribute removal (safe, well-documented defaults)
|
|
6
|
-
* - Color minification (hex shortening, rgb() to hex, named colors)
|
|
7
|
-
* - Identity transform removal
|
|
8
|
-
* - Path data space optimization
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
// Imports
|
|
12
|
-
|
|
13
|
-
import { LRU } from './utils.js';
|
|
14
|
-
import { RE_NUMERIC_VALUE } from './constants.js';
|
|
15
|
-
|
|
16
|
-
// Cache for minified numbers
|
|
17
|
-
const numberCache = new LRU(100);
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Named colors that are shorter than their hex equivalents
|
|
21
|
-
* Only includes cases where using the name saves bytes
|
|
22
|
-
*/
|
|
23
|
-
const NAMED_COLORS = {
|
|
24
|
-
'#f00': 'red', // #f00 (4) → red (3), saves 1
|
|
25
|
-
'#c0c0c0': 'silver', // #c0c0c0 (7) → silver (6), saves 1
|
|
26
|
-
'#808080': 'gray', // #808080 (7) → gray (4), saves 3
|
|
27
|
-
'#800000': 'maroon', // #800000 (7) → maroon (6), saves 1
|
|
28
|
-
'#808000': 'olive', // #808000 (7) → olive (5), saves 2
|
|
29
|
-
'#008000': 'green', // #008000 (7) → green (5), saves 2
|
|
30
|
-
'#800080': 'purple', // #800080 (7) → purple (6), saves 1
|
|
31
|
-
'#008080': 'teal', // #008080 (7) → teal (4), saves 3
|
|
32
|
-
'#000080': 'navy', // #000080 (7) → navy (4), saves 3
|
|
33
|
-
'#ffa500': 'orange' // #ffa500 (7) → orange (6), saves 1
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Default SVG attribute values that can be safely removed
|
|
38
|
-
* Only includes well-documented, widely-supported defaults
|
|
39
|
-
*/
|
|
40
|
-
const SVG_DEFAULT_ATTRS = {
|
|
41
|
-
// Fill and stroke defaults
|
|
42
|
-
fill: value => value === 'black' || value === '#000' || value === '#000000',
|
|
43
|
-
'fill-opacity': value => value === '1',
|
|
44
|
-
'fill-rule': value => value === 'nonzero',
|
|
45
|
-
stroke: value => value === 'none',
|
|
46
|
-
'stroke-dasharray': value => value === 'none',
|
|
47
|
-
'stroke-dashoffset': value => value === '0',
|
|
48
|
-
'stroke-linecap': value => value === 'butt',
|
|
49
|
-
'stroke-linejoin': value => value === 'miter',
|
|
50
|
-
'stroke-miterlimit': value => value === '4',
|
|
51
|
-
'stroke-opacity': value => value === '1',
|
|
52
|
-
'stroke-width': value => value === '1',
|
|
53
|
-
|
|
54
|
-
// Text and font defaults
|
|
55
|
-
'font-family': value => value === 'inherit',
|
|
56
|
-
'font-size': value => value === 'medium',
|
|
57
|
-
'font-style': value => value === 'normal',
|
|
58
|
-
'font-variant': value => value === 'normal',
|
|
59
|
-
'font-weight': value => value === 'normal',
|
|
60
|
-
'letter-spacing': value => value === 'normal',
|
|
61
|
-
'text-decoration': value => value === 'none',
|
|
62
|
-
'text-anchor': value => value === 'start',
|
|
63
|
-
|
|
64
|
-
// Other common defaults
|
|
65
|
-
opacity: value => value === '1',
|
|
66
|
-
visibility: value => value === 'visible',
|
|
67
|
-
display: value => value === 'inline',
|
|
68
|
-
// Note: Overflow handled especially in `isDefaultAttribute` (not safe for root `<svg>`)
|
|
69
|
-
|
|
70
|
-
// Clipping and masking defaults
|
|
71
|
-
'clip-rule': value => value === 'nonzero',
|
|
72
|
-
'clip-path': value => value === 'none',
|
|
73
|
-
mask: value => value === 'none',
|
|
74
|
-
|
|
75
|
-
// Marker defaults
|
|
76
|
-
'marker-start': value => value === 'none',
|
|
77
|
-
'marker-mid': value => value === 'none',
|
|
78
|
-
'marker-end': value => value === 'none',
|
|
79
|
-
|
|
80
|
-
// Filter and color defaults
|
|
81
|
-
filter: value => value === 'none',
|
|
82
|
-
'color-interpolation': value => value === 'sRGB',
|
|
83
|
-
'color-interpolation-filters': value => value === 'linearRGB'
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Minify numeric value by removing trailing zeros and unnecessary decimals
|
|
88
|
-
* @param {string} num - Numeric string to minify
|
|
89
|
-
* @param {number} precision - Maximum decimal places to keep
|
|
90
|
-
* @returns {string} Minified numeric string
|
|
91
|
-
*/
|
|
92
|
-
function minifyNumber(num, precision = 3) {
|
|
93
|
-
// Fast path for common values (avoids parsing and caching)
|
|
94
|
-
if (num === '0' || num === '1') return num;
|
|
95
|
-
// Common decimal variants that tools export
|
|
96
|
-
if (num === '0.0' || num === '0.00' || num === '0.000') return '0';
|
|
97
|
-
if (num === '1.0' || num === '1.00' || num === '1.000') return '1';
|
|
98
|
-
|
|
99
|
-
// Check cache
|
|
100
|
-
// (Note: Uses input string as key, so “0.0000” and “0.00000” create separate entries.
|
|
101
|
-
// This is intentional to avoid parsing overhead.
|
|
102
|
-
// Real-world SVG files from export tools typically use consistent formats.)
|
|
103
|
-
const cacheKey = `${num}:${precision}`;
|
|
104
|
-
const cached = numberCache.get(cacheKey);
|
|
105
|
-
if (cached !== undefined) return cached;
|
|
106
|
-
|
|
107
|
-
const parsed = parseFloat(num);
|
|
108
|
-
|
|
109
|
-
// Handle special cases
|
|
110
|
-
if (isNaN(parsed)) return num;
|
|
111
|
-
if (parsed === 0) return '0';
|
|
112
|
-
if (!isFinite(parsed)) return num;
|
|
113
|
-
|
|
114
|
-
// Convert to fixed precision, then remove trailing zeros
|
|
115
|
-
const fixed = parsed.toFixed(precision);
|
|
116
|
-
const trimmed = fixed.replace(/\.?0+$/, '');
|
|
117
|
-
|
|
118
|
-
// Remove leading zero before decimal point (e.g., `0.5` → `.5`, `-0.3` → `-.3`)
|
|
119
|
-
const result = (trimmed || '0').replace(/^(-?)0\./, '$1.');
|
|
120
|
-
numberCache.set(cacheKey, result);
|
|
121
|
-
return result;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Minify SVG path data by reducing numeric precision and removing unnecessary spaces
|
|
126
|
-
* @param {string} pathData - SVG path data string
|
|
127
|
-
* @param {number} precision - Decimal precision for coordinates
|
|
128
|
-
* @returns {string} Minified path data
|
|
129
|
-
*/
|
|
130
|
-
function minifyPathData(pathData, precision = 3) {
|
|
131
|
-
if (!pathData || typeof pathData !== 'string') return pathData;
|
|
132
|
-
|
|
133
|
-
// First, minify all numbers
|
|
134
|
-
let result = pathData.replace(RE_NUMERIC_VALUE, (match) => {
|
|
135
|
-
return minifyNumber(match, precision);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
// Remove unnecessary spaces around path commands
|
|
139
|
-
// Safe to remove space after a command letter when it’s followed by a number
|
|
140
|
-
// (which may be negative or start with a decimal point)
|
|
141
|
-
// `M 10 20` → `M10 20`, `L -5 -3` → `L-5-3`, `M .5 .3` → `M.5.3`
|
|
142
|
-
result = result.replace(/([MLHVCSQTAZmlhvcsqtaz])\s+(?=-?\.?\d)/g, '$1');
|
|
143
|
-
|
|
144
|
-
// Safe to remove space before command letter when preceded by a number
|
|
145
|
-
// `0 L` → `0L`, `20 M` → `20M`, `.5 L` → `.5L`
|
|
146
|
-
result = result.replace(/([\d.])\s+([MLHVCSQTAZmlhvcsqtaz])/g, '$1$2');
|
|
147
|
-
|
|
148
|
-
// Safe to remove space before negative number when preceded by a number
|
|
149
|
-
// `10 -20` → `10-20`, `.5 -.3` → `.5-.3` (minus sign is always a separator)
|
|
150
|
-
result = result.replace(/([\d.])\s+(-)/g, '$1$2');
|
|
151
|
-
|
|
152
|
-
// Safe to remove space between two decimal numbers (decimal point acts as separator)
|
|
153
|
-
// `.5 .3` → `.5.3` (only when previous char is `.`, indicating a complete decimal)
|
|
154
|
-
// Note: `0 .3` must not become `0.3` (that would change two numbers into one)
|
|
155
|
-
result = result.replace(/(\.\d*)\s+(\.)/g, '$1$2');
|
|
156
|
-
|
|
157
|
-
return result;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Minify whitespace in numeric attribute values
|
|
162
|
-
* Examples:
|
|
163
|
-
* - “10 , 20" → "10,20"
|
|
164
|
-
* - "translate( 10 20 )" → "translate(10 20)"
|
|
165
|
-
* - "100, 10 40, 198" → "100,10 40,198"
|
|
166
|
-
*
|
|
167
|
-
* @param {string} value - Attribute value to minify
|
|
168
|
-
* @returns {string} Minified value
|
|
169
|
-
*/
|
|
170
|
-
function minifyAttributeWhitespace(value) {
|
|
171
|
-
if (!value || typeof value !== 'string') return value;
|
|
172
|
-
|
|
173
|
-
return value
|
|
174
|
-
// Remove spaces around commas
|
|
175
|
-
.replace(/\s*,\s*/g, ',')
|
|
176
|
-
// Remove spaces around parentheses
|
|
177
|
-
.replace(/\(\s+/g, '(')
|
|
178
|
-
.replace(/\s+\)/g, ')')
|
|
179
|
-
// Collapse multiple spaces to single space
|
|
180
|
-
.replace(/\s+/g, ' ')
|
|
181
|
-
// Trim leading/trailing whitespace
|
|
182
|
-
.trim();
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Minify color values (hex shortening, rgb to hex conversion, named colors)
|
|
187
|
-
* Only processes simple color values; preserves case-sensitive references like `url(#id)`
|
|
188
|
-
* @param {string} color - Color value to minify
|
|
189
|
-
* @returns {string} Minified color value
|
|
190
|
-
*/
|
|
191
|
-
function minifyColor(color) {
|
|
192
|
-
if (!color || typeof color !== 'string') return color;
|
|
193
|
-
|
|
194
|
-
const trimmed = color.trim();
|
|
195
|
-
|
|
196
|
-
// Don’t process values that aren’t simple colors (preserve case-sensitive references)
|
|
197
|
-
// `url(#id)`, `var(--name)`, `inherit`, `currentColor`, etc.
|
|
198
|
-
if (trimmed.includes('url(') || trimmed.includes('var(') || trimmed === 'inherit' || trimmed === 'currentColor') {
|
|
199
|
-
return trimmed;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Now safe to lowercase for color matching
|
|
203
|
-
const lower = trimmed.toLowerCase();
|
|
204
|
-
|
|
205
|
-
// Shorten 6-digit hex to 3-digit when possible
|
|
206
|
-
// `#aabbcc` → `#abc`, `#000000` → `#000`
|
|
207
|
-
const hexMatch = lower.match(/^#([0-9a-f]{6})$/);
|
|
208
|
-
if (hexMatch) {
|
|
209
|
-
const hex = hexMatch[1];
|
|
210
|
-
if (hex[0] === hex[1] && hex[2] === hex[3] && hex[4] === hex[5]) {
|
|
211
|
-
const shortened = '#' + hex[0] + hex[2] + hex[4];
|
|
212
|
-
// Try to use named color if shorter
|
|
213
|
-
return NAMED_COLORS[shortened] || shortened;
|
|
214
|
-
}
|
|
215
|
-
// Can’t shorten, but check for named color
|
|
216
|
-
return NAMED_COLORS[lower] || lower;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Match 3-digit hex colors
|
|
220
|
-
const hex3Match = lower.match(/^#[0-9a-f]{3}$/);
|
|
221
|
-
if (hex3Match) {
|
|
222
|
-
// Check if there’s a shorter named color
|
|
223
|
-
return NAMED_COLORS[lower] || lower;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// Convert rgb() to hex
|
|
227
|
-
const rgbMatch = lower.match(/^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/);
|
|
228
|
-
if (rgbMatch) {
|
|
229
|
-
const r = parseInt(rgbMatch[1], 10);
|
|
230
|
-
const g = parseInt(rgbMatch[2], 10);
|
|
231
|
-
const b = parseInt(rgbMatch[3], 10);
|
|
232
|
-
|
|
233
|
-
if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) {
|
|
234
|
-
const toHex = (n) => {
|
|
235
|
-
const h = n.toString(16);
|
|
236
|
-
return h.length === 1 ? '0' + h : h;
|
|
237
|
-
};
|
|
238
|
-
const hexColor = '#' + toHex(r) + toHex(g) + toHex(b);
|
|
239
|
-
|
|
240
|
-
// Try to shorten if possible
|
|
241
|
-
if (hexColor[1] === hexColor[2] && hexColor[3] === hexColor[4] && hexColor[5] === hexColor[6]) {
|
|
242
|
-
const shortened = '#' + hexColor[1] + hexColor[3] + hexColor[5];
|
|
243
|
-
return NAMED_COLORS[shortened] || shortened;
|
|
244
|
-
}
|
|
245
|
-
return NAMED_COLORS[hexColor] || hexColor;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// Not a recognized color format, return as-is (preserves case)
|
|
250
|
-
return trimmed;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// Attributes that contain numeric sequences or path data
|
|
254
|
-
const NUMERIC_ATTRS = new Set([
|
|
255
|
-
'd', // Path data
|
|
256
|
-
'points', // Polygon/polyline points
|
|
257
|
-
'viewBox', // `viewBox` coordinates
|
|
258
|
-
'transform', // Transform functions
|
|
259
|
-
'x', 'y', 'x1', 'y1', 'x2', 'y2', // Coordinates
|
|
260
|
-
'cx', 'cy', 'r', 'rx', 'ry', // Circle/ellipse
|
|
261
|
-
'width', 'height', // Dimensions
|
|
262
|
-
'dx', 'dy', // Text offsets
|
|
263
|
-
'offset', // Gradient offset
|
|
264
|
-
'startOffset', // `textPath`
|
|
265
|
-
'pathLength', // Path length
|
|
266
|
-
'stdDeviation', // Filter params
|
|
267
|
-
'baseFrequency', // Turbulence
|
|
268
|
-
'k1', 'k2', 'k3', 'k4' // Composite filter
|
|
269
|
-
]);
|
|
270
|
-
|
|
271
|
-
// Attributes that contain color values
|
|
272
|
-
const COLOR_ATTRS = new Set([
|
|
273
|
-
'fill',
|
|
274
|
-
'stroke',
|
|
275
|
-
'stop-color',
|
|
276
|
-
'flood-color',
|
|
277
|
-
'lighting-color'
|
|
278
|
-
]);
|
|
279
|
-
|
|
280
|
-
// Pre-compiled regexes for identity transform detection (compiled once at module load)
|
|
281
|
-
// Separator pattern: Accepts comma with optional spaces or one or more spaces
|
|
282
|
-
const SEP = '(?:\\s*,\\s*|\\s+)';
|
|
283
|
-
|
|
284
|
-
// `translate(0)`, `translate(0,0)`, `translate(0 0)` (matches 0, 0.0, 0.00, etc.)
|
|
285
|
-
const IDENTITY_TRANSLATE_RE = new RegExp(`^translate\\s*\\(\\s*0(?:\\.0+)?\\s*(?:${SEP}0(?:\\.0+)?\\s*)?\\)$`, 'i');
|
|
286
|
-
|
|
287
|
-
// `scale(1)`, `scale(1,1)`, `scale(1 1)` (matches 1, 1.0, 1.00, etc.)
|
|
288
|
-
const IDENTITY_SCALE_RE = new RegExp(`^scale\\s*\\(\\s*1(?:\\.0+)?\\s*(?:${SEP}1(?:\\.0+)?\\s*)?\\)$`, 'i');
|
|
289
|
-
|
|
290
|
-
// `rotate(0)`, `rotate(0 cx cy)`, `rotate(0, cx, cy)` (matches 0, 0.0, 0.00, etc.)
|
|
291
|
-
// Note: `cx` and `cy` must be valid numbers if present
|
|
292
|
-
const IDENTITY_ROTATE_RE = new RegExp(`^rotate\\s*\\(\\s*0(?:\\.0+)?\\s*(?:${SEP}-?\\d+(?:\\.\\d+)?${SEP}-?\\d+(?:\\.\\d+)?)?\\s*\\)$`, 'i');
|
|
293
|
-
|
|
294
|
-
// `skewX(0)`, `skewY(0)` (matches 0, 0.0, 0.00, etc.)
|
|
295
|
-
const IDENTITY_SKEW_RE = /^skew[XY]\s*\(\s*0(?:\.0+)?\s*\)$/i;
|
|
296
|
-
|
|
297
|
-
// `matrix(1,0,0,1,0,0)`, `matrix(1 0 0 1 0 0)`—identity matrix (matches 1.0/0.0 variants)
|
|
298
|
-
const IDENTITY_MATRIX_RE = new RegExp(`^matrix\\s*\\(\\s*1(?:\\.0+)?\\s*${SEP}0(?:\\.0+)?\\s*${SEP}0(?:\\.0+)?\\s*${SEP}1(?:\\.0+)?\\s*${SEP}0(?:\\.0+)?\\s*${SEP}0(?:\\.0+)?\\s*\\)$`, 'i');
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Check if a transform attribute has no effect (identity transform)
|
|
302
|
-
* @param {string} transform - Transform attribute value
|
|
303
|
-
* @returns {boolean} True if transform is an identity (has no effect)
|
|
304
|
-
*/
|
|
305
|
-
function isIdentityTransform(transform) {
|
|
306
|
-
if (!transform || typeof transform !== 'string') return false;
|
|
307
|
-
|
|
308
|
-
const trimmed = transform.trim();
|
|
309
|
-
|
|
310
|
-
// Check for common identity transforms using pre-compiled regexes
|
|
311
|
-
return IDENTITY_TRANSLATE_RE.test(trimmed) ||
|
|
312
|
-
IDENTITY_SCALE_RE.test(trimmed) ||
|
|
313
|
-
IDENTITY_ROTATE_RE.test(trimmed) ||
|
|
314
|
-
IDENTITY_SKEW_RE.test(trimmed) ||
|
|
315
|
-
IDENTITY_MATRIX_RE.test(trimmed);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
/**
|
|
319
|
-
* Check if an attribute should be removed based on default value
|
|
320
|
-
* @param {string} tag - Element tag name (e.g., `svg`, `rect`, `path`)
|
|
321
|
-
* @param {string} name - Attribute name
|
|
322
|
-
* @param {string} value - Attribute value
|
|
323
|
-
* @returns {boolean} True if attribute can be removed
|
|
324
|
-
*/
|
|
325
|
-
function isDefaultAttribute(tag, name, value) {
|
|
326
|
-
// Special case: `overflow="visible"` is unsafe for root `<svg>` element
|
|
327
|
-
// Root SVG may need explicit `overflow="visible"` to show clipped content
|
|
328
|
-
if (name === 'overflow' && value === 'visible') {
|
|
329
|
-
return tag !== 'svg'; // Only remove for non-root SVG elements
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
const checker = SVG_DEFAULT_ATTRS[name];
|
|
333
|
-
if (!checker) return false;
|
|
334
|
-
|
|
335
|
-
// Special case: Don’t remove `fill="black"` if stroke exists without fill
|
|
336
|
-
// This would change the rendering (stroke-only shapes would gain black fill)
|
|
337
|
-
if (name === 'fill' && checker(value)) {
|
|
338
|
-
// This check would require looking at other attributes on the same element
|
|
339
|
-
// For safety, we’ll keep this conservative and not remove `fill="black"`
|
|
340
|
-
// in the initial implementation. Can be refined later.
|
|
341
|
-
return false;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
return checker(value);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Minify SVG attribute value based on attribute name
|
|
349
|
-
* @param {string} name - Attribute name
|
|
350
|
-
* @param {string} value - Attribute value
|
|
351
|
-
* @param {Object} options - Minification options
|
|
352
|
-
* @returns {string} Minified attribute value
|
|
353
|
-
*/
|
|
354
|
-
export function minifySVGAttributeValue(name, value, options = {}) {
|
|
355
|
-
if (!value || typeof value !== 'string') return value;
|
|
356
|
-
|
|
357
|
-
const { precision = 3, minifyColors = true } = options;
|
|
358
|
-
|
|
359
|
-
// Path data gets special treatment
|
|
360
|
-
if (name === 'd') {
|
|
361
|
-
return minifyPathData(value, precision);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
// Numeric attributes get precision reduction and whitespace minification
|
|
365
|
-
if (NUMERIC_ATTRS.has(name)) {
|
|
366
|
-
const minified = value.replace(RE_NUMERIC_VALUE, (match) => {
|
|
367
|
-
return minifyNumber(match, precision);
|
|
368
|
-
});
|
|
369
|
-
return minifyAttributeWhitespace(minified);
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// Color attributes get color minification
|
|
373
|
-
if (minifyColors && COLOR_ATTRS.has(name)) {
|
|
374
|
-
return minifyColor(value);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
return value;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
/**
|
|
381
|
-
* Check if an SVG attribute can be removed
|
|
382
|
-
* @param {string} tag - Element tag name (e.g., `svg`, `rect`, `path`)
|
|
383
|
-
* @param {string} name - Attribute name
|
|
384
|
-
* @param {string} value - Attribute value
|
|
385
|
-
* @param {Object} options - Minification options
|
|
386
|
-
* @returns {boolean} True if attribute should be removed
|
|
387
|
-
*/
|
|
388
|
-
export function shouldRemoveSVGAttribute(tag, name, value, options = {}) {
|
|
389
|
-
const { removeDefaults = true } = options;
|
|
390
|
-
|
|
391
|
-
if (!removeDefaults) return false;
|
|
392
|
-
|
|
393
|
-
// Check for identity transforms
|
|
394
|
-
if (name === 'transform' && isIdentityTransform(value)) {
|
|
395
|
-
return true;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
return isDefaultAttribute(tag, name, value);
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
/**
|
|
402
|
-
* Get default SVG minification options
|
|
403
|
-
* @param {Object} userOptions - User-provided options
|
|
404
|
-
* @returns {Object} Complete options object with defaults
|
|
405
|
-
*/
|
|
406
|
-
export function getSVGMinifierOptions(userOptions) {
|
|
407
|
-
if (typeof userOptions === 'boolean') {
|
|
408
|
-
return userOptions ? {
|
|
409
|
-
precision: 3,
|
|
410
|
-
removeDefaults: true,
|
|
411
|
-
minifyColors: true
|
|
412
|
-
} : null;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
if (typeof userOptions === 'object' && userOptions !== null) {
|
|
416
|
-
return {
|
|
417
|
-
precision: userOptions.precision ?? 3,
|
|
418
|
-
removeDefaults: userOptions.removeDefaults ?? true,
|
|
419
|
-
minifyColors: userOptions.minifyColors ?? true
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
return null;
|
|
424
|
-
}
|