html-minifier-next 4.9.2 → 4.10.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 CHANGED
@@ -112,6 +112,8 @@ HTML Minifier Next provides presets for common use cases. Presets are pre-config
112
112
  * `conservative`: Safe minification suitable for most projects. Includes whitespace collapsing, comment removal, and doctype normalization.
113
113
  * `comprehensive`: Aggressive minification for maximum file size reduction. Includes relevant conservative options plus attribute quote removal, optional tag removal, and more.
114
114
 
115
+ To review the specific options set, [presets.js](https://github.com/j9t/html-minifier-next/blob/main/src/presets.js) lists them in an accessible manner.
116
+
115
117
  **Using presets:**
116
118
 
117
119
  ```bash
@@ -233,32 +235,34 @@ How does HTML Minifier Next compare to other minifiers? (All with the most aggre
233
235
  | Site | Original Size (KB) | [HTML Minifier Next](https://github.com/j9t/html-minifier-next)<br>[![npm last update](https://img.shields.io/npm/last-update/html-minifier-next)](https://socket.dev/npm/package/html-minifier-next) | [HTML Minifier Terser](https://github.com/terser/html-minifier-terser)<br>[![npm last update](https://img.shields.io/npm/last-update/html-minifier-terser)](https://socket.dev/npm/package/html-minifier-terser) | [htmlnano](https://github.com/posthtml/htmlnano)<br>[![npm last update](https://img.shields.io/npm/last-update/htmlnano)](https://socket.dev/npm/package/htmlnano) | [@swc/html](https://github.com/swc-project/swc)<br>[![npm last update](https://img.shields.io/npm/last-update/@swc/html)](https://socket.dev/npm/package/@swc/html) | [minify-html](https://github.com/wilsonzlin/minify-html)<br>[![npm last update](https://img.shields.io/npm/last-update/@minify-html/node)](https://socket.dev/npm/package/@minify-html/node) | [minimize](https://github.com/Swaagie/minimize)<br>[![npm last update](https://img.shields.io/npm/last-update/minimize)](https://socket.dev/npm/package/minimize) | [html­com­pressor.­com](https://htmlcompressor.com/) |
234
236
  | --- | --- | --- | --- | --- | --- | --- | --- | --- |
235
237
  | [A List Apart](https://alistapart.com/) | 59 | **49** | 50 | 51 | 52 | 51 | 54 | 52 |
236
- | [Apple](https://www.apple.com/) | 260 | **203** | **203** | 231 | 235 | 236 | 237 | 238 |
237
- | [BBC](https://www.bbc.co.uk/) | 904 | **772** | 783 | 840 | 852 | 855 | 896 | n/a |
238
- | [CSS-Tricks](https://css-tricks.com/) | 160 | 120 | **119** | 126 | 141 | 141 | 146 | 143 |
238
+ | [Apple](https://www.apple.com/) | 260 | **203** | 204 | 231 | 235 | 236 | 238 | 238 |
239
+ | [BBC](https://www.bbc.co.uk/) | 720 | **655** | 665 | 677 | 677 | 678 | 714 | n/a |
240
+ | [CERN](https://home.cern/) | 156 | **87** | **87** | 95 | 95 | 95 | 97 | 100 |
241
+ | [CSS-Tricks](https://css-tricks.com/) | 163 | 122 | **120** | 128 | 143 | 144 | 149 | 145 |
239
242
  | [ECMAScript](https://tc39.es/ecma262/) | 7238 | **6341** | **6341** | 6561 | 6444 | 6567 | 6614 | n/a |
240
243
  | [EDRi](https://edri.org/) | 80 | **59** | 60 | 70 | 70 | 71 | 75 | 73 |
241
- | [EFF](https://www.eff.org/) | 56 | **47** | **47** | 50 | 49 | 49 | 51 | 50 |
244
+ | [EFF](https://www.eff.org/) | 56 | **48** | **48** | 50 | 49 | 49 | 51 | 51 |
242
245
  | [European Alternatives](https://european-alternatives.eu/) | 48 | **30** | **30** | 32 | 32 | 32 | 32 | 32 |
243
- | [FAZ](https://www.faz.net/aktuell/) | 1559 | 1452 | 1457 | **1397** | 1484 | 1495 | 1506 | n/a |
244
- | [Frontend Dogma](https://frontenddogma.com/) | 221 | **211** | 213 | 234 | 219 | 221 | 239 | 220 |
246
+ | [FAZ](https://www.faz.net/aktuell/) | 1562 | 1455 | 1460 | **1400** | 1487 | 1498 | 1509 | n/a |
247
+ | [French Tech](https://lafrenchtech.gouv.fr/) | 152 | **121** | 122 | 125 | 125 | 125 | 132 | 126 |
248
+ | [Frontend Dogma](https://frontenddogma.com/) | 222 | **213** | 215 | 236 | 221 | 222 | 241 | 222 |
245
249
  | [Google](https://www.google.com/) | 18 | **17** | **17** | **17** | **17** | **17** | 18 | 18 |
246
- | [Ground News](https://ground.news/) | 1967 | **1707** | 1710 | 1810 | 1835 | 1840 | 1954 | n/a |
250
+ | [Ground News](https://ground.news/) | 2339 | **2052** | 2056 | 2151 | 2179 | 2182 | 2326 | n/a |
247
251
  | [HTML Living Standard](https://html.spec.whatwg.org/multipage/) | 149 | **147** | **147** | 153 | **147** | 149 | 155 | 149 |
248
- | [Igalia](https://www.igalia.com/) | 49 | **33** | **33** | 36 | 35 | 36 | 36 | 36 |
249
- | [Leanpub](https://leanpub.com/) | 1268 | **1071** | **1071** | 1078 | 1076 | 1072 | 1263 | n/a |
250
- | [Mastodon](https://mastodon.social/explore) | 36 | **27** | **27** | 31 | 34 | 34 | 35 | 35 |
252
+ | [Igalia](https://www.igalia.com/) | 50 | **33** | **33** | 36 | 36 | 36 | 37 | 36 |
253
+ | [Leanpub](https://leanpub.com/) | 1204 | **997** | **997** | 1004 | 1003 | 1001 | 1198 | n/a |
254
+ | [Mastodon](https://mastodon.social/explore) | 37 | **27** | **27** | 32 | 34 | 35 | 35 | 35 |
251
255
  | [MDN](https://developer.mozilla.org/en-US/) | 109 | **62** | **62** | 64 | 65 | 65 | 68 | 68 |
252
- | [Middle East Eye](https://www.middleeasteye.net/) | 223 | **195** | **195** | 202 | 200 | 200 | 202 | 203 |
253
- | [Nielsen Norman Group](https://www.nngroup.com/) | 84 | 71 | 72 | **53** | 71 | 73 | 74 | 73 |
254
- | [SitePoint](https://www.sitepoint.com/) | 487 | **346** | **346** | 424 | 461 | 466 | 484 | n/a |
256
+ | [Middle East Eye](https://www.middleeasteye.net/) | 222 | **195** | 196 | 202 | 200 | 200 | 202 | 203 |
257
+ | [Nielsen Norman Group](https://www.nngroup.com/) | 84 | 71 | 71 | **53** | 71 | 73 | 74 | 73 |
258
+ | [SitePoint](https://www.sitepoint.com/) | 485 | **354** | **354** | 425 | 459 | 464 | 481 | n/a |
255
259
  | [TetraLogical](https://tetralogical.com/) | 44 | 38 | 38 | **35** | 38 | 38 | 39 | 39 |
256
- | [TPGi](https://www.tpgi.com/) | 176 | **160** | 162 | **160** | 165 | 166 | 173 | 172 |
257
- | [United Nations](https://www.un.org/en/) | 150 | **112** | 113 | 120 | 124 | 124 | 129 | 122 |
258
- | [W3C](https://www.w3.org/) | 50 | **35** | 36 | 38 | 38 | 38 | 40 | 38 |
259
- | **Average processing time** | | 313 ms (24/24) | 367 ms (24/24) | 184 ms (24/24) | 62 ms (24/24) | **18 ms (24/24)** | 336 ms (24/24) | 1392 ms (18/24) |
260
+ | [TPGi](https://www.tpgi.com/) | 175 | **159** | 161 | 160 | 164 | 165 | 172 | 171 |
261
+ | [United Nations](https://www.un.org/en/) | 151 | **112** | 114 | 121 | 125 | 124 | 130 | 123 |
262
+ | [W3C](https://www.w3.org/) | 50 | **36** | **36** | 38 | 38 | 38 | 40 | 38 |
263
+ | **Average processing time** | | 302 ms (26/26) | 356 ms (26/26) | 178 ms (26/26) | 59 ms (26/26) | **16 ms (26/26)** | 329 ms (26/26) | 1468 ms (20/26) |
260
264
 
261
- (Last updated: Dec 14, 2025)
265
+ (Last updated: Dec 16, 2025)
262
266
  <!-- End auto-generated -->
263
267
 
264
268
  ## Examples
@@ -89,6 +89,9 @@ const preCompiledStackedTags = {
89
89
  'noscript': /([\s\S]*?)<\/noscript[^>]*>/i
90
90
  };
91
91
 
92
+ // Cache for compiled attribute regexes per handler configuration
93
+ const attrRegexCache = new WeakMap();
94
+
92
95
  function attrForHandler(handler) {
93
96
  let pattern = singleAttrIdentifier.source +
94
97
  '(?:\\s*(' + joinSingleAttrAssigns(handler) + ')' +
@@ -126,22 +129,47 @@ class HTMLParser {
126
129
  }
127
130
 
128
131
  async parse() {
129
- let html = this.html;
130
132
  const handler = this.handler;
133
+ const fullHtml = this.html;
134
+ const fullLength = fullHtml.length;
131
135
 
132
136
  const stack = []; let lastTag;
133
- const attribute = attrForHandler(handler);
134
- let last, prevTag = undefined, nextTag = undefined;
135
-
136
- // Track position for better error messages
137
- let position = 0;
138
- const getLineColumn = (pos) => {
139
- const lines = this.html.slice(0, pos).split('\n');
140
- return { line: lines.length, column: lines[lines.length - 1].length + 1 };
137
+ // Use cached attribute regex if available
138
+ let attribute = attrRegexCache.get(handler);
139
+ if (!attribute) {
140
+ attribute = attrForHandler(handler);
141
+ attrRegexCache.set(handler, attribute);
142
+ }
143
+ let prevTag = undefined, nextTag = undefined;
144
+
145
+ // Index-based parsing
146
+ let pos = 0;
147
+ let lastPos;
148
+
149
+ // Helper to get remaining HTML from current position
150
+ const remaining = () => fullHtml.slice(pos);
151
+
152
+ // Helper to advance position
153
+ const advance = (n) => { pos += n; };
154
+
155
+ // Lazy line/column calculation—only compute on actual errors
156
+ const getLineColumn = (position) => {
157
+ let line = 1;
158
+ let column = 1;
159
+ for (let i = 0; i < position; i++) {
160
+ if (fullHtml[i] === '\n') {
161
+ line++;
162
+ column = 1;
163
+ } else {
164
+ column++;
165
+ }
166
+ }
167
+ return { line, column };
141
168
  };
142
169
 
143
- while (html) {
144
- last = html;
170
+ while (pos < fullLength) {
171
+ lastPos = pos;
172
+ const html = remaining();
145
173
  // Make sure we’re not in a `script` or `style` element
146
174
  if (!lastTag || !special.has(lastTag)) {
147
175
  let textEnd = html.indexOf('<');
@@ -154,7 +182,7 @@ class HTMLParser {
154
182
  if (handler.comment) {
155
183
  await handler.comment(html.substring(4, commentEnd));
156
184
  }
157
- html = html.substring(commentEnd + 3);
185
+ advance(commentEnd + 3);
158
186
  prevTag = '';
159
187
  continue;
160
188
  }
@@ -168,7 +196,7 @@ class HTMLParser {
168
196
  if (handler.comment) {
169
197
  await handler.comment(html.substring(2, conditionalEnd + 1), true /* non-standard */);
170
198
  }
171
- html = html.substring(conditionalEnd + 2);
199
+ advance(conditionalEnd + 2);
172
200
  prevTag = '';
173
201
  continue;
174
202
  }
@@ -180,7 +208,7 @@ class HTMLParser {
180
208
  if (handler.doctype) {
181
209
  handler.doctype(doctypeMatch[0]);
182
210
  }
183
- html = html.substring(doctypeMatch[0].length);
211
+ advance(doctypeMatch[0].length);
184
212
  prevTag = '';
185
213
  continue;
186
214
  }
@@ -188,7 +216,7 @@ class HTMLParser {
188
216
  // End tag
189
217
  const endTagMatch = html.match(endTag);
190
218
  if (endTagMatch) {
191
- html = html.substring(endTagMatch[0].length);
219
+ advance(endTagMatch[0].length);
192
220
  await parseEndTag(endTagMatch[0], endTagMatch[1]);
193
221
  prevTag = '/' + endTagMatch[1].toLowerCase();
194
222
  continue;
@@ -197,7 +225,7 @@ class HTMLParser {
197
225
  // Start tag
198
226
  const startTagMatch = parseStartTag(html);
199
227
  if (startTagMatch) {
200
- html = startTagMatch.rest;
228
+ advance(startTagMatch.advance);
201
229
  await handleStartTag(startTagMatch);
202
230
  prevTag = startTagMatch.tagName.toLowerCase();
203
231
  continue;
@@ -212,18 +240,19 @@ class HTMLParser {
212
240
  let text;
213
241
  if (textEnd >= 0) {
214
242
  text = html.substring(0, textEnd);
215
- html = html.substring(textEnd);
243
+ advance(textEnd);
216
244
  } else {
217
245
  text = html;
218
- html = '';
246
+ advance(html.length);
219
247
  }
220
248
 
221
249
  // Next tag
222
- let nextTagMatch = parseStartTag(html);
250
+ const nextHtml = remaining();
251
+ let nextTagMatch = parseStartTag(nextHtml);
223
252
  if (nextTagMatch) {
224
253
  nextTag = nextTagMatch.tagName;
225
254
  } else {
226
- nextTagMatch = html.match(endTag);
255
+ nextTagMatch = nextHtml.match(endTag);
227
256
  if (nextTagMatch) {
228
257
  nextTag = '/' + nextTagMatch[1];
229
258
  } else {
@@ -252,41 +281,38 @@ class HTMLParser {
252
281
  await handler.chars(text);
253
282
  }
254
283
  // Advance HTML past the matched special tag content and its closing tag
255
- html = html.slice(m.index + m[0].length);
284
+ advance(m.index + m[0].length);
256
285
  await parseEndTag('</' + stackedTag + '>', stackedTag);
257
286
  } else {
258
287
  // No closing tag found; to avoid infinite loop, break similarly to previous behavior
259
288
  if (handler.continueOnParseError && handler.chars && html) {
260
289
  await handler.chars(html[0], prevTag, '');
261
- html = html.substring(1);
290
+ advance(1);
262
291
  } else {
263
292
  break;
264
293
  }
265
294
  }
266
295
  }
267
296
 
268
- if (html === last) {
297
+ if (pos === lastPos) {
269
298
  if (handler.continueOnParseError) {
270
299
  // Skip the problematic character and continue
271
300
  if (handler.chars) {
272
- await handler.chars(html[0], prevTag, '');
301
+ await handler.chars(fullHtml[pos], prevTag, '');
273
302
  }
274
- html = html.substring(1);
275
- position++;
303
+ advance(1);
276
304
  prevTag = '';
277
305
  continue;
278
306
  }
279
- const loc = getLineColumn(position);
280
- // Include some context before the error position so the snippet contains
281
- // the offending markup plus preceding characters (e.g. "invalid<tag").
307
+ const loc = getLineColumn(pos);
308
+ // Include some context before the error position so the snippet contains the offending markup plus preceding characters (e.g., “invalid<tag”)
282
309
  const CONTEXT_BEFORE = 50;
283
- const startPos = Math.max(0, position - CONTEXT_BEFORE);
284
- const snippet = this.html.slice(startPos, startPos + 200).replace(/\n/g, ' ');
310
+ const startPos = Math.max(0, pos - CONTEXT_BEFORE);
311
+ const snippet = fullHtml.slice(startPos, startPos + 200).replace(/\n/g, ' ');
285
312
  throw new Error(
286
- `Parse error at line ${loc.line}, column ${loc.column}:\n${snippet}${this.html.length > startPos + 200 ? '…' : ''}`
313
+ `Parse error at line ${loc.line}, column ${loc.column}:\n${snippet}${fullHtml.length > startPos + 200 ? '…' : ''}`
287
314
  );
288
315
  }
289
- position = this.html.length - html.length;
290
316
  }
291
317
 
292
318
  if (!handler.partialMarkup) {
@@ -299,9 +325,11 @@ class HTMLParser {
299
325
  if (start) {
300
326
  const match = {
301
327
  tagName: start[1],
302
- attrs: []
328
+ attrs: [],
329
+ advance: 0
303
330
  };
304
- input = input.slice(start[0].length);
331
+ let consumed = start[0].length;
332
+ input = input.slice(consumed);
305
333
  let end, attr;
306
334
 
307
335
  // Safety limit: max length of input to check for attributes
@@ -351,7 +379,9 @@ class HTMLParser {
351
379
  } else {
352
380
  attr[baseIndex + 3] = value; // Single-quoted value
353
381
  }
354
- input = input.slice(fullAttr.length);
382
+ const attrLen = fullAttr.length;
383
+ input = input.slice(attrLen);
384
+ consumed += attrLen;
355
385
  match.attrs.push(attr);
356
386
  continue;
357
387
  }
@@ -368,7 +398,9 @@ class HTMLParser {
368
398
  break;
369
399
  }
370
400
 
371
- input = input.slice(attr[0].length);
401
+ const attrLen = attr[0].length;
402
+ input = input.slice(attrLen);
403
+ consumed += attrLen;
372
404
  match.attrs.push(attr);
373
405
  }
374
406
 
@@ -376,7 +408,8 @@ class HTMLParser {
376
408
  end = input.match(startTagClose);
377
409
  if (end) {
378
410
  match.unarySlash = end[1];
379
- match.rest = input.slice(end[0].length);
411
+ consumed += end[0].length;
412
+ match.advance = consumed;
380
413
  return match;
381
414
  }
382
415
  }
@@ -386,7 +419,7 @@ class HTMLParser {
386
419
  let pos;
387
420
  const needle = tagName.toLowerCase();
388
421
  for (pos = stack.length - 1; pos >= 0; pos--) {
389
- const currentTag = stack[pos].tag.toLowerCase();
422
+ const currentTag = stack[pos].lowerTag;
390
423
  if (currentTag === needle) {
391
424
  return pos;
392
425
  }
@@ -440,7 +473,7 @@ class HTMLParser {
440
473
  }
441
474
  if (tagName === 'col' && findTag('colgroup') < 0) {
442
475
  lastTag = 'colgroup';
443
- stack.push({ tag: lastTag, attrs: [] });
476
+ stack.push({ tag: lastTag, lowerTag: 'colgroup', attrs: [] });
444
477
  if (handler.start) {
445
478
  await handler.start(lastTag, [], false, '');
446
479
  }
@@ -519,7 +552,7 @@ class HTMLParser {
519
552
  });
520
553
 
521
554
  if (!unary) {
522
- stack.push({ tag: tagName, attrs });
555
+ stack.push({ tag: tagName, lowerTag: tagName.toLowerCase(), attrs });
523
556
  lastTag = tagName;
524
557
  unarySlash = '';
525
558
  }
@@ -533,7 +566,7 @@ class HTMLParser {
533
566
  let pos;
534
567
  const needle = tagName.toLowerCase();
535
568
  for (pos = stack.length - 1; pos >= 0; pos--) {
536
- if (stack[pos].tag.toLowerCase() === needle) {
569
+ if (stack[pos].lowerTag === needle) {
537
570
  break;
538
571
  }
539
572
  }
@@ -585,21 +618,40 @@ class HTMLParser {
585
618
  class Sorter {
586
619
  sort(tokens, fromIndex = 0) {
587
620
  for (let i = 0, len = this.keys.length; i < len; i++) {
588
- const key = this.keys[i];
589
- const token = key.slice(1);
621
+ const token = this.keys[i];
622
+
623
+ // Build position map for this token to avoid repeated `indexOf`
624
+ const positions = [];
625
+ for (let j = fromIndex; j < tokens.length; j++) {
626
+ if (tokens[j] === token) {
627
+ positions.push(j);
628
+ }
629
+ }
590
630
 
591
- let index = tokens.indexOf(token, fromIndex);
631
+ if (positions.length > 0) {
632
+ // Build new array with tokens in sorted order instead of splicing
633
+ const result = [];
634
+
635
+ // Add all instances of the current token first
636
+ for (let j = 0; j < positions.length; j++) {
637
+ result.push(token);
638
+ }
592
639
 
593
- if (index !== -1) {
594
- do {
595
- if (index !== fromIndex) {
596
- tokens.splice(index, 1);
597
- tokens.splice(fromIndex, 0, token);
640
+ // Add other tokens, skipping positions where current token was
641
+ const posSet = new Set(positions);
642
+ for (let j = fromIndex; j < tokens.length; j++) {
643
+ if (!posSet.has(j)) {
644
+ result.push(tokens[j]);
598
645
  }
599
- fromIndex++;
600
- } while ((index = tokens.indexOf(token, fromIndex)) !== -1);
646
+ }
647
+
648
+ // Copy sorted portion back to tokens array
649
+ for (let j = 0; j < result.length; j++) {
650
+ tokens[fromIndex + j] = result[j];
651
+ }
601
652
 
602
- return this[key].sort(tokens, fromIndex);
653
+ const newFromIndex = fromIndex + positions.length;
654
+ return this.sorterMap.get(token).sort(tokens, newFromIndex);
603
655
  }
604
656
  }
605
657
  return tokens;
@@ -607,44 +659,68 @@ class Sorter {
607
659
  }
608
660
 
609
661
  class TokenChain {
662
+ constructor() {
663
+ // Use Map instead of object properties for better performance
664
+ this.map = new Map();
665
+ }
666
+
610
667
  add(tokens) {
611
668
  tokens.forEach((token) => {
612
- const key = '$' + token;
613
- if (!this[key]) {
614
- this[key] = [];
615
- this[key].processed = 0;
669
+ if (!this.map.has(token)) {
670
+ this.map.set(token, { arrays: [], processed: 0 });
616
671
  }
617
- this[key].push(tokens);
672
+ this.map.get(token).arrays.push(tokens);
618
673
  });
619
674
  }
620
675
 
621
676
  createSorter() {
622
677
  const sorter = new Sorter();
678
+ sorter.sorterMap = new Map();
679
+
680
+ // Convert Map entries to array and sort
681
+ const entries = Array.from(this.map.entries()).sort((a, b) => {
682
+ const m = a[1].arrays.length;
683
+ const n = b[1].arrays.length;
684
+ // Sort by length descending (larger first)
685
+ const lengthDiff = n - m;
686
+ if (lengthDiff !== 0) return lengthDiff;
687
+ // If lengths equal, sort by key ascending
688
+ return a[0].localeCompare(b[0]);
689
+ });
623
690
 
624
- sorter.keys = Object.keys(this).sort((j, k) => {
625
- const m = this[j].length;
626
- const n = this[k].length;
627
- return m < n ? 1 : m > n ? -1 : j < k ? -1 : j > k ? 1 : 0;
628
- }).filter((key) => {
629
- if (this[key].processed < this[key].length) {
630
- const token = key.slice(1);
691
+ sorter.keys = [];
692
+
693
+ entries.forEach(([token, data]) => {
694
+ if (data.processed < data.arrays.length) {
631
695
  const chain = new TokenChain();
632
696
 
633
- this[key].forEach((tokens) => {
634
- let index;
635
- while ((index = tokens.indexOf(token)) !== -1) {
636
- tokens.splice(index, 1);
697
+ data.arrays.forEach((tokens) => {
698
+ // Build new array without the current token instead of splicing
699
+ const filtered = [];
700
+ for (let i = 0; i < tokens.length; i++) {
701
+ if (tokens[i] !== token) {
702
+ filtered.push(tokens[i]);
703
+ }
637
704
  }
638
- tokens.forEach((token) => {
639
- this['$' + token].processed++;
705
+
706
+ // Mark remaining tokens as processed
707
+ filtered.forEach((t) => {
708
+ const tData = this.map.get(t);
709
+ if (tData) {
710
+ tData.processed++;
711
+ }
640
712
  });
641
- chain.add(tokens.slice(0));
713
+
714
+ if (filtered.length > 0) {
715
+ chain.add(filtered);
716
+ }
642
717
  });
643
- sorter[key] = chain.createSorter();
644
- return true;
718
+
719
+ sorter.keys.push(token);
720
+ sorter.sorterMap.set(token, chain.createSorter());
645
721
  }
646
- return false;
647
722
  });
723
+
648
724
  return sorter;
649
725
  }
650
726
  }