html-minifier-next 4.16.4 → 4.17.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 +57 -70
- package/cli.js +27 -25
- package/dist/htmlminifier.cjs +264 -138
- package/dist/htmlminifier.esm.bundle.js +264 -138
- 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 +1 -1
- package/dist/types/lib/attributes.d.ts.map +1 -1
- package/dist/types/lib/constants.d.ts +16 -15
- package/dist/types/lib/constants.d.ts.map +1 -1
- package/dist/types/lib/content.d.ts.map +1 -1
- package/dist/types/lib/options.d.ts +2 -2
- package/dist/types/lib/whitespace.d.ts +1 -1
- package/dist/types/lib/whitespace.d.ts.map +1 -1
- package/dist/types/presets.d.ts +1 -1
- package/package.json +8 -7
- package/src/htmlminifier.js +49 -47
- package/src/htmlparser.js +44 -13
- package/src/lib/attributes.js +72 -30
- package/src/lib/constants.js +46 -39
- package/src/lib/content.js +0 -1
- package/src/lib/elements.js +15 -15
- package/src/lib/options.js +9 -9
- package/src/lib/svg.js +14 -14
- package/src/lib/whitespace.js +53 -4
- package/src/presets.js +4 -4
- package/src/tokenchain.js +2 -2
- package/src/lib/index.js +0 -20
package/src/lib/elements.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
// Imports
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
headerElements,
|
|
5
|
+
descriptionElements,
|
|
6
|
+
pBlockElements,
|
|
7
7
|
rubyEndTagOmission,
|
|
8
8
|
rubyRtcEndTagOmission,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
optionElements,
|
|
10
|
+
tableContentElements,
|
|
11
|
+
tableSectionElements,
|
|
12
|
+
cellElements
|
|
13
13
|
} from './constants.js';
|
|
14
14
|
import { hasAttrName } from './attributes.js';
|
|
15
15
|
|
|
@@ -21,7 +21,7 @@ function canRemoveParentTag(optionalStartTag, tag) {
|
|
|
21
21
|
case 'head':
|
|
22
22
|
return true;
|
|
23
23
|
case 'body':
|
|
24
|
-
return !
|
|
24
|
+
return !headerElements.has(tag);
|
|
25
25
|
case 'colgroup':
|
|
26
26
|
return tag === 'col';
|
|
27
27
|
case 'tbody':
|
|
@@ -35,7 +35,7 @@ function isStartTagMandatory(optionalEndTag, tag) {
|
|
|
35
35
|
case 'colgroup':
|
|
36
36
|
return optionalEndTag === 'colgroup';
|
|
37
37
|
case 'tbody':
|
|
38
|
-
return
|
|
38
|
+
return tableSectionElements.has(optionalEndTag);
|
|
39
39
|
}
|
|
40
40
|
return false;
|
|
41
41
|
}
|
|
@@ -54,9 +54,9 @@ function canRemovePrecedingTag(optionalEndTag, tag) {
|
|
|
54
54
|
return tag === optionalEndTag;
|
|
55
55
|
case 'dt':
|
|
56
56
|
case 'dd':
|
|
57
|
-
return
|
|
57
|
+
return descriptionElements.has(tag);
|
|
58
58
|
case 'p':
|
|
59
|
-
return
|
|
59
|
+
return pBlockElements.has(tag);
|
|
60
60
|
case 'rb':
|
|
61
61
|
case 'rt':
|
|
62
62
|
case 'rp':
|
|
@@ -64,15 +64,15 @@ function canRemovePrecedingTag(optionalEndTag, tag) {
|
|
|
64
64
|
case 'rtc':
|
|
65
65
|
return rubyRtcEndTagOmission.has(tag);
|
|
66
66
|
case 'option':
|
|
67
|
-
return
|
|
67
|
+
return optionElements.has(tag);
|
|
68
68
|
case 'thead':
|
|
69
69
|
case 'tbody':
|
|
70
|
-
return
|
|
70
|
+
return tableContentElements.has(tag);
|
|
71
71
|
case 'tfoot':
|
|
72
72
|
return tag === 'tbody';
|
|
73
73
|
case 'td':
|
|
74
74
|
case 'th':
|
|
75
|
-
return
|
|
75
|
+
return cellElements.has(tag);
|
|
76
76
|
}
|
|
77
77
|
return false;
|
|
78
78
|
}
|
|
@@ -175,7 +175,7 @@ function parseRemoveEmptyElementsExcept(input, options) {
|
|
|
175
175
|
if (typeof item === 'string') {
|
|
176
176
|
const spec = parseElementSpec(item, options);
|
|
177
177
|
if (!spec && options.log) {
|
|
178
|
-
options.log('Warning: Unable to parse “removeEmptyElementsExcept” specification:
|
|
178
|
+
options.log('Warning: Unable to parse “removeEmptyElementsExcept” specification: “' + item + '”');
|
|
179
179
|
}
|
|
180
180
|
return spec;
|
|
181
181
|
}
|
package/src/lib/options.js
CHANGED
|
@@ -26,8 +26,8 @@ function shouldMinifyInnerHTML(options) {
|
|
|
26
26
|
/**
|
|
27
27
|
* @param {Partial<MinifierOptions>} inputOptions - User-provided options
|
|
28
28
|
* @param {Object} deps - Dependencies from htmlminifier.js
|
|
29
|
-
* @param {Function} deps.getLightningCSS - Function to lazily load
|
|
30
|
-
* @param {Function} deps.getTerser - Function to lazily load
|
|
29
|
+
* @param {Function} deps.getLightningCSS - Function to lazily load Lightning CSS
|
|
30
|
+
* @param {Function} deps.getTerser - Function to lazily load Terser
|
|
31
31
|
* @param {Function} deps.getSwc - Function to lazily load @swc/core
|
|
32
32
|
* @param {LRU} deps.cssMinifyCache - CSS minification cache
|
|
33
33
|
* @param {LRU} deps.jsMinifyCache - JS minification cache
|
|
@@ -64,7 +64,7 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, cssM
|
|
|
64
64
|
if (typeof value === 'string') {
|
|
65
65
|
return new RegExp(value.replace(/^\/(.*)\/$/, '$1'));
|
|
66
66
|
}
|
|
67
|
-
return value; // Already a RegExp or
|
|
67
|
+
return value; // Already a RegExp or another type
|
|
68
68
|
};
|
|
69
69
|
|
|
70
70
|
const parseRegExpArray = (arr) => {
|
|
@@ -201,7 +201,7 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, cssM
|
|
|
201
201
|
// Validate engine
|
|
202
202
|
const supportedEngines = ['terser', 'swc'];
|
|
203
203
|
if (!supportedEngines.includes(engine)) {
|
|
204
|
-
throw new Error(`Unsupported JS minifier engine:
|
|
204
|
+
throw new Error(`Unsupported JS minifier engine: “${engine}”. Supported engines: ${supportedEngines.join(', ')}`);
|
|
205
205
|
}
|
|
206
206
|
|
|
207
207
|
// Extract engine-specific options (excluding `engine` field itself)
|
|
@@ -308,14 +308,14 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, cssM
|
|
|
308
308
|
relateUrlOptions = {};
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
-
// Cache
|
|
311
|
+
// Cache relateurl instance for reuse (expensive to create)
|
|
312
312
|
const relateUrlInstance = new RelateURL(relateUrlOptions.site || '', relateUrlOptions);
|
|
313
313
|
|
|
314
314
|
// Create instance-specific cache (results depend on site configuration)
|
|
315
315
|
const instanceCache = urlMinifyCache ? new (urlMinifyCache.constructor)(500) : null;
|
|
316
316
|
|
|
317
317
|
options.minifyURLs = function (text) {
|
|
318
|
-
// Fast-path: Skip if text doesn
|
|
318
|
+
// Fast-path: Skip if text doesn’t look like a URL that needs processing
|
|
319
319
|
// Only process if contains URL-like characters (`/`, `:`, `#`, `?`) or spaces that need encoding
|
|
320
320
|
if (!/[/:?#\s]/.test(text)) {
|
|
321
321
|
return text;
|
|
@@ -347,17 +347,17 @@ const processOptions = (inputOptions, { getLightningCSS, getTerser, getSwc, cssM
|
|
|
347
347
|
};
|
|
348
348
|
} else if (key === 'minifySVG') {
|
|
349
349
|
// Process SVG minification options
|
|
350
|
-
// Unlike minifyCSS
|
|
350
|
+
// Unlike `minifyCSS`/`minifyJS`, this is a simple options object, not a function
|
|
351
351
|
// The actual minification is applied inline during attribute processing
|
|
352
352
|
options.minifySVG = getSVGMinifierOptions(option);
|
|
353
353
|
} else if (key === 'customAttrCollapse') {
|
|
354
|
-
// Single
|
|
354
|
+
// Single regex pattern
|
|
355
355
|
options[key] = parseRegExp(option);
|
|
356
356
|
} else if (key === 'customAttrSurround') {
|
|
357
357
|
// Nested array of RegExp pairs: `[[openRegExp, closeRegExp], …]`
|
|
358
358
|
options[key] = parseNestedRegExpArray(option);
|
|
359
359
|
} else if (['customAttrAssign', 'customEventAttributes', 'ignoreCustomComments', 'ignoreCustomFragments'].includes(key)) {
|
|
360
|
-
// Array of
|
|
360
|
+
// Array of regex patterns
|
|
361
361
|
options[key] = parseRegExpArray(option);
|
|
362
362
|
} else {
|
|
363
363
|
options[key] = option;
|
package/src/lib/svg.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Lightweight SVG optimizations:
|
|
3
|
-
*
|
|
4
3
|
* - Numeric precision reduction for coordinates and path data
|
|
5
4
|
* - Whitespace removal in attribute values (numeric sequences)
|
|
6
5
|
* - Default attribute removal (safe, well-documented defaults)
|
|
@@ -9,6 +8,8 @@
|
|
|
9
8
|
* - Path data space optimization
|
|
10
9
|
*/
|
|
11
10
|
|
|
11
|
+
// Imports
|
|
12
|
+
|
|
12
13
|
import { LRU } from './utils.js';
|
|
13
14
|
import { RE_NUMERIC_VALUE } from './constants.js';
|
|
14
15
|
|
|
@@ -96,7 +97,7 @@ function minifyNumber(num, precision = 3) {
|
|
|
96
97
|
if (num === '1.0' || num === '1.00' || num === '1.000') return '1';
|
|
97
98
|
|
|
98
99
|
// Check cache
|
|
99
|
-
// (Note:
|
|
100
|
+
// (Note: Uses input string as key, so “0.0000” and “0.00000” create separate entries.
|
|
100
101
|
// This is intentional to avoid parsing overhead.
|
|
101
102
|
// Real-world SVG files from export tools typically use consistent formats.)
|
|
102
103
|
const cacheKey = `${num}:${precision}`;
|
|
@@ -135,15 +136,15 @@ function minifyPathData(pathData, precision = 3) {
|
|
|
135
136
|
|
|
136
137
|
// Remove unnecessary spaces around path commands
|
|
137
138
|
// Safe to remove space after a command letter when it’s followed by a number (which may be negative)
|
|
138
|
-
// M 10 20 → M10 20
|
|
139
|
+
// `M 10 20` → `M10 20`, `L -5 -3` → `L-5-3`
|
|
139
140
|
result = result.replace(/([MLHVCSQTAZmlhvcsqtaz])\s+(?=-?\d)/g, '$1');
|
|
140
141
|
|
|
141
142
|
// Safe to remove space before command letter when preceded by a number
|
|
142
|
-
// 0 L → 0L
|
|
143
|
+
// `0 L` → `0L`, `20 M` → `20M`
|
|
143
144
|
result = result.replace(/(\d)\s+([MLHVCSQTAZmlhvcsqtaz])/g, '$1$2');
|
|
144
145
|
|
|
145
146
|
// Safe to remove space before negative number when preceded by a number
|
|
146
|
-
// 10 -20 → 10-20 (numbers are separated by the minus sign)
|
|
147
|
+
// `10 -20` → `10-20` (numbers are separated by the minus sign)
|
|
147
148
|
result = result.replace(/(\d)\s+(-\d)/g, '$1$2');
|
|
148
149
|
|
|
149
150
|
return result;
|
|
@@ -152,9 +153,9 @@ function minifyPathData(pathData, precision = 3) {
|
|
|
152
153
|
/**
|
|
153
154
|
* Minify whitespace in numeric attribute values
|
|
154
155
|
* Examples:
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
156
|
+
* - “10 , 20" → "10,20"
|
|
157
|
+
* - "translate( 10 20 )" → "translate(10 20)"
|
|
158
|
+
* - "100, 10 40, 198" → "100,10 40,198"
|
|
158
159
|
*
|
|
159
160
|
* @param {string} value - Attribute value to minify
|
|
160
161
|
* @returns {string} Minified value
|
|
@@ -187,8 +188,7 @@ function minifyColor(color) {
|
|
|
187
188
|
|
|
188
189
|
// Don’t process values that aren’t simple colors (preserve case-sensitive references)
|
|
189
190
|
// `url(#id)`, `var(--name)`, `inherit`, `currentColor`, etc.
|
|
190
|
-
if (trimmed.includes('url(') || trimmed.includes('var(') ||
|
|
191
|
-
trimmed === 'inherit' || trimmed === 'currentColor') {
|
|
191
|
+
if (trimmed.includes('url(') || trimmed.includes('var(') || trimmed === 'inherit' || trimmed === 'currentColor') {
|
|
192
192
|
return trimmed;
|
|
193
193
|
}
|
|
194
194
|
|
|
@@ -196,7 +196,7 @@ function minifyColor(color) {
|
|
|
196
196
|
const lower = trimmed.toLowerCase();
|
|
197
197
|
|
|
198
198
|
// Shorten 6-digit hex to 3-digit when possible
|
|
199
|
-
//
|
|
199
|
+
// `#aabbcc` → `#abc`, `#000000` → `#000`
|
|
200
200
|
const hexMatch = lower.match(/^#([0-9a-f]{6})$/);
|
|
201
201
|
if (hexMatch) {
|
|
202
202
|
const hex = hexMatch[1];
|
|
@@ -216,7 +216,7 @@ function minifyColor(color) {
|
|
|
216
216
|
return NAMED_COLORS[lower] || lower;
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
-
// Convert rgb(
|
|
219
|
+
// Convert rgb() to hex
|
|
220
220
|
const rgbMatch = lower.match(/^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/);
|
|
221
221
|
if (rgbMatch) {
|
|
222
222
|
const r = parseInt(rgbMatch[1], 10);
|
|
@@ -247,14 +247,14 @@ function minifyColor(color) {
|
|
|
247
247
|
const NUMERIC_ATTRS = new Set([
|
|
248
248
|
'd', // Path data
|
|
249
249
|
'points', // Polygon/polyline points
|
|
250
|
-
'viewBox', // viewBox coordinates
|
|
250
|
+
'viewBox', // `viewBox` coordinates
|
|
251
251
|
'transform', // Transform functions
|
|
252
252
|
'x', 'y', 'x1', 'y1', 'x2', 'y2', // Coordinates
|
|
253
253
|
'cx', 'cy', 'r', 'rx', 'ry', // Circle/ellipse
|
|
254
254
|
'width', 'height', // Dimensions
|
|
255
255
|
'dx', 'dy', // Text offsets
|
|
256
256
|
'offset', // Gradient offset
|
|
257
|
-
'startOffset', // textPath
|
|
257
|
+
'startOffset', // `textPath`
|
|
258
258
|
'pathLength', // Path length
|
|
259
259
|
'stdDeviation', // Filter params
|
|
260
260
|
'baseFrequency', // Turbulence
|
package/src/lib/whitespace.js
CHANGED
|
@@ -9,7 +9,8 @@ import {
|
|
|
9
9
|
RE_NBSP_TRAILING_GROUP,
|
|
10
10
|
RE_NBSP_TRAILING_STRIP,
|
|
11
11
|
inlineElementsToKeepWhitespace,
|
|
12
|
-
inlineElementsToKeepWhitespaceWithin
|
|
12
|
+
inlineElementsToKeepWhitespaceWithin,
|
|
13
|
+
formControlElements
|
|
13
14
|
} from './constants.js';
|
|
14
15
|
|
|
15
16
|
// Trim whitespace
|
|
@@ -71,7 +72,7 @@ function collapseWhitespace(str, options, trimLeft, trimRight, collapseAll) {
|
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
if (trimLeft) {
|
|
74
|
-
//
|
|
75
|
+
// No-break space is specifically handled inside the replacer function
|
|
75
76
|
str = str.replace(/^[ \n\r\t\f\xA0]+/, function (spaces) {
|
|
76
77
|
const conservative = !lineBreakBefore && options.conservativeCollapse;
|
|
77
78
|
if (conservative && spaces === '\t') {
|
|
@@ -82,7 +83,7 @@ function collapseWhitespace(str, options, trimLeft, trimRight, collapseAll) {
|
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
if (trimRight) {
|
|
85
|
-
//
|
|
86
|
+
// No-break space is specifically handled inside the replacer function
|
|
86
87
|
str = str.replace(/[ \n\r\t\f\xA0]+$/, function (spaces) {
|
|
87
88
|
const conservative = !lineBreakAfter && options.conservativeCollapse;
|
|
88
89
|
if (conservative && spaces === '\t') {
|
|
@@ -106,11 +107,42 @@ function collapseWhitespace(str, options, trimLeft, trimRight, collapseAll) {
|
|
|
106
107
|
|
|
107
108
|
// Collapse whitespace smartly based on surrounding tags
|
|
108
109
|
|
|
109
|
-
function collapseWhitespaceSmart(str, prevTag, nextTag, options, inlineElements, inlineTextSet) {
|
|
110
|
+
function collapseWhitespaceSmart(str, prevTag, nextTag, prevAttrs, nextAttrs, options, inlineElements, inlineTextSet) {
|
|
111
|
+
const prevTagName = prevTag && (prevTag.charAt(0) === '/' ? prevTag.slice(1) : prevTag);
|
|
112
|
+
const nextTagName = nextTag && (nextTag.charAt(0) === '/' ? nextTag.slice(1) : nextTag);
|
|
113
|
+
|
|
114
|
+
// Helper: Check if an input element has `type="hidden"`
|
|
115
|
+
const isHiddenInput = (tagName, attrs) => {
|
|
116
|
+
if (tagName !== 'input' || !attrs || !attrs.length) return false;
|
|
117
|
+
const typeAttr = attrs.find(attr => attr.name === 'type');
|
|
118
|
+
return typeAttr && typeAttr.value === 'hidden';
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// Check if prev/next are non-rendering (hidden) elements
|
|
122
|
+
const prevIsHidden = isHiddenInput(prevTagName, prevAttrs);
|
|
123
|
+
const nextIsHidden = isHiddenInput(nextTagName, nextAttrs);
|
|
124
|
+
|
|
110
125
|
let trimLeft = prevTag && !inlineElementsToKeepWhitespace.has(prevTag);
|
|
126
|
+
|
|
127
|
+
// Smart default behavior: Collapse space after non-rendering elements (`type="hidden"`)
|
|
128
|
+
// This happens even in basic `collapseWhitespace` mode (safe optimization)
|
|
129
|
+
if (!trimLeft && prevIsHidden && str && !/\S/.test(str)) {
|
|
130
|
+
trimLeft = true;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Aggressive mode: Collapse between all form controls (pure whitespace only)
|
|
134
|
+
const isPureWhitespace = str && !/\S/.test(str);
|
|
135
|
+
if (!trimLeft && prevTagName && nextTagName &&
|
|
136
|
+
options.collapseInlineTagWhitespace &&
|
|
137
|
+
isPureWhitespace &&
|
|
138
|
+
formControlElements.has(prevTagName) && formControlElements.has(nextTagName)) {
|
|
139
|
+
trimLeft = true;
|
|
140
|
+
}
|
|
141
|
+
|
|
111
142
|
if (trimLeft && !options.collapseInlineTagWhitespace) {
|
|
112
143
|
trimLeft = prevTag.charAt(0) === '/' ? !inlineElements.has(prevTag.slice(1)) : !inlineTextSet.has(prevTag);
|
|
113
144
|
}
|
|
145
|
+
|
|
114
146
|
// When `collapseInlineTagWhitespace` is enabled, still preserve whitespace around inline text elements
|
|
115
147
|
if (trimLeft && options.collapseInlineTagWhitespace) {
|
|
116
148
|
const tagName = prevTag.charAt(0) === '/' ? prevTag.slice(1) : prevTag;
|
|
@@ -118,10 +150,26 @@ function collapseWhitespaceSmart(str, prevTag, nextTag, options, inlineElements,
|
|
|
118
150
|
trimLeft = false;
|
|
119
151
|
}
|
|
120
152
|
}
|
|
153
|
+
|
|
121
154
|
let trimRight = nextTag && !inlineElementsToKeepWhitespace.has(nextTag);
|
|
155
|
+
|
|
156
|
+
// Smart default behavior: Collapse space before non-rendering elements (`type="hidden"`)
|
|
157
|
+
if (!trimRight && nextIsHidden && str && !/\S/.test(str)) {
|
|
158
|
+
trimRight = true;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Aggressive mode: Same as `trimLeft`
|
|
162
|
+
if (!trimRight && prevTagName && nextTagName &&
|
|
163
|
+
options.collapseInlineTagWhitespace &&
|
|
164
|
+
isPureWhitespace &&
|
|
165
|
+
formControlElements.has(prevTagName) && formControlElements.has(nextTagName)) {
|
|
166
|
+
trimRight = true;
|
|
167
|
+
}
|
|
168
|
+
|
|
122
169
|
if (trimRight && !options.collapseInlineTagWhitespace) {
|
|
123
170
|
trimRight = nextTag.charAt(0) === '/' ? !inlineTextSet.has(nextTag.slice(1)) : !inlineElements.has(nextTag);
|
|
124
171
|
}
|
|
172
|
+
|
|
125
173
|
// When `collapseInlineTagWhitespace` is enabled, still preserve whitespace around inline text elements
|
|
126
174
|
if (trimRight && options.collapseInlineTagWhitespace) {
|
|
127
175
|
const tagName = nextTag.charAt(0) === '/' ? nextTag.slice(1) : nextTag;
|
|
@@ -129,6 +177,7 @@ function collapseWhitespaceSmart(str, prevTag, nextTag, options, inlineElements,
|
|
|
129
177
|
trimRight = false;
|
|
130
178
|
}
|
|
131
179
|
}
|
|
180
|
+
|
|
132
181
|
return collapseWhitespace(str, options, trimLeft, trimRight, prevTag && nextTag);
|
|
133
182
|
}
|
|
134
183
|
|
package/src/presets.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Preset configurations
|
|
2
|
+
* Preset configurations
|
|
3
3
|
*
|
|
4
4
|
* Presets provide curated option sets for common use cases:
|
|
5
|
-
* - conservative
|
|
6
|
-
* - comprehensive
|
|
5
|
+
* - `conservative`: Safe minification suitable for most projects
|
|
6
|
+
* - `comprehensive`: Aggressive minification for maximum file size reduction
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
export const presets = {
|
|
@@ -49,7 +49,7 @@ export const presets = {
|
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* Get preset configuration by name
|
|
52
|
-
* @param {string} name - Preset name (
|
|
52
|
+
* @param {string} name - Preset name (“conservative” or “comprehensive”)
|
|
53
53
|
* @returns {object|null} Preset options object or null if not found
|
|
54
54
|
*/
|
|
55
55
|
export function getPreset(name) {
|
package/src/tokenchain.js
CHANGED
|
@@ -35,7 +35,7 @@ class Sorter {
|
|
|
35
35
|
|
|
36
36
|
class TokenChain {
|
|
37
37
|
constructor() {
|
|
38
|
-
// Use
|
|
38
|
+
// Use map instead of object properties for better performance
|
|
39
39
|
this.map = new Map();
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -52,7 +52,7 @@ class TokenChain {
|
|
|
52
52
|
const sorter = new Sorter();
|
|
53
53
|
sorter.sorterMap = new Map();
|
|
54
54
|
|
|
55
|
-
// Convert
|
|
55
|
+
// Convert map entries to array and sort by frequency (descending), then alphabetically
|
|
56
56
|
const entries = Array.from(this.map.entries()).sort((a, b) => {
|
|
57
57
|
const m = a[1].arrays.length;
|
|
58
58
|
const n = b[1].arrays.length;
|
package/src/lib/index.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
// Utils
|
|
2
|
-
export * from './utils.js';
|
|
3
|
-
|
|
4
|
-
// Constants
|
|
5
|
-
export * from './constants.js';
|
|
6
|
-
|
|
7
|
-
// Whitespace
|
|
8
|
-
export * from './whitespace.js';
|
|
9
|
-
|
|
10
|
-
// Attributes
|
|
11
|
-
export * from './attributes.js';
|
|
12
|
-
|
|
13
|
-
// Elements
|
|
14
|
-
export * from './elements.js';
|
|
15
|
-
|
|
16
|
-
// Content processors
|
|
17
|
-
export * from './content.js';
|
|
18
|
-
|
|
19
|
-
// Options
|
|
20
|
-
export * from './options.js';
|