html-minifier-next 4.12.2 → 4.14.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 (37) hide show
  1. package/README.md +84 -26
  2. package/cli.js +1 -1
  3. package/dist/htmlminifier.cjs +1552 -1320
  4. package/dist/htmlminifier.esm.bundle.js +4204 -3972
  5. package/dist/types/htmlminifier.d.ts +10 -3
  6. package/dist/types/htmlminifier.d.ts.map +1 -1
  7. package/dist/types/htmlparser.d.ts.map +1 -1
  8. package/dist/types/lib/attributes.d.ts +29 -0
  9. package/dist/types/lib/attributes.d.ts.map +1 -0
  10. package/dist/types/lib/constants.d.ts +83 -0
  11. package/dist/types/lib/constants.d.ts.map +1 -0
  12. package/dist/types/lib/content.d.ts +7 -0
  13. package/dist/types/lib/content.d.ts.map +1 -0
  14. package/dist/types/lib/elements.d.ts +39 -0
  15. package/dist/types/lib/elements.d.ts.map +1 -0
  16. package/dist/types/lib/options.d.ts +17 -0
  17. package/dist/types/lib/options.d.ts.map +1 -0
  18. package/dist/types/lib/utils.d.ts +21 -0
  19. package/dist/types/lib/utils.d.ts.map +1 -0
  20. package/dist/types/lib/whitespace.d.ts +7 -0
  21. package/dist/types/lib/whitespace.d.ts.map +1 -0
  22. package/dist/types/presets.d.ts.map +1 -1
  23. package/package.json +10 -1
  24. package/src/htmlminifier.js +114 -1229
  25. package/src/htmlparser.js +11 -11
  26. package/src/lib/attributes.js +511 -0
  27. package/src/lib/constants.js +213 -0
  28. package/src/lib/content.js +105 -0
  29. package/src/lib/elements.js +242 -0
  30. package/src/lib/index.js +20 -0
  31. package/src/lib/options.js +300 -0
  32. package/src/lib/utils.js +90 -0
  33. package/src/lib/whitespace.js +139 -0
  34. package/src/presets.js +0 -1
  35. package/src/tokenchain.js +1 -1
  36. package/dist/types/utils.d.ts +0 -2
  37. package/dist/types/utils.d.ts.map +0 -1
