html-minifier-next 7.6.0 → 8.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/src/tokenchain.js CHANGED
@@ -1,7 +1,9 @@
1
1
  class Sorter {
2
2
  constructor() {
3
- /** @type {string[]} */
4
- this.keys = [];
3
+ // Rank per token, in the order the keys were added—looked up per token, so a
4
+ // token list sorts in one pass instead of one scan per known key
5
+ /** @type {Map<string, number>} */
6
+ this.rank = new Map();
5
7
  /** @type {Map<string, Sorter>} */
6
8
  this.sorterMap = new Map();
7
9
  }
@@ -12,36 +14,41 @@ class Sorter {
12
14
  * @returns {string[]}
13
15
  */
14
16
  sort(tokens, fromIndex = 0) {
15
- for (const token of this.keys) {
16
-
17
- // Single pass: Count matches and collect non-matches
18
- let matchCount = 0;
19
- const others = [];
20
-
21
- for (let j = fromIndex; j < tokens.length; j++) {
22
- const t = /** @type {string} */ (tokens[j]);
23
- if (t === token) {
24
- matchCount++;
25
- } else {
26
- others.push(t);
27
- }
17
+ // The present token with the lowest rank comes first—the same choice scanning
18
+ // the keys in order would make
19
+ let best = null;
20
+ let bestRank = Infinity;
21
+ for (let j = fromIndex; j < tokens.length; j++) {
22
+ const rank = this.rank.get(/** @type {string} */ (tokens[j]));
23
+ if (rank !== undefined && rank < bestRank) {
24
+ bestRank = rank;
25
+ best = /** @type {string} */ (tokens[j]);
28
26
  }
29
-
30
- if (matchCount > 0) {
31
- // Rebuild: `matchCount` instances of token first, then others
32
- let writeIdx = fromIndex;
33
- for (let j = 0; j < matchCount; j++) {
34
- tokens[writeIdx++] = token;
35
- }
36
- for (const other of others) {
37
- tokens[writeIdx++] = other;
38
- }
39
-
40
- const newFromIndex = fromIndex + matchCount;
41
- return this.sorterMap.get(token)?.sort(tokens, newFromIndex) ?? tokens;
27
+ }
28
+ if (best === null) return tokens;
29
+
30
+ // Single pass: Count matches and collect non-matches
31
+ let matchCount = 0;
32
+ const others = [];
33
+ for (let j = fromIndex; j < tokens.length; j++) {
34
+ const t = /** @type {string} */ (tokens[j]);
35
+ if (t === best) {
36
+ matchCount++;
37
+ } else {
38
+ others.push(t);
42
39
  }
43
40
  }
44
- return tokens;
41
+
42
+ // Rebuild: `matchCount` instances of the best token first, then others
43
+ let writeIdx = fromIndex;
44
+ for (let j = 0; j < matchCount; j++) {
45
+ tokens[writeIdx++] = best;
46
+ }
47
+ for (const other of others) {
48
+ tokens[writeIdx++] = other;
49
+ }
50
+
51
+ return this.sorterMap.get(best)?.sort(tokens, fromIndex + matchCount) ?? tokens;
45
52
  }
46
53
  }
47
54
 
@@ -104,7 +111,7 @@ class TokenChain {
104
111
  }
105
112
  });
106
113
 
107
- sorter.keys.push(token);
114
+ sorter.rank.set(token, sorter.rank.size);
108
115
  sorter.sorterMap.set(token, chain.createSorter());
109
116
  }
110
117
  });