html-minifier-next 4.17.2 → 4.19.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 +93 -21
- package/cli.js +3 -0
- package/dist/htmlminifier.cjs +344 -99
- package/dist/htmlminifier.esm.bundle.js +344 -99
- package/dist/types/htmlminifier.d.ts +28 -0
- package/dist/types/htmlminifier.d.ts.map +1 -1
- package/dist/types/htmlparser.d.ts.map +1 -1
- package/dist/types/lib/attributes.d.ts.map +1 -1
- package/dist/types/lib/constants.d.ts +1 -0
- package/dist/types/lib/constants.d.ts.map +1 -1
- package/dist/types/lib/options.d.ts +1 -2
- package/dist/types/lib/options.d.ts.map +1 -1
- package/dist/types/lib/svg.d.ts.map +1 -1
- package/dist/types/presets.d.ts +1 -0
- package/dist/types/presets.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/htmlminifier.js +206 -12
- package/src/htmlparser.js +111 -63
- package/src/lib/attributes.js +4 -1
- package/src/lib/constants.js +6 -0
- package/src/lib/options.js +8 -14
- package/src/lib/svg.js +15 -8
- package/src/presets.js +1 -0
|
@@ -36,6 +36,26 @@ export type MinifierOptions = {
|
|
|
36
36
|
* Default: Built-in `canTrimWhitespace` function
|
|
37
37
|
*/
|
|
38
38
|
canTrimWhitespace?: (tag: string | null, attrs: HTMLAttribute[] | undefined, canTrimWhitespace: (tag: string) => boolean) => boolean;
|
|
39
|
+
/**
|
|
40
|
+
* The maximum number of entries for the CSS minification cache. Higher values
|
|
41
|
+
* improve performance for inputs with repeated CSS (e.g., batch processing).
|
|
42
|
+
* - Cache is created on first `minify()` call and persists for the process lifetime
|
|
43
|
+
* - Cache size is locked after first call—subsequent calls reuse the same cache
|
|
44
|
+
* - Explicit `0` values are coerced to `1` (minimum functional cache size)
|
|
45
|
+
*
|
|
46
|
+
* Default: `500` (or `1000` when `CI=true` environment variable is set)
|
|
47
|
+
*/
|
|
48
|
+
cacheCSS?: number;
|
|
49
|
+
/**
|
|
50
|
+
* The maximum number of entries for the JavaScript minification cache. Higher
|
|
51
|
+
* values improve performance for inputs with repeated JavaScript.
|
|
52
|
+
* - Cache is created on first `minify()` call and persists for the process lifetime
|
|
53
|
+
* - Cache size is locked after first call—subsequent calls reuse the same cache
|
|
54
|
+
* - Explicit `0` values are coerced to `1` (minimum functional cache size)
|
|
55
|
+
*
|
|
56
|
+
* Default: `500` (or `1000` when `CI=true` environment variable is set)
|
|
57
|
+
*/
|
|
58
|
+
cacheJS?: number;
|
|
39
59
|
/**
|
|
40
60
|
* When true, tag and attribute names are treated as case-sensitive.
|
|
41
61
|
* Useful for custom HTML tags.
|
|
@@ -208,6 +228,14 @@ export type MinifierOptions = {
|
|
|
208
228
|
* Default: No limit
|
|
209
229
|
*/
|
|
210
230
|
maxLineLength?: number;
|
|
231
|
+
/**
|
|
232
|
+
* When true, consecutive inline `<script>` elements are merged into one.
|
|
233
|
+
* Only merges compatible scripts (same `type`, matching `async`/`defer`/
|
|
234
|
+
* `nomodule`/`nonce` attributes). Does not merge external scripts (with `src`).
|
|
235
|
+
*
|
|
236
|
+
* Default: `false`
|
|
237
|
+
*/
|
|
238
|
+
mergeScripts?: boolean;
|
|
211
239
|
/**
|
|
212
240
|
* When true, enables CSS minification for inline `<style>` tags or
|
|
213
241
|
* `style` attributes. If an object is provided, it is passed to
|
|
@@ -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":"htmlparser.d.ts","sourceRoot":"","sources":["../../src/htmlparser.js"],"names":[],"mappings":"AA+CA,
|
|
1
|
+
{"version":3,"file":"htmlparser.d.ts","sourceRoot":"","sources":["../../src/htmlparser.js"],"names":[],"mappings":"AA+CA,4BAAkE;AA8ElE;IACE,qCAGC;IAFC,UAAgB;IAChB,aAAsB;IAGxB,uBA+iBC;CACF"}
|
|
@@ -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":"AA2BA,yDAEC;AAED,mEAOC;AAED,uEAWC;AAED,8DAGC;AAED,4EAOC;AAED,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"}
|
|
@@ -79,6 +79,7 @@ export const keepScriptsMimetypes: Set<string>;
|
|
|
79
79
|
export const jsonScriptTypes: Set<string>;
|
|
80
80
|
export const isSimpleBoolean: Set<string>;
|
|
81
81
|
export const isBooleanValue: Set<string>;
|
|
82
|
+
export const emptyCollapsible: Set<string>;
|
|
82
83
|
export const srcsetElements: Set<string>;
|
|
83
84
|
export const optionalStartTags: Set<string>;
|
|
84
85
|
export const optionalEndTags: 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;
|
|
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"}
|
|
@@ -7,10 +7,9 @@ export function shouldMinifyInnerHTML(options: any): boolean;
|
|
|
7
7
|
* @param {Function} deps.getSwc - Function to lazily load @swc/core
|
|
8
8
|
* @param {LRU} deps.cssMinifyCache - CSS minification cache
|
|
9
9
|
* @param {LRU} deps.jsMinifyCache - JS minification cache
|
|
10
|
-
* @param {LRU} deps.urlMinifyCache - URL minification cache
|
|
11
10
|
* @returns {MinifierOptions} Normalized options with defaults applied
|
|
12
11
|
*/
|
|
13
|
-
export function processOptions(inputOptions: Partial<MinifierOptions>, { getLightningCSS, getTerser, getSwc, cssMinifyCache, jsMinifyCache
|
|
12
|
+
export function processOptions(inputOptions: Partial<MinifierOptions>, { getLightningCSS, getTerser, getSwc, cssMinifyCache, jsMinifyCache }?: {
|
|
14
13
|
getLightningCSS: Function;
|
|
15
14
|
getTerser: Function;
|
|
16
15
|
getSwc: Function;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../../src/lib/options.js"],"names":[],"mappings":"AAYA,6DAUC;AAID
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../../src/lib/options.js"],"names":[],"mappings":"AAYA,6DAUC;AAID;;;;;;;;;GASG;AACH,6CATW,OAAO,CAAC,eAAe,CAAC,0EAEhC;IAAuB,eAAe;IACf,SAAS;IACT,MAAM;CAA2B,GAG9C,eAAe,CAuV3B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"svg.d.ts","sourceRoot":"","sources":["../../../src/lib/svg.js"],"names":[],"mappings":"
|
|
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"}
|
package/dist/types/presets.d.ts
CHANGED
|
@@ -34,6 +34,7 @@ export namespace presets {
|
|
|
34
34
|
export { continueOnParseError_1 as continueOnParseError };
|
|
35
35
|
let decodeEntities_1: boolean;
|
|
36
36
|
export { decodeEntities_1 as decodeEntities };
|
|
37
|
+
export let mergeScripts: boolean;
|
|
37
38
|
export let minifyCSS: boolean;
|
|
38
39
|
export let minifyJS: boolean;
|
|
39
40
|
export let minifySVG: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../src/presets.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../src/presets.js"],"names":[],"mappings":"AAiDA;;;;GAIG;AACH,gCAHW,MAAM,GACJ,MAAM,GAAC,IAAI,CAMvB;AAED;;;GAGG;AACH,kCAFa,MAAM,EAAE,CAIpB"}
|
package/package.json
CHANGED
package/src/htmlminifier.js
CHANGED
|
@@ -91,11 +91,129 @@ async function getSwc() {
|
|
|
91
91
|
return swcPromise;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
// Minification caches
|
|
94
|
+
// Minification caches (initialized on first use with configurable sizes)
|
|
95
|
+
let cssMinifyCache = null;
|
|
96
|
+
let jsMinifyCache = null;
|
|
95
97
|
|
|
96
|
-
|
|
97
|
-
const
|
|
98
|
-
const
|
|
98
|
+
// Pre-compiled patterns for script merging (avoid repeated allocation in hot path)
|
|
99
|
+
const RE_SCRIPT_ATTRS = /([^\s=]+)(?:=(?:"([^"]*)"|'([^']*)'|([^\s>]+)))?/g;
|
|
100
|
+
const SCRIPT_BOOL_ATTRS = new Set(['async', 'defer', 'nomodule']);
|
|
101
|
+
const DEFAULT_JS_TYPES = new Set(['', 'text/javascript', 'application/javascript']);
|
|
102
|
+
|
|
103
|
+
// Pre-compiled patterns for buffer scanning
|
|
104
|
+
const RE_START_TAG = /^<[^/!]/;
|
|
105
|
+
const RE_END_TAG = /^<\//;
|
|
106
|
+
|
|
107
|
+
// Script merging
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Merge consecutive inline script tags into one (`mergeConsecutiveScripts`).
|
|
111
|
+
* Only merges scripts that are compatible:
|
|
112
|
+
* - Both inline (no `src` attribute)
|
|
113
|
+
* - Same `type` (or both default JavaScript)
|
|
114
|
+
* - No conflicting attributes (`async`, `defer`, `nomodule`, different `nonce`)
|
|
115
|
+
*
|
|
116
|
+
* Limitation: This function uses regex-based matching (`pattern` variable below),
|
|
117
|
+
* which can produce incorrect results if a script’s content contains a literal
|
|
118
|
+
* `</script>` string (e.g., `document.write('<script>…</script>')`). In valid
|
|
119
|
+
* HTML, such strings should be escaped as `<\/script>` or split like
|
|
120
|
+
* `'</scr' + 'ipt>'`, so this limitation rarely affects real-world code. The
|
|
121
|
+
* earlier `minifyJS` step (if enabled) typically handles this escaping already.
|
|
122
|
+
*
|
|
123
|
+
* @param {string} html - The HTML string to process
|
|
124
|
+
* @returns {string} HTML with consecutive scripts merged
|
|
125
|
+
*/
|
|
126
|
+
function mergeConsecutiveScripts(html) {
|
|
127
|
+
// `pattern`: Regex to match consecutive `</script>` followed by `<script…>`.
|
|
128
|
+
// See function JSDoc above for known limitations with literal `</script>` in content.
|
|
129
|
+
// Captures:
|
|
130
|
+
// 1. first script attrs
|
|
131
|
+
// 2. first script content
|
|
132
|
+
// 3. whitespace between
|
|
133
|
+
// 4. second script attrs
|
|
134
|
+
// 5. second script content
|
|
135
|
+
const pattern = /<script([^>]*)>([\s\S]*?)<\/script>([\s]*)<script([^>]*)>([\s\S]*?)<\/script>/gi;
|
|
136
|
+
|
|
137
|
+
let result = html;
|
|
138
|
+
let changed = true;
|
|
139
|
+
|
|
140
|
+
// Keep merging until no more changes (handles chains of 3+ scripts)
|
|
141
|
+
while (changed) {
|
|
142
|
+
changed = false;
|
|
143
|
+
result = result.replace(pattern, (match, attrs1, content1, whitespace, attrs2, content2) => {
|
|
144
|
+
// Parse attributes from both script tags (uses pre-compiled RE_SCRIPT_ATTRS)
|
|
145
|
+
const parseAttrs = (attrStr) => {
|
|
146
|
+
const attrs = {};
|
|
147
|
+
RE_SCRIPT_ATTRS.lastIndex = 0; // Reset for reuse
|
|
148
|
+
let m;
|
|
149
|
+
while ((m = RE_SCRIPT_ATTRS.exec(attrStr)) !== null) {
|
|
150
|
+
const name = m[1].toLowerCase();
|
|
151
|
+
const value = m[2] ?? m[3] ?? m[4] ?? '';
|
|
152
|
+
attrs[name] = value;
|
|
153
|
+
}
|
|
154
|
+
return attrs;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const a1 = parseAttrs(attrs1);
|
|
158
|
+
const a2 = parseAttrs(attrs2);
|
|
159
|
+
|
|
160
|
+
// Check for `src`—cannot merge external scripts
|
|
161
|
+
if ('src' in a1 || 'src' in a2) {
|
|
162
|
+
return match;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Check `type` compatibility (both must be same, or both default JS)
|
|
166
|
+
const type1 = a1.type || '';
|
|
167
|
+
const type2 = a2.type || '';
|
|
168
|
+
|
|
169
|
+
if (DEFAULT_JS_TYPES.has(type1) && DEFAULT_JS_TYPES.has(type2)) {
|
|
170
|
+
// Both are default JavaScript—compatible
|
|
171
|
+
} else if (type1 === type2) {
|
|
172
|
+
// Same explicit type—compatible
|
|
173
|
+
} else {
|
|
174
|
+
// Incompatible types
|
|
175
|
+
return match;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Check for conflicting boolean attributes (uses pre-compiled SCRIPT_BOOL_ATTRS)
|
|
179
|
+
for (const attr of SCRIPT_BOOL_ATTRS) {
|
|
180
|
+
const has1 = attr in a1;
|
|
181
|
+
const has2 = attr in a2;
|
|
182
|
+
if (has1 !== has2) {
|
|
183
|
+
// One has it, one doesn't - incompatible
|
|
184
|
+
return match;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Check `nonce`—must be same or both absent
|
|
189
|
+
if (a1.nonce !== a2.nonce) {
|
|
190
|
+
return match;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Scripts are compatible—merge them
|
|
194
|
+
changed = true;
|
|
195
|
+
|
|
196
|
+
// Combine content—use semicolon normally, newline only for trailing `//` comments
|
|
197
|
+
const c1 = content1.trim();
|
|
198
|
+
const c2 = content2.trim();
|
|
199
|
+
let mergedContent;
|
|
200
|
+
if (c1 && c2) {
|
|
201
|
+
// Check if last line of c1 contains `//` (single-line comment)
|
|
202
|
+
// If so, use newline to terminate it; otherwise use semicolon (if not already present)
|
|
203
|
+
const lastLine = c1.slice(c1.lastIndexOf('\n') + 1);
|
|
204
|
+
const separator = lastLine.includes('//') ? '\n' : (c1.endsWith(';') ? '' : ';');
|
|
205
|
+
mergedContent = c1 + separator + c2;
|
|
206
|
+
} else {
|
|
207
|
+
mergedContent = c1 || c2;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Use first script’s attributes (they should be compatible)
|
|
211
|
+
return `<script${attrs1}>${mergedContent}</script>`;
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return result;
|
|
216
|
+
}
|
|
99
217
|
|
|
100
218
|
// Type definitions
|
|
101
219
|
|
|
@@ -128,6 +246,24 @@ const urlMinifyCache = new LRU(500);
|
|
|
128
246
|
*
|
|
129
247
|
* Default: Built-in `canTrimWhitespace` function
|
|
130
248
|
*
|
|
249
|
+
* @prop {number} [cacheCSS]
|
|
250
|
+
* The maximum number of entries for the CSS minification cache. Higher values
|
|
251
|
+
* improve performance for inputs with repeated CSS (e.g., batch processing).
|
|
252
|
+
* - Cache is created on first `minify()` call and persists for the process lifetime
|
|
253
|
+
* - Cache size is locked after first call—subsequent calls reuse the same cache
|
|
254
|
+
* - Explicit `0` values are coerced to `1` (minimum functional cache size)
|
|
255
|
+
*
|
|
256
|
+
* Default: `500` (or `1000` when `CI=true` environment variable is set)
|
|
257
|
+
*
|
|
258
|
+
* @prop {number} [cacheJS]
|
|
259
|
+
* The maximum number of entries for the JavaScript minification cache. Higher
|
|
260
|
+
* values improve performance for inputs with repeated JavaScript.
|
|
261
|
+
* - Cache is created on first `minify()` call and persists for the process lifetime
|
|
262
|
+
* - Cache size is locked after first call—subsequent calls reuse the same cache
|
|
263
|
+
* - Explicit `0` values are coerced to `1` (minimum functional cache size)
|
|
264
|
+
*
|
|
265
|
+
* Default: `500` (or `1000` when `CI=true` environment variable is set)
|
|
266
|
+
*
|
|
131
267
|
* @prop {boolean} [caseSensitive]
|
|
132
268
|
* When true, tag and attribute names are treated as case-sensitive.
|
|
133
269
|
* Useful for custom HTML tags.
|
|
@@ -277,6 +413,13 @@ const urlMinifyCache = new LRU(500);
|
|
|
277
413
|
*
|
|
278
414
|
* Default: No limit
|
|
279
415
|
*
|
|
416
|
+
* @prop {boolean} [mergeScripts]
|
|
417
|
+
* When true, consecutive inline `<script>` elements are merged into one.
|
|
418
|
+
* Only merges compatible scripts (same `type`, matching `async`/`defer`/
|
|
419
|
+
* `nomodule`/`nonce` attributes). Does not merge external scripts (with `src`).
|
|
420
|
+
*
|
|
421
|
+
* Default: `false`
|
|
422
|
+
*
|
|
280
423
|
* @prop {boolean | Partial<import("lightningcss").TransformOptions<import("lightningcss").CustomAtRules>> | ((text: string, type?: string) => Promise<string> | string)} [minifyCSS]
|
|
281
424
|
* When true, enables CSS minification for inline `<style>` tags or
|
|
282
425
|
* `style` attributes. If an object is provided, it is passed to
|
|
@@ -855,7 +998,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
855
998
|
|
|
856
999
|
function removeStartTag() {
|
|
857
1000
|
let index = buffer.length - 1;
|
|
858
|
-
while (index > 0 &&
|
|
1001
|
+
while (index > 0 && !RE_START_TAG.test(buffer[index])) {
|
|
859
1002
|
index--;
|
|
860
1003
|
}
|
|
861
1004
|
buffer.length = Math.max(0, index);
|
|
@@ -863,7 +1006,7 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
863
1006
|
|
|
864
1007
|
function removeEndTag() {
|
|
865
1008
|
let index = buffer.length - 1;
|
|
866
|
-
while (index > 0 &&
|
|
1009
|
+
while (index > 0 && !RE_END_TAG.test(buffer[index])) {
|
|
867
1010
|
index--;
|
|
868
1011
|
}
|
|
869
1012
|
buffer.length = Math.max(0, index);
|
|
@@ -1174,8 +1317,8 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1174
1317
|
charsPrevTag = /^\s*$/.test(text) ? prevTag : 'comment';
|
|
1175
1318
|
if (options.decodeEntities && text && !specialContentElements.has(currentTag)) {
|
|
1176
1319
|
// Escape any `&` symbols that start either:
|
|
1177
|
-
// 1
|
|
1178
|
-
// 2
|
|
1320
|
+
// 1. a legacy-named character reference (i.e., one that doesn’t end with `;`)
|
|
1321
|
+
// 2. or any other character reference (i.e., one that does end with `;`)
|
|
1179
1322
|
// Note that `&` can be escaped as `&`, without the semicolon.
|
|
1180
1323
|
// https://mathiasbynens.be/notes/ambiguous-ampersands
|
|
1181
1324
|
if (text.indexOf('&') !== -1) {
|
|
@@ -1379,6 +1522,49 @@ function joinResultSegments(results, options, restoreCustom, restoreIgnore) {
|
|
|
1379
1522
|
return options.collapseWhitespace ? collapseWhitespace(str, options, true, true) : str;
|
|
1380
1523
|
}
|
|
1381
1524
|
|
|
1525
|
+
/**
|
|
1526
|
+
* Initialize minification caches with configurable sizes.
|
|
1527
|
+
*
|
|
1528
|
+
* Important behavior notes:
|
|
1529
|
+
* - Caches are created on the first `minify()` call and persist for the lifetime of the process
|
|
1530
|
+
* - Cache sizes are locked after first initialization—subsequent calls use the same caches
|
|
1531
|
+
* even if different `cacheCSS`/`cacheJS` options are provided
|
|
1532
|
+
* - The first call’s options determine the cache sizes for subsequent calls
|
|
1533
|
+
* - Explicit `0` values are coerced to `1` (minimum functional cache size)
|
|
1534
|
+
*/
|
|
1535
|
+
function initCaches(options) {
|
|
1536
|
+
// Only create caches once (on first call)—sizes are locked after this
|
|
1537
|
+
if (!cssMinifyCache) {
|
|
1538
|
+
// Determine default size based on environment
|
|
1539
|
+
const defaultSize = process.env.CI === 'true' ? 1000 : 500;
|
|
1540
|
+
|
|
1541
|
+
// Helper to parse env var—returns parsed number (including 0) or undefined if absent, invalid, or negative
|
|
1542
|
+
const parseEnvCacheSize = (envVar) => {
|
|
1543
|
+
if (envVar === undefined) return undefined;
|
|
1544
|
+
const parsed = Number(envVar);
|
|
1545
|
+
if (Number.isNaN(parsed) || !Number.isFinite(parsed) || parsed < 0) {
|
|
1546
|
+
return undefined;
|
|
1547
|
+
}
|
|
1548
|
+
return parsed;
|
|
1549
|
+
};
|
|
1550
|
+
|
|
1551
|
+
// Get cache sizes with precedence: Options > env > default
|
|
1552
|
+
const cssSize = options.cacheCSS !== undefined ? options.cacheCSS
|
|
1553
|
+
: (parseEnvCacheSize(process.env.HMN_CACHE_CSS) ?? defaultSize);
|
|
1554
|
+
const jsSize = options.cacheJS !== undefined ? options.cacheJS
|
|
1555
|
+
: (parseEnvCacheSize(process.env.HMN_CACHE_JS) ?? defaultSize);
|
|
1556
|
+
|
|
1557
|
+
// Coerce `0` to `1` (minimum functional cache size) to avoid immediate eviction
|
|
1558
|
+
const cssFinalSize = cssSize === 0 ? 1 : cssSize;
|
|
1559
|
+
const jsFinalSize = jsSize === 0 ? 1 : jsSize;
|
|
1560
|
+
|
|
1561
|
+
cssMinifyCache = new LRU(cssFinalSize);
|
|
1562
|
+
jsMinifyCache = new LRU(jsFinalSize);
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
return { cssMinifyCache, jsMinifyCache };
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1382
1568
|
/**
|
|
1383
1569
|
* @param {string} value
|
|
1384
1570
|
* @param {MinifierOptions} [options]
|
|
@@ -1386,15 +1572,23 @@ function joinResultSegments(results, options, restoreCustom, restoreIgnore) {
|
|
|
1386
1572
|
*/
|
|
1387
1573
|
export const minify = async function (value, options) {
|
|
1388
1574
|
const start = Date.now();
|
|
1575
|
+
|
|
1576
|
+
// Initialize caches on first use with configurable sizes
|
|
1577
|
+
const caches = initCaches(options || {});
|
|
1578
|
+
|
|
1389
1579
|
options = processOptions(options || {}, {
|
|
1390
1580
|
getLightningCSS,
|
|
1391
1581
|
getTerser,
|
|
1392
1582
|
getSwc,
|
|
1393
|
-
|
|
1394
|
-
jsMinifyCache,
|
|
1395
|
-
urlMinifyCache
|
|
1583
|
+
...caches
|
|
1396
1584
|
});
|
|
1397
|
-
|
|
1585
|
+
let result = await minifyHTML(value, options);
|
|
1586
|
+
|
|
1587
|
+
// Post-processing: Merge consecutive inline scripts if enabled
|
|
1588
|
+
if (options.mergeScripts) {
|
|
1589
|
+
result = mergeConsecutiveScripts(result);
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1398
1592
|
options.log('minified in: ' + (Date.now() - start) + 'ms');
|
|
1399
1593
|
return result;
|
|
1400
1594
|
};
|