html-minifier 3.5.21 → 5.0.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 (55) hide show
  1. package/LICENSE.txt +23 -0
  2. package/README.md +851 -109
  3. package/cli.js +991 -206
  4. package/dist/types/htmlminifier.d.ts +629 -0
  5. package/dist/types/htmlminifier.d.ts.map +1 -0
  6. package/dist/types/htmlparser.d.ts +28 -0
  7. package/dist/types/htmlparser.d.ts.map +1 -0
  8. package/dist/types/lib/attributes.d.ts +87 -0
  9. package/dist/types/lib/attributes.d.ts.map +1 -0
  10. package/dist/types/lib/constants.d.ts +421 -0
  11. package/dist/types/lib/constants.d.ts.map +1 -0
  12. package/dist/types/lib/content.d.ts +44 -0
  13. package/dist/types/lib/content.d.ts.map +1 -0
  14. package/dist/types/lib/elements.d.ts +48 -0
  15. package/dist/types/lib/elements.d.ts.map +1 -0
  16. package/dist/types/lib/fragments.d.ts +47 -0
  17. package/dist/types/lib/fragments.d.ts.map +1 -0
  18. package/dist/types/lib/option-definitions.d.ts +33 -0
  19. package/dist/types/lib/option-definitions.d.ts.map +1 -0
  20. package/dist/types/lib/options.d.ts +93 -0
  21. package/dist/types/lib/options.d.ts.map +1 -0
  22. package/dist/types/lib/unused-css.d.ts +76 -0
  23. package/dist/types/lib/unused-css.d.ts.map +1 -0
  24. package/dist/types/lib/urls.d.ts +15 -0
  25. package/dist/types/lib/urls.d.ts.map +1 -0
  26. package/dist/types/lib/utils.d.ts +132 -0
  27. package/dist/types/lib/utils.d.ts.map +1 -0
  28. package/dist/types/lib/whitespace.d.ts +97 -0
  29. package/dist/types/lib/whitespace.d.ts.map +1 -0
  30. package/dist/types/presets.d.ts +50 -0
  31. package/dist/types/presets.d.ts.map +1 -0
  32. package/dist/types/tokenchain.d.ts +36 -0
  33. package/dist/types/tokenchain.d.ts.map +1 -0
  34. package/html-minifier.schema.json +322 -0
  35. package/package.json +70 -58
  36. package/src/htmlminifier.js +2848 -1095
  37. package/src/htmlparser.js +1165 -450
  38. package/src/lib/attributes.js +1196 -0
  39. package/src/lib/constants.js +521 -0
  40. package/src/lib/content.js +237 -0
  41. package/src/lib/elements.js +268 -0
  42. package/src/lib/file-pool.js +193 -0
  43. package/src/lib/file-write.js +87 -0
  44. package/src/lib/fragments.js +340 -0
  45. package/src/lib/option-definitions.js +246 -0
  46. package/src/lib/options.js +841 -0
  47. package/src/lib/unused-css.js +499 -0
  48. package/src/lib/urls.js +146 -0
  49. package/src/lib/utils.js +1026 -0
  50. package/src/lib/whitespace.js +431 -0
  51. package/src/presets.js +58 -0
  52. package/src/tokenchain.js +142 -56
  53. package/LICENSE +0 -22
  54. package/sample-cli-config-file.conf +0 -39
  55. package/src/utils.js +0 -18