@@ -0,0 +1,300 @@
1
+ // Imports
2
+
3
+ import RelateURL from 'relateurl';
4
+ import { stableStringify, identity, identityAsync, replaceAsync } from './utils.js';
5
+ import { RE_TRAILING_SEMICOLON } from './constants.js';
6
+ import { canCollapseWhitespace, canTrimWhitespace } from './whitespace.js';
7
+ import { wrapCSS, unwrapCSS } from './content.js';
8
+
9
+ // Helper functions
10
+
11
+ function shouldMinifyInnerHTML(options) {
12
+ return Boolean(
13
+ options.collapseWhitespace ||
14
+ options.removeComments ||
15
+ options.removeOptionalTags ||
16
+ options.minifyJS !== identity ||
17
+ options.minifyCSS !== identityAsync ||
18
+ options.minifyURLs !== identity
19
+ );
20
+ }
21
+
22
+ // Main options processor
23
+
24
+ /**
25
+ * @param {Partial<MinifierOptions>} inputOptions - User-provided options
26
+ * @param {Object} deps - Dependencies from htmlminifier.js
27
+ * @param {Function} deps.getLightningCSS - Function to lazily load lightningcss
28
+ * @param {Function} deps.getTerser - Function to lazily load terser
29
+ * @param {Function} deps.getSwc - Function to lazily load @swc/core
30
+ * @param {LRU} deps.cssMinifyCache - CSS minification cache
31
+ * @param {LRU} deps.jsMinifyCache - JS minification cache
32
+ * @returns {MinifierOptions} Normalized options with defaults applied
33
+ */
34
+ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, cssMinifyCache, jsMinifyCache } = {}) => {
35
+ const options = {
36
+ name: function (name) {
37
+ return name.toLowerCase();
38
+ },
39
+ canCollapseWhitespace,
40
+ canTrimWhitespace,
41
+ continueOnMinifyError: true,
42
+ html5: true,
43
+ ignoreCustomComments: [
44
+ /^!/,
45
+ /^\s*#/
46
+ ],
47
+ ignoreCustomFragments: [
48
+ /<%[\s\S]*?%>/,
49
+ /<\?[\s\S]*?\?>/
50
+ ],
51
+ includeAutoGeneratedTags: true,
52
+ log: identity,
53
+ minifyCSS: identityAsync,
54
+ minifyJS: identity,
55
+ minifyURLs: identity
56
+ };
57
+
58
+ Object.keys(inputOptions).forEach(function (key) {
59
+ const option = inputOptions[key];
60
+
61
+ if (key === 'caseSensitive') {
62
+ if (option) {
63
+ options.name = identity;
64
+ }
65
+ } else if (key === 'log') {
66
+ if (typeof option === 'function') {
67
+ options.log = option;
68
+ }
69
+ } else if (key === 'minifyCSS' && typeof option !== 'function') {
70
+ if (!option) {
71
+ return;
72
+ }
73
+
74
+ const lightningCssOptions = typeof option === 'object' ? option : {};
75
+
76
+ options.minifyCSS = async function (text, type) {
77
+ // Fast path: Nothing to minify
78
+ if (!text || !text.trim()) {
79
+ return text;
80
+ }
81
+ text = await replaceAsync(
82
+ text,
83
+ /(url\s*\(\s*)(?:"([^"]*)"|'([^']*)'|([^\s)]+))(\s*\))/ig,
84
+ async function (match, prefix, dq, sq, unq, suffix) {
85
+ const quote = dq != null ? '"' : (sq != null ? "'" : '');
86
+ const url = dq ?? sq ?? unq ?? '';
87
+ try {
88
+ const out = await options.minifyURLs(url);
89
+ return prefix + quote + (typeof out === 'string' ? out : url) + quote + suffix;
90
+ } catch (err) {
91
+ if (!options.continueOnMinifyError) {
92
+ throw err;
93
+ }
94
+ options.log && options.log(err);
95
+ return match;
96
+ }
97
+ }
98
+ );
99
+ // Cache key: Wrapped content, type, options signature
100
+ const inputCSS = wrapCSS(text, type);
101
+ const cssSig = stableStringify({ type, opts: lightningCssOptions, cont: !!options.continueOnMinifyError });
102
+ // For large inputs, use length and content fingerprint (first/last 50 chars) to prevent collisions
103
+ const cssKey = inputCSS.length > 2048
104
+ ? (inputCSS.length + '|' + inputCSS.slice(0, 50) + inputCSS.slice(-50) + '|' + type + '|' + cssSig)
105
+ : (inputCSS + '|' + type + '|' + cssSig);
106
+
107
+ try {
108
+ const cached = cssMinifyCache.get(cssKey);
109
+ if (cached) {
110
+ return cached;
111
+ }
112
+
113
+ const transformCSS = await getLightningCSS();
114
+ const result = transformCSS({
115
+ filename: 'input.css',
116
+ code: Buffer.from(inputCSS),
117
+ minify: true,
118
+ errorRecovery: !!options.continueOnMinifyError,
119
+ ...lightningCssOptions
120
+ });
121
+
122
+ const outputCSS = unwrapCSS(result.code.toString(), type);
123
+
124
+ // If Lightning CSS removed significant content that looks like template syntax or UIDs, return original
125
+ // This preserves:
126
+ // 1. Template code like `<?php ?>`, `<%= %>`, `{{ }}`, etc. (contain `<` or `>` but not `CDATA`)
127
+ // 2. UIDs representing custom fragments (only lowercase letters and digits, no spaces)
128
+ // CDATA sections, HTML entities, and other invalid CSS are allowed to be removed
129
+ const isCDATA = text.includes('<![CDATA[');
130
+ const uidPattern = /[a-z0-9]{10,}/; // UIDs are long alphanumeric strings
131
+ const hasUID = uidPattern.test(text) && !isCDATA; // Exclude CDATA from UID detection
132
+ const looksLikeTemplate = (text.includes('<') || text.includes('>')) && !isCDATA;
133
+
134
+ // Preserve if output is empty and input had template syntax or UIDs
135
+ // This catches cases where Lightning CSS removed content that should be preserved
136
+ const finalOutput = (text.trim() && !outputCSS.trim() && (looksLikeTemplate || hasUID)) ? text : outputCSS;
137
+
138
+ cssMinifyCache.set(cssKey, finalOutput);
139
+ return finalOutput;
140
+ } catch (err) {
141
+ cssMinifyCache.delete(cssKey);
142
+ if (!options.continueOnMinifyError) {
143
+ throw err;
144
+ }
145
+ options.log && options.log(err);
146
+ return text;
147
+ }
148
+ };
149
+ } else if (key === 'minifyJS' && typeof option !== 'function') {
150
+ if (!option) {
151
+ return;
152
+ }
153
+
154
+ // Parse configuration
155
+ const config = typeof option === 'object' ? option : {};
156
+ const engine = (config.engine || 'terser').toLowerCase();
157
+
158
+ // Validate engine
159
+ const supportedEngines = ['terser', 'swc'];
160
+ if (!supportedEngines.includes(engine)) {
161
+ throw new Error(`Unsupported JS minifier engine: "${engine}". Supported engines: ${supportedEngines.join(', ')}`);
162
+ }
163
+
164
+ // Extract engine-specific options (excluding `engine` field itself)
165
+ const engineOptions = { ...config };
166
+ delete engineOptions.engine;
167
+
168
+ // Terser options (needed for inline JS and when engine is `terser`)
169
+ const terserOptions = engine === 'terser' ? engineOptions : {};
170
+ terserOptions.parse = {
171
+ ...terserOptions.parse,
172
+ bare_returns: false
173
+ };
174
+
175
+ // SWC options (when engine is `swc`)
176
+ const swcOptions = engine === 'swc' ? engineOptions : {};
177
+
178
+ // Pre-compute option signatures once for performance (avoid repeated stringification)
179
+ const terserSig = stableStringify({
180
+ ...terserOptions,
181
+ cont: !!options.continueOnMinifyError
182
+ });
183
+ const swcSig = stableStringify({
184
+ ...swcOptions,
185
+ cont: !!options.continueOnMinifyError
186
+ });
187
+
188
+ options.minifyJS = async function (text, inline) {
189
+ const start = text.match(/^\s*<!--.*/);
190
+ const code = start ? text.slice(start[0].length).replace(/\n\s*-->\s*$/, '') : text;
191
+
192
+ // Fast path: Avoid invoking minifier for empty/whitespace-only content
193
+ if (!code || !code.trim()) {
194
+ return '';
195
+ }
196
+
197
+ // Hybrid strategy: Always use Terser for inline JS (needs bare returns support)
198
+ // Use user’s chosen engine for script blocks
199
+ const useEngine = inline ? 'terser' : engine;
200
+
201
+ let jsKey;
202
+ try {
203
+ // Select pre-computed signature based on engine
204
+ const optsSig = useEngine === 'terser' ? terserSig : swcSig;
205
+
206
+ // For large inputs, use length and content fingerprint to prevent collisions
207
+ jsKey = (code.length > 2048 ? (code.length + '|' + code.slice(0, 50) + code.slice(-50) + '|') : (code + '|'))
208
+ + (inline ? '1' : '0') + '|' + useEngine + '|' + optsSig;
209
+
210
+ const cached = jsMinifyCache.get(jsKey);
211
+ if (cached) {
212
+ return await cached;
213
+ }
214
+
215
+ const inFlight = (async () => {
216
+ // Dispatch to appropriate minifier
217
+ if (useEngine === 'terser') {
218
+ // Create a copy to avoid mutating shared `terserOptions` (race condition)
219
+ const terserCallOptions = {
220
+ ...terserOptions,
221
+ parse: {
222
+ ...terserOptions.parse,
223
+ bare_returns: inline
224
+ }
225
+ };
226
+ const terser = await getTerser();
227
+ const result = await terser(code, terserCallOptions);
228
+ return result.code.replace(RE_TRAILING_SEMICOLON, '');
229
+ } else if (useEngine === 'swc') {
230
+ const swc = await getSwc();
231
+ // `swc.minify()` takes compress and mangle directly as options
232
+ const result = await swc.minify(code, {
233
+ compress: true,
234
+ mangle: true,
235
+ ...swcOptions, // User options override defaults
236
+ });
237
+ return result.code.replace(RE_TRAILING_SEMICOLON, '');
238
+ }
239
+ throw new Error(`Unknown JS minifier engine: ${useEngine}`);
240
+ })();
241
+
242
+ jsMinifyCache.set(jsKey, inFlight);
243
+ const resolved = await inFlight;
244
+ jsMinifyCache.set(jsKey, resolved);
245
+ return resolved;
246
+ } catch (err) {
247
+ if (jsKey) jsMinifyCache.delete(jsKey);
248
+ if (!options.continueOnMinifyError) {
249
+ throw err;
250
+ }
251
+ options.log && options.log(err);
252
+ return text;
253
+ }
254
+ };
255
+ } else if (key === 'minifyURLs' && typeof option !== 'function') {
256
+ if (!option) {
257
+ return;
258
+ }
259
+
260
+ let relateUrlOptions = option;
261
+
262
+ if (typeof option === 'string') {
263
+ relateUrlOptions = { site: option };
264
+ } else if (typeof option !== 'object') {
265
+ relateUrlOptions = {};
266
+ }
267
+
268
+ // Cache RelateURL instance for reuse (expensive to create)
269
+ const relateUrlInstance = new RelateURL(relateUrlOptions.site || '', relateUrlOptions);
270
+
271
+ options.minifyURLs = function (text) {
272
+ // Fast-path: Skip if text doesn’t look like a URL that needs processing
273
+ // Only process if contains URL-like characters (`/`, `:`, `#`, `?`) or spaces that need encoding
274
+ if (!/[/:?#\s]/.test(text)) {
275
+ return text;
276
+ }
277
+
278
+ try {
279
+ return relateUrlInstance.relate(text);
280
+ } catch (err) {
281
+ if (!options.continueOnMinifyError) {
282
+ throw err;
283
+ }
284
+ options.log && options.log(err);
285
+ return text;
286
+ }
287
+ };
288
+ } else {
289
+ options[key] = option;
290
+ }
291
+ });
292
+ return options;
293
+ };
294
+
295
+ // Exports
296
+
297
+ export {
298
+ shouldMinifyInnerHTML,
299
+ processOptions
300
+ };
@@ -0,0 +1,90 @@
1
+ // Stringify for options signatures (sorted keys, shallow, nested objects)
2
+
3
+ function stableStringify(obj) {
4
+ if (obj == null || typeof obj !== 'object') return JSON.stringify(obj);
5
+ if (Array.isArray(obj)) return '[' + obj.map(stableStringify).join(',') + ']';
6
+ const keys = Object.keys(obj).sort();
7
+ let out = '{';
8
+ for (let i = 0; i < keys.length; i++) {
9
+ const k = keys[i];
10
+ out += JSON.stringify(k) + ':' + stableStringify(obj[k]) + (i < keys.length - 1 ? ',' : '');
11
+ }
12
+ return out + '}';
13
+ }
14
+
15
+ // LRU cache for strings and promises
16
+
17
+ class LRU {
18
+ constructor(limit = 200) {
19
+ this.limit = limit;
20
+ this.map = new Map();
21
+ }
22
+ get(key) {
23
+ if (this.map.has(key)) {
24
+ const v = this.map.get(key);
25
+ this.map.delete(key);
26
+ this.map.set(key, v);
27
+ return v;
28
+ }
29
+ return undefined;
30
+ }
31
+ set(key, value) {
32
+ if (this.map.has(key)) this.map.delete(key);
33
+ this.map.set(key, value);
34
+ if (this.map.size > this.limit) {
35
+ const first = this.map.keys().next().value;
36
+ this.map.delete(first);
37
+ }
38
+ }
39
+ delete(key) { this.map.delete(key); }
40
+ }
41
+
42
+ // Unique ID generator
43
+
44
+ function uniqueId(value) {
45
+ let id;
46
+ do {
47
+ id = Math.random().toString(36).replace(/^0\.[0-9]*/, '');
48
+ } while (~value.indexOf(id));
49
+ return id;
50
+ }
51
+
52
+ // Identity functions
53
+
54
+ function identity(value) {
55
+ return value;
56
+ }
57
+
58
+ function identityAsync(value) {
59
+ return Promise.resolve(value);
60
+ }
61
+
62
+ // Replace async helper
63
+
64
+ /**
65
+ * Asynchronously replace matches in a string
66
+ * @param {string} str - Input string
67
+ * @param {RegExp} regex - Regular expression with global flag
68
+ * @param {Function} asyncFn - Async function to process each match
69
+ * @returns {Promise<string>} Processed string
70
+ */
71
+ async function replaceAsync(str, regex, asyncFn) {
72
+ const promises = [];
73
+
74
+ str.replace(regex, (match, ...args) => {
75
+ const promise = asyncFn(match, ...args);
76
+ promises.push(promise);
77
+ });
78
+
79
+ const data = await Promise.all(promises);
80
+ return str.replace(regex, () => data.shift());
81
+ }
82
+
83
+ // Exports
84
+
85
+ export { stableStringify };
86
+ export { LRU };
87
+ export { uniqueId };
88
+ export { identity };
89
+ export { identityAsync };
90
+ export { replaceAsync };
@@ -0,0 +1,139 @@
1
+ // Imports
2
+
3
+ import {
4
+ RE_WS_START,
5
+ RE_WS_END,
6
+ RE_ALL_WS_NBSP,
7
+ RE_NBSP_LEADING_GROUP,
8
+ RE_NBSP_LEAD_GROUP,
9
+ RE_NBSP_TRAILING_GROUP,
10
+ RE_NBSP_TRAILING_STRIP,
11
+ inlineElementsToKeepWhitespace
12
+ } from './constants.js';
13
+
14
+ // Trim whitespace
15
+
16
+ const trimWhitespace = str => {
17
+ if (!str) return str;
18
+ // Fast path: If no whitespace at start or end, return early
19
+ if (!/^[ \n\r\t\f]/.test(str) && !/[ \n\r\t\f]$/.test(str)) {
20
+ return str;
21
+ }
22
+ return str.replace(RE_WS_START, '').replace(RE_WS_END, '');
23
+ };
24
+
25
+ // Collapse all whitespace
26
+
27
+ function collapseWhitespaceAll(str) {
28
+ if (!str) return str;
29
+ // Fast path: If there are no common whitespace characters, return early
30
+ if (!/[ \n\r\t\f\xA0]/.test(str)) {
31
+ return str;
32
+ }
33
+ // No-break space is specifically handled inside the replacer function here:
34
+ return str.replace(RE_ALL_WS_NBSP, function (spaces) {
35
+ // Preserve standalone tabs
36
+ if (spaces === '\t') return '\t';
37
+ // Fast path: No no-break space, common case—just collapse to single space
38
+ // This avoids the nested regex for the majority of cases
39
+ if (spaces.indexOf('\xA0') === -1) return ' ';
40
+ // For no-break space handling, use the original regex approach
41
+ return spaces.replace(RE_NBSP_LEADING_GROUP, '$1 ');
42
+ });
43
+ }
44
+
45
+ // Collapse whitespace with options
46
+
47
+ function collapseWhitespace(str, options, trimLeft, trimRight, collapseAll) {
48
+ let lineBreakBefore = ''; let lineBreakAfter = '';
49
+
50
+ if (!str) return str;
51
+
52
+ // Fast path: Nothing to do
53
+ if (!trimLeft && !trimRight && !collapseAll && !options.preserveLineBreaks) {
54
+ return str;
55
+ }
56
+
57
+ // Fast path: No whitespace at all
58
+ if (!/[ \n\r\t\f\xA0]/.test(str)) {
59
+ return str;
60
+ }
61
+
62
+ if (options.preserveLineBreaks) {
63
+ str = str.replace(/^[ \n\r\t\f]*?[\n\r][ \n\r\t\f]*/, function () {
64
+ lineBreakBefore = '\n';
65
+ return '';
66
+ }).replace(/[ \n\r\t\f]*?[\n\r][ \n\r\t\f]*$/, function () {
67
+ lineBreakAfter = '\n';
68
+ return '';
69
+ });
70
+ }
71
+
72
+ if (trimLeft) {
73
+ // Non-breaking space is specifically handled inside the replacer function
74
+ str = str.replace(/^[ \n\r\t\f\xA0]+/, function (spaces) {
75
+ const conservative = !lineBreakBefore && options.conservativeCollapse;
76
+ if (conservative && spaces === '\t') {
77
+ return '\t';
78
+ }
79
+ return spaces.replace(/^[^\xA0]+/, '').replace(RE_NBSP_LEAD_GROUP, '$1 ') || (conservative ? ' ' : '');
80
+ });
81
+ }
82
+
83
+ if (trimRight) {
84
+ // Non-breaking space is specifically handled inside the replacer function
85
+ str = str.replace(/[ \n\r\t\f\xA0]+$/, function (spaces) {
86
+ const conservative = !lineBreakAfter && options.conservativeCollapse;
87
+ if (conservative && spaces === '\t') {
88
+ return '\t';
89
+ }
90
+ return spaces.replace(RE_NBSP_TRAILING_GROUP, ' $1').replace(RE_NBSP_TRAILING_STRIP, '') || (conservative ? ' ' : '');
91
+ });
92
+ }
93
+
94
+ if (collapseAll) {
95
+ // Strip non-space whitespace then compress spaces to one
96
+ str = collapseWhitespaceAll(str);
97
+ }
98
+
99
+ // Avoid string concatenation when no line breaks (common case)
100
+ if (!lineBreakBefore && !lineBreakAfter) return str;
101
+ if (!lineBreakBefore) return str + lineBreakAfter;
102
+ if (!lineBreakAfter) return lineBreakBefore + str;
103
+ return lineBreakBefore + str + lineBreakAfter;
104
+ }
105
+
106
+ // Collapse whitespace smartly based on surrounding tags
107
+
108
+ function collapseWhitespaceSmart(str, prevTag, nextTag, options, inlineElements, inlineTextSet) {
109
+ let trimLeft = prevTag && !inlineElementsToKeepWhitespace.has(prevTag);
110
+ if (trimLeft && !options.collapseInlineTagWhitespace) {
111
+ trimLeft = prevTag.charAt(0) === '/' ? !inlineElements.has(prevTag.slice(1)) : !inlineTextSet.has(prevTag);
112
+ }
113
+ let trimRight = nextTag && !inlineElementsToKeepWhitespace.has(nextTag);
114
+ if (trimRight && !options.collapseInlineTagWhitespace) {
115
+ trimRight = nextTag.charAt(0) === '/' ? !inlineTextSet.has(nextTag.slice(1)) : !inlineElements.has(nextTag);
116
+ }
117
+ return collapseWhitespace(str, options, trimLeft, trimRight, prevTag && nextTag);
118
+ }
119
+
120
+ // Collapse/trim whitespace for given tag
121
+
122
+ function canCollapseWhitespace(tag) {
123
+ return !/^(?:script|style|pre|textarea)$/.test(tag);
124
+ }
125
+
126
+ function canTrimWhitespace(tag) {
127
+ return !/^(?:pre|textarea)$/.test(tag);
128
+ }
129
+
130
+ // Exports
131
+
132
+ export {
133
+ trimWhitespace,
134
+ collapseWhitespaceAll,
135
+ collapseWhitespace,
136
+ collapseWhitespaceSmart,
137
+ canCollapseWhitespace,
138
+ canTrimWhitespace
139
+ };
package/src/presets.js CHANGED
@@ -22,7 +22,6 @@ export const presets = {
22
22
  useShortDoctype: true
23
23
  },
24
24
  comprehensive: {
25
- // @@ Add `collapseAttributeWhitespace: true` (also add to preset in demo)
26
25
  caseSensitive: true,
27
26
  collapseBooleanAttributes: true,
28
27
  collapseInlineTagWhitespace: true,
package/src/tokenchain.js CHANGED
@@ -60,7 +60,7 @@ class TokenChain {
60
60
  const sorter = new Sorter();
61
61
  sorter.sorterMap = new Map();
62
62
 
63
- // Convert Map entries to array and sort
63
+ // Convert Map entries to array and sort by frequency (descending) then alphabetically
64
64
  const entries = Array.from(this.map.entries()).sort((a, b) => {
65
65
  const m = a[1].arrays.length;
66
66
  const n = b[1].arrays.length;
@@ -1,2 +0,0 @@
1
- export function replaceAsync(str: any, regex: any, asyncFn: any): Promise<any>;
2
- //# sourceMappingURL=utils.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.js"],"names":[],"mappings":"AAAA,+EAUC"}