package/src/tokenchain.js CHANGED
@@ -1,71 +1,157 @@
1
- 'use strict';
1
+ class Sorter {
2
+ constructor() {
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();
7
+ /** @type {Map<string, Sorter | (() => Sorter)>} */
8
+ this.sorterMap = new Map();
9
+ }
10
+
11
+ /** @param {string} token */
12
+ child(token) {
13
+ let child = this.sorterMap.get(token);
14
+ // Nested sorters are built on first use, as most are never reached
15
+ if (typeof child === 'function') {
16
+ child = child();
17
+ this.sorterMap.set(token, child);
18
+ }
19
+ return child;
20
+ }
21
+
22
+ /**
23
+ * @param {string[]} tokens
24
+ * @param {number} fromIndex
25
+ * @returns {string[]}
26
+ */
27
+ sort(tokens, fromIndex = 0) {
28
+ // The present token with the lowest rank comes first—the same choice scanning
29
+ // the keys in order would make
30
+ let best = null;
31
+ let bestRank = Infinity;
32
+ for (let j = fromIndex; j < tokens.length; j++) {
33
+ const rank = this.rank.get(/** @type {string} */ (tokens[j]));
34
+ if (rank !== undefined && rank < bestRank) {
35
+ bestRank = rank;
36
+ best = /** @type {string} */ (tokens[j]);
37
+ }
38
+ }
39
+ if (best === null) return tokens;
40
+
41
+ // Single pass: Count matches and collect non-matches
42
+ let matchCount = 0;
43
+ const others = [];
44
+ for (let j = fromIndex; j < tokens.length; j++) {
45
+ const t = /** @type {string} */ (tokens[j]);
46
+ if (t === best) {
47
+ matchCount++;
48
+ } else {
49
+ others.push(t);
50
+ }
51
+ }
52
+
53
+ // Rebuild: `matchCount` instances of the best token first, then others
54
+ let writeIdx = fromIndex;
55
+ for (let j = 0; j < matchCount; j++) {
56
+ tokens[writeIdx++] = best;
57
+ }
58
+ for (const other of others) {
59
+ tokens[writeIdx++] = other;
60
+ }
2
61
 
3
- function Sorter() {
62
+ return this.child(best)?.sort(tokens, fromIndex + matchCount) ?? tokens;
63
+ }
4
64
  }
5
65
 
6
- Sorter.prototype.sort = function(tokens, fromIndex) {
7
- fromIndex = fromIndex || 0;
8
- for (var i = 0, len = this.keys.length; i < len; i++) {
9
- var key = this.keys[i];
10
- var token = key.slice(1);
11
- var index = tokens.indexOf(token, fromIndex);
12
- if (index !== -1) {
13
- do {
14
- if (index !== fromIndex) {
15
- tokens.splice(index, 1);
16
- tokens.splice(fromIndex, 0, token);
17
- }
18
- fromIndex++;
19
- } while ((index = tokens.indexOf(token, fromIndex)) !== -1);
20
- return this[key].sort(tokens, fromIndex);
66
+ const compareLocale = new Intl.Collator().compare;
67
+
68
+ class TokenChain {
69
+ constructor() {
70
+ // Identical token lists are kept once, with how often they occurred
71
+ /** @type {Map<string, {lists: Map<string, {tokens: string[], weight: number}>, count: number, processed: number}>} */
72
+ this.map = new Map();
73
+ }
74
+
75
+ /**
76
+ * @param {string[]} tokens
77
+ * @param {number} [weight]
78
+ */
79
+ add(tokens, weight = 1) {
80
+ // Tokens hold no whitespace, so a space-joined key is unique
81
+ const key = tokens.join(' ');
82
+ for (const token of tokens) {
83
+ let entry = this.map.get(token);
84
+ if (!entry) {
85
+ entry = { lists: new Map(), count: 0, processed: 0 };
86
+ this.map.set(token, entry);
87
+ }
88
+ const list = entry.lists.get(key);
89
+ if (list) {
90
+ list.weight += weight;
91
+ } else {
92
+ entry.lists.set(key, { tokens, weight });
93
+ }
94
+ entry.count += weight;
21
95
  }
22
96
  }
23
- return tokens;
24
- };
25
97
 
26
- function TokenChain() {
27
- }
98
+ /** @param {Map<string, number>} [localeRanks] */
99
+ createSorter(localeRanks) {
100
+ const sorter = new Sorter();
28
101
 
29
- TokenChain.prototype = {
30
- add: function(tokens) {
31
- var self = this;
32
- tokens.forEach(function(token) {
33
- var key = '$' + token;
34
- if (!self[key]) {
35
- self[key] = [];
36
- self[key].processed = 0;
102
+ // Rank all tokens in locale order once, so nested chains break ties on numbers;
103
+ // tokens the collator deems equal share a rank and keep the stable sort’s order
104
+ if (!localeRanks) {
105
+ localeRanks = new Map();
106
+ const tokens = Array.from(this.map.keys()).sort(compareLocale);
107
+ let rank = 0;
108
+ for (let i = 0; i < tokens.length; i++) {
109
+ const token = /** @type {string} */ (tokens[i]);
110
+ if (i && compareLocale(/** @type {string} */ (tokens[i - 1]), token) !== 0) {
111
+ rank++;
112
+ }
113
+ localeRanks.set(token, rank);
37
114
  }
38
- self[key].push(tokens);
115
+ }
116
+ const ranks = localeRanks;
117
+
118
+ // Sort tokens by usage frequency (descending), then alphabetically
119
+ const entries = Array.from(this.map.entries()).sort((a, b) => {
120
+ const countDiff = b[1].count - a[1].count;
121
+ if (countDiff !== 0) return countDiff;
122
+ return /** @type {number} */ (ranks.get(a[0])) - /** @type {number} */ (ranks.get(b[0]));
39
123
  });
40
- },
41
- createSorter: function() {
42
- var self = this;
43
- var sorter = new Sorter();
44
- sorter.keys = Object.keys(self).sort(function(j, k) {
45
- var m = self[j].length;
46
- var n = self[k].length;
47
- return m < n ? 1 : m > n ? -1 : j < k ? -1 : j > k ? 1 : 0;
48
- }).filter(function(key) {
49
- if (self[key].processed < self[key].length) {
50
- var token = key.slice(1);
51
- var chain = new TokenChain();
52
- self[key].forEach(function(tokens) {
53
- var index;
54
- while ((index = tokens.indexOf(token)) !== -1) {
55
- tokens.splice(index, 1);
124
+
125
+ for (const [token, data] of entries) {
126
+ if (data.processed < data.count) {
127
+ const lists = data.lists;
128
+ for (const { tokens, weight } of lists.values()) {
129
+ for (const t of tokens) {
130
+ if (t !== token) {
131
+ const tData = this.map.get(t);
132
+ if (tData) {
133
+ tData.processed += weight;
134
+ }
135
+ }
136
+ }
137
+ }
138
+
139
+ sorter.rank.set(token, sorter.rank.size);
140
+ sorter.sorterMap.set(token, () => {
141
+ const chain = new TokenChain();
142
+ for (const { tokens, weight } of lists.values()) {
143
+ const filtered = tokens.filter(t => t !== token);
144
+ if (filtered.length > 0) {
145
+ chain.add(filtered, weight);
146
+ }
56
147
  }
57
- tokens.forEach(function(token) {
58
- self['$' + token].processed++;
59
- });
60
- chain.add(tokens.slice(0));
148
+ return chain.createSorter(ranks);
61
149
  });
62
- sorter[key] = chain.createSorter();
63
- return true;
64
150
  }
65
- return false;
66
- });
151
+ }
152
+
67
153
  return sorter;
68
154
  }
69
- };
155
+ }
70
156
 
71
- module.exports = TokenChain;
157
+ export default TokenChain;
package/LICENSE DELETED
@@ -1,22 +0,0 @@
1
- Copyright (c) 2010-2018 Juriy "kangax" Zaytsev
2
-
3
- Permission is hereby granted, free of charge, to any person
4
- obtaining a copy of this software and associated documentation
5
- files (the "Software"), to deal in the Software without
6
- restriction, including without limitation the rights to use,
7
- copy, modify, merge, publish, distribute, sublicense, and/or sell
8
- copies of the Software, and to permit persons to whom the
9
- Software is furnished to do so, subject to the following
10
- conditions:
11
-
12
- The above copyright notice and this permission notice shall be
13
- included in all copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
- OTHER DEALINGS IN THE SOFTWARE.
@@ -1,39 +0,0 @@
1
- {
2
- "caseSensitive": false,
3
- "collapseBooleanAttributes": true,
4
- "collapseInlineTagWhitespace": false,
5
- "collapseWhitespace": true,
6
- "conservativeCollapse": false,
7
- "customAttrCollapse": ".*",
8
- "decodeEntities": true,
9
- "html5": true,
10
- "ignoreCustomFragments": [
11
- "<#[\\s\\S]*?#>",
12
- "<%[\\s\\S]*?%>",
13
- "<\\?[\\s\\S]*?\\?>"
14
- ],
15
- "includeAutoGeneratedTags": false,
16
- "keepClosingSlash": false,
17
- "maxLineLength": 0,
18
- "minifyCSS": true,
19
- "minifyJS": true,
20
- "preserveLineBreaks": false,
21
- "preventAttributesEscaping": false,
22
- "processConditionalComments": true,
23
- "processScripts": [
24
- "text/html"
25
- ],
26
- "removeAttributeQuotes": true,
27
- "removeComments": true,
28
- "removeEmptyAttributes": true,
29
- "removeEmptyElements": true,
30
- "removeOptionalTags": true,
31
- "removeRedundantAttributes": true,
32
- "removeScriptTypeAttributes": true,
33
- "removeStyleLinkTypeAttributes": true,
34
- "removeTagWhitespace": true,
35
- "sortAttributes": true,
36
- "sortClassName": true,
37
- "trimCustomFragments": true,
38
- "useShortDoctype": true
39
- }
package/src/utils.js DELETED
@@ -1,18 +0,0 @@
1
- 'use strict';
2
-
3
- function createMap(values, ignoreCase) {
4
- var map = {};
5
- values.forEach(function(value) {
6
- map[value] = 1;
7
- });
8
- return ignoreCase ? function(value) {
9
- return map[value.toLowerCase()] === 1;
10
- } : function(value) {
11
- return map[value] === 1;
12
- };
13
- }
14
-
15
- exports.createMap = createMap;
16
- exports.createMapFromString = function(values, ignoreCase) {
17
- return createMap(values.split(/,/), ignoreCase);
18
- };