htmlnano 2.1.1 → 2.1.3

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 (186) hide show
  1. package/README.md +53 -12
  2. package/dist/_modules/collapseAttributeWhitespace.d.mts +57 -0
  3. package/dist/_modules/collapseAttributeWhitespace.d.ts +57 -0
  4. package/dist/_modules/collapseAttributeWhitespace.js +296 -0
  5. package/dist/_modules/collapseAttributeWhitespace.mjs +293 -0
  6. package/dist/_modules/collapseBooleanAttributes.d.mts +60 -0
  7. package/dist/_modules/collapseBooleanAttributes.d.ts +60 -0
  8. package/dist/_modules/collapseBooleanAttributes.js +159 -0
  9. package/{lib/modules → dist/_modules}/collapseBooleanAttributes.mjs +39 -57
  10. package/dist/_modules/collapseWhitespace.d.mts +57 -0
  11. package/dist/_modules/collapseWhitespace.d.ts +57 -0
  12. package/dist/_modules/collapseWhitespace.js +172 -0
  13. package/dist/_modules/collapseWhitespace.mjs +170 -0
  14. package/dist/_modules/custom.d.mts +57 -0
  15. package/dist/_modules/custom.d.ts +57 -0
  16. package/dist/_modules/custom.js +22 -0
  17. package/dist/_modules/custom.mjs +20 -0
  18. package/dist/_modules/deduplicateAttributeValues.d.mts +56 -0
  19. package/dist/_modules/deduplicateAttributeValues.d.ts +56 -0
  20. package/dist/_modules/deduplicateAttributeValues.js +38 -0
  21. package/dist/_modules/deduplicateAttributeValues.mjs +36 -0
  22. package/dist/_modules/example.d.mts +59 -0
  23. package/dist/_modules/example.d.ts +59 -0
  24. package/dist/_modules/example.js +67 -0
  25. package/dist/_modules/example.mjs +65 -0
  26. package/dist/_modules/mergeScripts.d.mts +56 -0
  27. package/dist/_modules/mergeScripts.d.ts +56 -0
  28. package/dist/_modules/mergeScripts.js +53 -0
  29. package/dist/_modules/mergeScripts.mjs +51 -0
  30. package/dist/_modules/mergeStyles.d.mts +56 -0
  31. package/dist/_modules/mergeStyles.d.ts +56 -0
  32. package/dist/_modules/mergeStyles.js +42 -0
  33. package/dist/_modules/mergeStyles.mjs +40 -0
  34. package/dist/_modules/minifyConditionalComments.d.mts +56 -0
  35. package/dist/_modules/minifyConditionalComments.d.ts +56 -0
  36. package/dist/_modules/minifyConditionalComments.js +54 -0
  37. package/{lib/modules → dist/_modules}/minifyConditionalComments.mjs +21 -22
  38. package/dist/_modules/minifyCss.d.mts +56 -0
  39. package/dist/_modules/minifyCss.d.ts +56 -0
  40. package/dist/_modules/minifyCss.js +84 -0
  41. package/dist/_modules/minifyCss.mjs +82 -0
  42. package/dist/_modules/minifyJs.d.mts +56 -0
  43. package/dist/_modules/minifyJs.d.ts +56 -0
  44. package/dist/_modules/minifyJs.js +108 -0
  45. package/dist/_modules/minifyJs.mjs +106 -0
  46. package/dist/_modules/minifyJson.d.mts +56 -0
  47. package/dist/_modules/minifyJson.d.ts +56 -0
  48. package/dist/_modules/minifyJson.js +35 -0
  49. package/dist/_modules/minifyJson.mjs +33 -0
  50. package/dist/_modules/minifySvg.d.mts +56 -0
  51. package/dist/_modules/minifySvg.d.ts +56 -0
  52. package/dist/_modules/minifySvg.js +40 -0
  53. package/dist/_modules/minifySvg.mjs +38 -0
  54. package/dist/_modules/minifyUrls.d.mts +56 -0
  55. package/dist/_modules/minifyUrls.d.ts +56 -0
  56. package/dist/_modules/minifyUrls.js +180 -0
  57. package/dist/_modules/minifyUrls.mjs +178 -0
  58. package/dist/_modules/normalizeAttributeValues.d.mts +56 -0
  59. package/dist/_modules/normalizeAttributeValues.d.ts +56 -0
  60. package/dist/_modules/normalizeAttributeValues.js +234 -0
  61. package/dist/_modules/normalizeAttributeValues.mjs +232 -0
  62. package/dist/_modules/removeAttributeQuotes.d.mts +56 -0
  63. package/dist/_modules/removeAttributeQuotes.d.ts +56 -0
  64. package/dist/_modules/removeAttributeQuotes.js +15 -0
  65. package/dist/_modules/removeAttributeQuotes.mjs +13 -0
  66. package/dist/_modules/removeComments.d.mts +58 -0
  67. package/dist/_modules/removeComments.d.ts +58 -0
  68. package/dist/_modules/removeComments.js +83 -0
  69. package/{lib/modules → dist/_modules}/removeComments.mjs +24 -35
  70. package/dist/_modules/removeEmptyAttributes.d.mts +56 -0
  71. package/dist/_modules/removeEmptyAttributes.d.ts +56 -0
  72. package/dist/_modules/removeEmptyAttributes.js +197 -0
  73. package/dist/_modules/removeEmptyAttributes.mjs +195 -0
  74. package/dist/_modules/removeOptionalTags.d.mts +56 -0
  75. package/dist/_modules/removeOptionalTags.d.ts +56 -0
  76. package/dist/_modules/removeOptionalTags.js +190 -0
  77. package/{lib/modules → dist/_modules}/removeOptionalTags.mjs +54 -91
  78. package/dist/_modules/removeRedundantAttributes.d.mts +57 -0
  79. package/dist/_modules/removeRedundantAttributes.d.ts +57 -0
  80. package/dist/_modules/removeRedundantAttributes.js +128 -0
  81. package/{lib/modules → dist/_modules}/removeRedundantAttributes.mjs +43 -59
  82. package/dist/_modules/removeUnusedCss.d.mts +60 -0
  83. package/dist/_modules/removeUnusedCss.d.ts +60 -0
  84. package/dist/_modules/removeUnusedCss.js +134 -0
  85. package/dist/_modules/removeUnusedCss.mjs +132 -0
  86. package/dist/_modules/sortAttributes.d.mts +57 -0
  87. package/dist/_modules/sortAttributes.d.ts +57 -0
  88. package/dist/_modules/sortAttributes.js +102 -0
  89. package/{lib/modules → dist/_modules}/sortAttributes.mjs +39 -60
  90. package/dist/_modules/sortAttributesWithLists.d.mts +56 -0
  91. package/dist/_modules/sortAttributesWithLists.d.ts +56 -0
  92. package/dist/_modules/sortAttributesWithLists.js +118 -0
  93. package/{lib/modules → dist/_modules}/sortAttributesWithLists.mjs +41 -60
  94. package/dist/helpers.js +72 -0
  95. package/dist/helpers.mjs +63 -0
  96. package/dist/index.js +223 -0
  97. package/dist/index.mjs +209 -0
  98. package/dist/presets/ampSafe.js +19 -0
  99. package/{lib → dist}/presets/ampSafe.mjs +6 -4
  100. package/dist/presets/max.js +28 -0
  101. package/{lib → dist}/presets/max.mjs +6 -4
  102. package/dist/presets/safe.js +60 -0
  103. package/{lib → dist}/presets/safe.mjs +13 -20
  104. package/package.json +53 -59
  105. package/.eslintignore +0 -3
  106. package/CHANGELOG.md +0 -398
  107. package/docs/README.md +0 -33
  108. package/docs/babel.config.js +0 -3
  109. package/docs/docs/010-introduction.md +0 -22
  110. package/docs/docs/020-usage.md +0 -117
  111. package/docs/docs/030-config.md +0 -21
  112. package/docs/docs/040-presets.md +0 -75
  113. package/docs/docs/050-modules.md +0 -855
  114. package/docs/docs/060-contribute.md +0 -16
  115. package/docs/docusaurus.config.js +0 -65
  116. package/docs/netlify.toml +0 -4
  117. package/docs/package-lock.json +0 -21796
  118. package/docs/package.json +0 -40
  119. package/docs/sidebars.js +0 -26
  120. package/docs/versioned_docs/version-1.1.1/010-introduction.md +0 -22
  121. package/docs/versioned_docs/version-1.1.1/020-usage.md +0 -77
  122. package/docs/versioned_docs/version-1.1.1/030-config.md +0 -21
  123. package/docs/versioned_docs/version-1.1.1/040-presets.md +0 -75
  124. package/docs/versioned_docs/version-1.1.1/050-modules.md +0 -785
  125. package/docs/versioned_docs/version-1.1.1/060-contribute.md +0 -16
  126. package/docs/versioned_docs/version-2.0.0/010-introduction.md +0 -22
  127. package/docs/versioned_docs/version-2.0.0/020-usage.md +0 -77
  128. package/docs/versioned_docs/version-2.0.0/030-config.md +0 -21
  129. package/docs/versioned_docs/version-2.0.0/040-presets.md +0 -75
  130. package/docs/versioned_docs/version-2.0.0/050-modules.md +0 -838
  131. package/docs/versioned_docs/version-2.0.0/060-contribute.md +0 -16
  132. package/docs/versioned_sidebars/version-1.1.1-sidebars.json +0 -8
  133. package/docs/versioned_sidebars/version-2.0.0-sidebars.json +0 -8
  134. package/docs/versions.json +0 -4
  135. package/index.cjs +0 -11
  136. package/index.d.cts +0 -3
  137. package/index.d.mts +0 -3
  138. package/index.d.ts +0 -93
  139. package/index.mjs +0 -2
  140. package/lib/helpers.cjs +0 -79
  141. package/lib/helpers.mjs +0 -53
  142. package/lib/htmlnano.cjs +0 -200
  143. package/lib/htmlnano.mjs +0 -196
  144. package/lib/modules/collapseAttributeWhitespace.cjs +0 -86
  145. package/lib/modules/collapseAttributeWhitespace.mjs +0 -104
  146. package/lib/modules/collapseBooleanAttributes.cjs +0 -62
  147. package/lib/modules/collapseWhitespace.cjs +0 -100
  148. package/lib/modules/collapseWhitespace.mjs +0 -132
  149. package/lib/modules/custom.cjs +0 -19
  150. package/lib/modules/custom.mjs +0 -16
  151. package/lib/modules/deduplicateAttributeValues.cjs +0 -38
  152. package/lib/modules/deduplicateAttributeValues.mjs +0 -40
  153. package/lib/modules/example.cjs +0 -85
  154. package/lib/modules/example.mjs +0 -75
  155. package/lib/modules/mergeScripts.cjs +0 -54
  156. package/lib/modules/mergeScripts.mjs +0 -56
  157. package/lib/modules/mergeStyles.cjs +0 -38
  158. package/lib/modules/mergeStyles.mjs +0 -36
  159. package/lib/modules/minifyConditionalComments.cjs +0 -47
  160. package/lib/modules/minifyCss.cjs +0 -73
  161. package/lib/modules/minifyCss.mjs +0 -88
  162. package/lib/modules/minifyJs.cjs +0 -103
  163. package/lib/modules/minifyJs.mjs +0 -121
  164. package/lib/modules/minifyJson.cjs +0 -24
  165. package/lib/modules/minifyJson.mjs +0 -21
  166. package/lib/modules/minifySvg.cjs +0 -37
  167. package/lib/modules/minifySvg.mjs +0 -30
  168. package/lib/modules/minifyUrls.cjs +0 -141
  169. package/lib/modules/minifyUrls.mjs +0 -229
  170. package/lib/modules/normalizeAttributeValues.cjs +0 -120
  171. package/lib/modules/normalizeAttributeValues.mjs +0 -140
  172. package/lib/modules/removeAttributeQuotes.cjs +0 -17
  173. package/lib/modules/removeAttributeQuotes.mjs +0 -12
  174. package/lib/modules/removeComments.cjs +0 -86
  175. package/lib/modules/removeEmptyAttributes.cjs +0 -72
  176. package/lib/modules/removeEmptyAttributes.mjs +0 -121
  177. package/lib/modules/removeOptionalTags.cjs +0 -183
  178. package/lib/modules/removeRedundantAttributes.cjs +0 -112
  179. package/lib/modules/removeUnusedCss.cjs +0 -113
  180. package/lib/modules/removeUnusedCss.mjs +0 -122
  181. package/lib/modules/sortAttributes.cjs +0 -99
  182. package/lib/modules/sortAttributesWithLists.cjs +0 -115
  183. package/lib/presets/ampSafe.cjs +0 -18
  184. package/lib/presets/max.cjs +0 -27
  185. package/lib/presets/safe.cjs +0 -65
  186. package/test.js +0 -48
@@ -0,0 +1,102 @@
1
+ Object.defineProperty(exports, '__esModule', { value: true });
2
+
3
+ const validOptions = new Set([
4
+ 'frequency',
5
+ 'alphabetical'
6
+ ]);
7
+ const processModuleOptions = (options)=>{
8
+ if (options === true) return 'alphabetical';
9
+ if (options === false) return false;
10
+ return validOptions.has(options) ? options : false;
11
+ };
12
+ class AttributeTokenChain {
13
+ addFromNodeAttrs(nodeAttrs) {
14
+ Object.keys(nodeAttrs).forEach((attrName)=>{
15
+ const attrNameLower = attrName.toLowerCase();
16
+ if (this.freqData.has(attrNameLower)) {
17
+ this.freqData.set(attrNameLower, this.freqData.get(attrNameLower) + 1);
18
+ } else {
19
+ this.freqData.set(attrNameLower, 1);
20
+ }
21
+ });
22
+ }
23
+ createSortOrder() {
24
+ const _sortOrder = [
25
+ ...this.freqData.entries()
26
+ ];
27
+ _sortOrder.sort((a, b)=>b[1] - a[1]);
28
+ this.sortOrder = _sortOrder.map((i)=>i[0]);
29
+ }
30
+ sortFromNodeAttrs(nodeAttrs) {
31
+ const newAttrs = {};
32
+ // Convert node.attrs attrName into lower case.
33
+ const loweredNodeAttrs = {};
34
+ Object.entries(nodeAttrs).forEach(([attrName, attrValue])=>{
35
+ loweredNodeAttrs[attrName.toLowerCase()] = attrValue;
36
+ });
37
+ if (!this.sortOrder) {
38
+ this.createSortOrder();
39
+ }
40
+ this.sortOrder.forEach((attrNameLower)=>{
41
+ // The attrName inside "sortOrder" has been lowered
42
+ if (loweredNodeAttrs[attrNameLower] != null) {
43
+ newAttrs[attrNameLower] = loweredNodeAttrs[attrNameLower];
44
+ }
45
+ });
46
+ return newAttrs;
47
+ }
48
+ constructor(){
49
+ /** <attr, frequency> */ this.freqData = new Map();
50
+ this.sortOrder = null;
51
+ }
52
+ }
53
+ /** Sort attibutes */ const mod = {
54
+ default (tree, options, moduleOptions) {
55
+ const sortType = processModuleOptions(moduleOptions);
56
+ if (sortType === 'alphabetical') {
57
+ return sortAttributesInAlphabeticalOrder(tree);
58
+ }
59
+ if (sortType === 'frequency') {
60
+ return sortAttributesByFrequency(tree);
61
+ }
62
+ // Invalid configuration
63
+ return tree;
64
+ }
65
+ };
66
+ function sortAttributesInAlphabeticalOrder(tree) {
67
+ tree.walk((node)=>{
68
+ if (!node.attrs) {
69
+ return node;
70
+ }
71
+ const newAttrs = {};
72
+ Object.keys(node.attrs)// @ts-expect-error -- deliberately use minus operator to sort things
73
+ .sort((a, b)=>typeof a.localeCompare === 'function' ? a.localeCompare(b) : a - b).forEach((attr)=>{
74
+ newAttrs[attr] = node.attrs[attr];
75
+ });
76
+ node.attrs = newAttrs;
77
+ return node;
78
+ });
79
+ return tree;
80
+ }
81
+ function sortAttributesByFrequency(tree) {
82
+ const tokenchain = new AttributeTokenChain();
83
+ // Traverse through tree to get frequency
84
+ tree.walk((node)=>{
85
+ if (!node.attrs) {
86
+ return node;
87
+ }
88
+ tokenchain.addFromNodeAttrs(node.attrs);
89
+ return node;
90
+ });
91
+ // Traverse through tree again, this time sort the attributes
92
+ tree.walk((node)=>{
93
+ if (!node.attrs) {
94
+ return node;
95
+ }
96
+ node.attrs = tokenchain.sortFromNodeAttrs(node.attrs);
97
+ return node;
98
+ });
99
+ return tree;
100
+ }
101
+
102
+ exports.default = mod;
@@ -1,22 +1,16 @@
1
- import { sort as timSort } from 'timsort';
2
-
3
- const validOptions = new Set(['frequency', 'alphabetical']);
4
-
5
- const processModuleOptions = options => {
1
+ const validOptions = new Set([
2
+ 'frequency',
3
+ 'alphabetical'
4
+ ]);
5
+ const processModuleOptions = (options)=>{
6
6
  if (options === true) return 'alphabetical';
7
-
7
+ if (options === false) return false;
8
8
  return validOptions.has(options) ? options : false;
9
9
  };
10
-
11
10
  class AttributeTokenChain {
12
- constructor() {
13
- this.freqData = new Map(); // <attr, frequency>[]
14
- }
15
-
16
11
  addFromNodeAttrs(nodeAttrs) {
17
- Object.keys(nodeAttrs).forEach(attrName => {
12
+ Object.keys(nodeAttrs).forEach((attrName)=>{
18
13
  const attrNameLower = attrName.toLowerCase();
19
-
20
14
  if (this.freqData.has(attrNameLower)) {
21
15
  this.freqData.set(attrNameLower, this.freqData.get(attrNameLower) + 1);
22
16
  } else {
@@ -24,98 +18,83 @@ class AttributeTokenChain {
24
18
  }
25
19
  });
26
20
  }
27
-
28
21
  createSortOrder() {
29
- let _sortOrder = [...this.freqData.entries()];
30
- timSort(_sortOrder, (a, b) => b[1] - a[1]);
31
-
32
- this.sortOrder = _sortOrder.map(i => i[0]);
22
+ const _sortOrder = [
23
+ ...this.freqData.entries()
24
+ ];
25
+ _sortOrder.sort((a, b)=>b[1] - a[1]);
26
+ this.sortOrder = _sortOrder.map((i)=>i[0]);
33
27
  }
34
-
35
28
  sortFromNodeAttrs(nodeAttrs) {
36
29
  const newAttrs = {};
37
-
38
30
  // Convert node.attrs attrName into lower case.
39
31
  const loweredNodeAttrs = {};
40
- Object.entries(nodeAttrs).forEach(([attrName, attrValue]) => {
32
+ Object.entries(nodeAttrs).forEach(([attrName, attrValue])=>{
41
33
  loweredNodeAttrs[attrName.toLowerCase()] = attrValue;
42
34
  });
43
-
44
35
  if (!this.sortOrder) {
45
36
  this.createSortOrder();
46
37
  }
47
-
48
- this.sortOrder.forEach(attrNameLower => {
38
+ this.sortOrder.forEach((attrNameLower)=>{
49
39
  // The attrName inside "sortOrder" has been lowered
50
40
  if (loweredNodeAttrs[attrNameLower] != null) {
51
41
  newAttrs[attrNameLower] = loweredNodeAttrs[attrNameLower];
52
42
  }
53
43
  });
54
-
55
44
  return newAttrs;
56
45
  }
57
- }
58
-
59
- /** Sort attibutes */
60
- export default function sortAttributes(tree, options, moduleOptions) {
61
- const sortType = processModuleOptions(moduleOptions);
62
-
63
- if (sortType === 'alphabetical') {
64
- return sortAttributesInAlphabeticalOrder(tree);
65
- }
66
-
67
- if (sortType === 'frequency') {
68
- return sortAttributesByFrequency(tree);
46
+ constructor(){
47
+ /** <attr, frequency> */ this.freqData = new Map();
48
+ this.sortOrder = null;
69
49
  }
70
-
71
- // Invalid configuration
72
- return tree;
73
50
  }
74
-
51
+ /** Sort attibutes */ const mod = {
52
+ default (tree, options, moduleOptions) {
53
+ const sortType = processModuleOptions(moduleOptions);
54
+ if (sortType === 'alphabetical') {
55
+ return sortAttributesInAlphabeticalOrder(tree);
56
+ }
57
+ if (sortType === 'frequency') {
58
+ return sortAttributesByFrequency(tree);
59
+ }
60
+ // Invalid configuration
61
+ return tree;
62
+ }
63
+ };
75
64
  function sortAttributesInAlphabeticalOrder(tree) {
76
- tree.walk(node => {
65
+ tree.walk((node)=>{
77
66
  if (!node.attrs) {
78
67
  return node;
79
68
  }
80
-
81
69
  const newAttrs = {};
82
-
83
- Object.keys(node.attrs)
84
- .sort((a, b) => typeof a.localeCompare === 'function' ? a.localeCompare(b) : a - b)
85
- .forEach(attr => newAttrs[attr] = node.attrs[attr]);
86
-
70
+ Object.keys(node.attrs)// @ts-expect-error -- deliberately use minus operator to sort things
71
+ .sort((a, b)=>typeof a.localeCompare === 'function' ? a.localeCompare(b) : a - b).forEach((attr)=>{
72
+ newAttrs[attr] = node.attrs[attr];
73
+ });
87
74
  node.attrs = newAttrs;
88
-
89
75
  return node;
90
76
  });
91
-
92
77
  return tree;
93
78
  }
94
-
95
79
  function sortAttributesByFrequency(tree) {
96
80
  const tokenchain = new AttributeTokenChain();
97
-
98
81
  // Traverse through tree to get frequency
99
- tree.walk(node => {
82
+ tree.walk((node)=>{
100
83
  if (!node.attrs) {
101
84
  return node;
102
85
  }
103
-
104
86
  tokenchain.addFromNodeAttrs(node.attrs);
105
-
106
87
  return node;
107
88
  });
108
-
109
89
  // Traverse through tree again, this time sort the attributes
110
- tree.walk(node => {
90
+ tree.walk((node)=>{
111
91
  if (!node.attrs) {
112
92
  return node;
113
93
  }
114
-
115
94
  node.attrs = tokenchain.sortFromNodeAttrs(node.attrs);
116
-
117
95
  return node;
118
96
  });
119
-
120
97
  return tree;
121
98
  }
99
+
100
+ export { mod as default };
@@ -0,0 +1,56 @@
1
+ import PostHTML from 'posthtml';
2
+ import { MinifyOptions } from 'terser';
3
+ import { Options } from 'cssnano';
4
+ import { Config } from 'svgo';
5
+
6
+ type PostHTMLTreeLike = [PostHTML.Node] & PostHTML.NodeAPI & {
7
+ options?: {
8
+ quoteAllAttributes?: boolean | undefined;
9
+ } | undefined;
10
+ render(): string;
11
+ render(node: PostHTML.Node | PostHTMLTreeLike, renderOptions?: any): string;
12
+ };
13
+ type MaybeArray<T> = T | Array<T>;
14
+ type PostHTMLNodeLike = PostHTML.Node | string;
15
+ interface HtmlnanoOptions {
16
+ skipConfigLoading?: boolean;
17
+ skipInternalWarnings?: boolean;
18
+ collapseAttributeWhitespace?: boolean;
19
+ collapseBooleanAttributes?: {
20
+ amphtml?: boolean;
21
+ };
22
+ collapseWhitespace?: 'conservative' | 'all' | 'aggressive';
23
+ custom?: MaybeArray<(tree: PostHTMLTreeLike, options?: any) => (PostHTML.Node | PostHTMLTreeLike)>;
24
+ deduplicateAttributeValues?: boolean;
25
+ minifyUrls?: URL | string | false;
26
+ mergeStyles?: boolean;
27
+ mergeScripts?: boolean;
28
+ minifyCss?: Options | boolean;
29
+ minifyConditionalComments?: boolean;
30
+ minifyJs?: MinifyOptions | boolean;
31
+ minifyJson?: boolean;
32
+ minifySvg?: Config | boolean;
33
+ normalizeAttributeValues?: boolean;
34
+ removeAttributeQuotes?: boolean;
35
+ removeComments?: boolean | 'safe' | 'all' | RegExp | ((comment: string) => boolean);
36
+ removeEmptyAttributes?: boolean;
37
+ removeRedundantAttributes?: boolean;
38
+ removeOptionalTags?: boolean;
39
+ removeUnusedCss?: boolean;
40
+ sortAttributes?: boolean | 'alphabetical' | 'frequency';
41
+ sortAttributesWithLists?: boolean | 'alphabetical' | 'frequency';
42
+ }
43
+ type HtmlnanoModuleAttrsHandler = (attrs: Record<string, string | boolean | void>, node: PostHTML.Node) => Record<string, string | boolean | void>;
44
+ type HtmlnanoModuleContentHandler = (content: Array<PostHTMLNodeLike>, node: PostHTML.Node) => MaybeArray<PostHTMLNodeLike>;
45
+ type HtmlnanoModuleNodeHandler = (node: PostHTMLNodeLike) => PostHTML.Node | string;
46
+ type OptionalOptions<T> = T extends boolean | string | Function | number | null | undefined ? T : T extends object ? Partial<T> : T;
47
+ type HtmlnanoModule<Options = any> = {
48
+ onAttrs?: (options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => HtmlnanoModuleAttrsHandler;
49
+ onContent?: (options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => HtmlnanoModuleContentHandler;
50
+ onNode?: (options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => HtmlnanoModuleNodeHandler;
51
+ default?: (tree: PostHTMLTreeLike, options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => PostHTMLTreeLike | Promise<PostHTMLTreeLike>;
52
+ };
53
+
54
+ declare const mod: HtmlnanoModule<boolean | 'alphabetical' | 'frequency'>;
55
+
56
+ export { mod as default };
@@ -0,0 +1,56 @@
1
+ import PostHTML from 'posthtml';
2
+ import { MinifyOptions } from 'terser';
3
+ import { Options } from 'cssnano';
4
+ import { Config } from 'svgo';
5
+
6
+ type PostHTMLTreeLike = [PostHTML.Node] & PostHTML.NodeAPI & {
7
+ options?: {
8
+ quoteAllAttributes?: boolean | undefined;
9
+ } | undefined;
10
+ render(): string;
11
+ render(node: PostHTML.Node | PostHTMLTreeLike, renderOptions?: any): string;
12
+ };
13
+ type MaybeArray<T> = T | Array<T>;
14
+ type PostHTMLNodeLike = PostHTML.Node | string;
15
+ interface HtmlnanoOptions {
16
+ skipConfigLoading?: boolean;
17
+ skipInternalWarnings?: boolean;
18
+ collapseAttributeWhitespace?: boolean;
19
+ collapseBooleanAttributes?: {
20
+ amphtml?: boolean;
21
+ };
22
+ collapseWhitespace?: 'conservative' | 'all' | 'aggressive';
23
+ custom?: MaybeArray<(tree: PostHTMLTreeLike, options?: any) => (PostHTML.Node | PostHTMLTreeLike)>;
24
+ deduplicateAttributeValues?: boolean;
25
+ minifyUrls?: URL | string | false;
26
+ mergeStyles?: boolean;
27
+ mergeScripts?: boolean;
28
+ minifyCss?: Options | boolean;
29
+ minifyConditionalComments?: boolean;
30
+ minifyJs?: MinifyOptions | boolean;
31
+ minifyJson?: boolean;
32
+ minifySvg?: Config | boolean;
33
+ normalizeAttributeValues?: boolean;
34
+ removeAttributeQuotes?: boolean;
35
+ removeComments?: boolean | 'safe' | 'all' | RegExp | ((comment: string) => boolean);
36
+ removeEmptyAttributes?: boolean;
37
+ removeRedundantAttributes?: boolean;
38
+ removeOptionalTags?: boolean;
39
+ removeUnusedCss?: boolean;
40
+ sortAttributes?: boolean | 'alphabetical' | 'frequency';
41
+ sortAttributesWithLists?: boolean | 'alphabetical' | 'frequency';
42
+ }
43
+ type HtmlnanoModuleAttrsHandler = (attrs: Record<string, string | boolean | void>, node: PostHTML.Node) => Record<string, string | boolean | void>;
44
+ type HtmlnanoModuleContentHandler = (content: Array<PostHTMLNodeLike>, node: PostHTML.Node) => MaybeArray<PostHTMLNodeLike>;
45
+ type HtmlnanoModuleNodeHandler = (node: PostHTMLNodeLike) => PostHTML.Node | string;
46
+ type OptionalOptions<T> = T extends boolean | string | Function | number | null | undefined ? T : T extends object ? Partial<T> : T;
47
+ type HtmlnanoModule<Options = any> = {
48
+ onAttrs?: (options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => HtmlnanoModuleAttrsHandler;
49
+ onContent?: (options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => HtmlnanoModuleContentHandler;
50
+ onNode?: (options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => HtmlnanoModuleNodeHandler;
51
+ default?: (tree: PostHTMLTreeLike, options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => PostHTMLTreeLike | Promise<PostHTMLTreeLike>;
52
+ };
53
+
54
+ declare const mod: HtmlnanoModule<boolean | 'alphabetical' | 'frequency'>;
55
+
56
+ export { mod as default };
@@ -0,0 +1,118 @@
1
+ Object.defineProperty(exports, '__esModule', { value: true });
2
+
3
+ var collapseAttributeWhitespace_js = require('./collapseAttributeWhitespace.js');
4
+
5
+ // class, rel, ping
6
+ const validOptions = new Set([
7
+ 'frequency',
8
+ 'alphabetical'
9
+ ]);
10
+ const processModuleOptions = (options)=>{
11
+ if (options === true) return 'alphabetical';
12
+ if (options === false) return false;
13
+ return validOptions.has(options) ? options : false;
14
+ };
15
+ class AttributeTokenChain {
16
+ addFromNodeAttrsArray(attrValuesArray) {
17
+ attrValuesArray.forEach((attrValue)=>{
18
+ if (this.freqData.has(attrValue)) {
19
+ this.freqData.set(attrValue, this.freqData.get(attrValue) + 1);
20
+ } else {
21
+ this.freqData.set(attrValue, 1);
22
+ }
23
+ });
24
+ }
25
+ createSortOrder() {
26
+ const _sortOrder = [
27
+ ...this.freqData.entries()
28
+ ];
29
+ _sortOrder.sort((a, b)=>b[1] - a[1]);
30
+ this.sortOrder = _sortOrder.map((i)=>i[0]);
31
+ }
32
+ sortFromNodeAttrsArray(attrValuesArray) {
33
+ const resultArray = [];
34
+ if (!this.sortOrder) {
35
+ this.createSortOrder();
36
+ }
37
+ this.sortOrder.forEach((k)=>{
38
+ if (attrValuesArray.includes(k)) {
39
+ resultArray.push(k);
40
+ }
41
+ });
42
+ return resultArray;
43
+ }
44
+ constructor(){
45
+ /** <attr, frequency> */ this.freqData = new Map();
46
+ this.sortOrder = null;
47
+ }
48
+ }
49
+ /** Sort values inside list-like attributes (e.g. class, rel) */ const mod = {
50
+ default (tree, options, moduleOptions) {
51
+ const sortType = processModuleOptions(moduleOptions);
52
+ if (sortType === 'alphabetical') {
53
+ return sortAttributesWithListsInAlphabeticalOrder(tree);
54
+ }
55
+ if (sortType === 'frequency') {
56
+ return sortAttributesWithListsByFrequency(tree);
57
+ }
58
+ // Invalid configuration
59
+ return tree;
60
+ }
61
+ };
62
+ function sortAttributesWithListsInAlphabeticalOrder(tree) {
63
+ tree.walk((node)=>{
64
+ if (!node.attrs) {
65
+ return node;
66
+ }
67
+ Object.keys(node.attrs).forEach((attrName)=>{
68
+ const attrNameLower = attrName.toLowerCase();
69
+ if (!collapseAttributeWhitespace_js.attributesWithLists.has(attrNameLower)) {
70
+ return;
71
+ }
72
+ const attrValues = node.attrs[attrName].split(/\s/);
73
+ node.attrs[attrName] = attrValues.sort((a, b)=>{
74
+ // @ts-expect-error -- deliberately use minus operator to sort things
75
+ return typeof a.localeCompare === 'function' ? a.localeCompare(b) : a - b;
76
+ }).join(' ');
77
+ });
78
+ return node;
79
+ });
80
+ return tree;
81
+ }
82
+ function sortAttributesWithListsByFrequency(tree) {
83
+ const tokenChainObj = {}; // <attrNameLower: AttributeTokenChain>[]
84
+ // Traverse through tree to get frequency
85
+ tree.walk((node)=>{
86
+ if (!node.attrs) {
87
+ return node;
88
+ }
89
+ Object.entries(node.attrs).forEach(([attrName, attrValues])=>{
90
+ const attrNameLower = attrName.toLowerCase();
91
+ if (!collapseAttributeWhitespace_js.attributesWithLists.has(attrNameLower)) {
92
+ return;
93
+ }
94
+ tokenChainObj[attrNameLower] = tokenChainObj[attrNameLower] || new AttributeTokenChain();
95
+ tokenChainObj[attrNameLower].addFromNodeAttrsArray(attrValues.split(/\s/));
96
+ });
97
+ return node;
98
+ });
99
+ // Traverse through tree again, this time sort the attribute values
100
+ tree.walk((node)=>{
101
+ if (!node.attrs) {
102
+ return node;
103
+ }
104
+ Object.entries(node.attrs).forEach(([attrName, attrValues])=>{
105
+ const attrNameLower = attrName.toLowerCase();
106
+ if (!collapseAttributeWhitespace_js.attributesWithLists.has(attrNameLower)) {
107
+ return;
108
+ }
109
+ if (tokenChainObj[attrNameLower]) {
110
+ node.attrs[attrName] = tokenChainObj[attrNameLower].sortFromNodeAttrsArray(attrValues.split(/\s/)).join(' ');
111
+ }
112
+ });
113
+ return node;
114
+ });
115
+ return tree;
116
+ }
117
+
118
+ exports.default = mod;
@@ -1,21 +1,18 @@
1
- // class, rel, ping
2
- import { sort as timSort } from 'timsort';
3
1
  import { attributesWithLists } from './collapseAttributeWhitespace.mjs';
4
2
 
5
- const validOptions = new Set(['frequency', 'alphabetical']);
6
- const processModuleOptions = options => {
3
+ // class, rel, ping
4
+ const validOptions = new Set([
5
+ 'frequency',
6
+ 'alphabetical'
7
+ ]);
8
+ const processModuleOptions = (options)=>{
7
9
  if (options === true) return 'alphabetical';
8
-
10
+ if (options === false) return false;
9
11
  return validOptions.has(options) ? options : false;
10
12
  };
11
-
12
13
  class AttributeTokenChain {
13
- constructor() {
14
- this.freqData = new Map(); // <attrValue, frequency>[]
15
- }
16
-
17
14
  addFromNodeAttrsArray(attrValuesArray) {
18
- attrValuesArray.forEach(attrValue => {
15
+ attrValuesArray.forEach((attrValue)=>{
19
16
  if (this.freqData.has(attrValue)) {
20
17
  this.freqData.set(attrValue, this.freqData.get(attrValue) + 1);
21
18
  } else {
@@ -23,113 +20,97 @@ class AttributeTokenChain {
23
20
  }
24
21
  });
25
22
  }
26
-
27
23
  createSortOrder() {
28
- let _sortOrder = [...this.freqData.entries()];
29
- timSort(_sortOrder, (a, b) => b[1] - a[1]);
30
-
31
- this.sortOrder = _sortOrder.map(i => i[0]);
24
+ const _sortOrder = [
25
+ ...this.freqData.entries()
26
+ ];
27
+ _sortOrder.sort((a, b)=>b[1] - a[1]);
28
+ this.sortOrder = _sortOrder.map((i)=>i[0]);
32
29
  }
33
-
34
30
  sortFromNodeAttrsArray(attrValuesArray) {
35
31
  const resultArray = [];
36
-
37
32
  if (!this.sortOrder) {
38
33
  this.createSortOrder();
39
34
  }
40
-
41
- this.sortOrder.forEach(k => {
35
+ this.sortOrder.forEach((k)=>{
42
36
  if (attrValuesArray.includes(k)) {
43
37
  resultArray.push(k);
44
38
  }
45
39
  });
46
-
47
40
  return resultArray;
48
41
  }
49
- }
50
-
51
- /** Sort values inside list-like attributes (e.g. class, rel) */
52
- export default function collapseAttributeWhitespace(tree, options, moduleOptions) {
53
- const sortType = processModuleOptions(moduleOptions);
54
-
55
- if (sortType === 'alphabetical') {
56
- return sortAttributesWithListsInAlphabeticalOrder(tree);
57
- }
58
-
59
- if (sortType === 'frequency') {
60
- return sortAttributesWithListsByFrequency(tree);
42
+ constructor(){
43
+ /** <attr, frequency> */ this.freqData = new Map();
44
+ this.sortOrder = null;
61
45
  }
62
-
63
- // Invalid configuration
64
- return tree;
65
46
  }
66
-
47
+ /** Sort values inside list-like attributes (e.g. class, rel) */ const mod = {
48
+ default (tree, options, moduleOptions) {
49
+ const sortType = processModuleOptions(moduleOptions);
50
+ if (sortType === 'alphabetical') {
51
+ return sortAttributesWithListsInAlphabeticalOrder(tree);
52
+ }
53
+ if (sortType === 'frequency') {
54
+ return sortAttributesWithListsByFrequency(tree);
55
+ }
56
+ // Invalid configuration
57
+ return tree;
58
+ }
59
+ };
67
60
  function sortAttributesWithListsInAlphabeticalOrder(tree) {
68
- tree.walk(node => {
61
+ tree.walk((node)=>{
69
62
  if (!node.attrs) {
70
63
  return node;
71
64
  }
72
-
73
- Object.keys(node.attrs).forEach(attrName => {
65
+ Object.keys(node.attrs).forEach((attrName)=>{
74
66
  const attrNameLower = attrName.toLowerCase();
75
67
  if (!attributesWithLists.has(attrNameLower)) {
76
68
  return;
77
69
  }
78
-
79
70
  const attrValues = node.attrs[attrName].split(/\s/);
80
-
81
- node.attrs[attrName] = attrValues.sort((a, b) => {
71
+ node.attrs[attrName] = attrValues.sort((a, b)=>{
72
+ // @ts-expect-error -- deliberately use minus operator to sort things
82
73
  return typeof a.localeCompare === 'function' ? a.localeCompare(b) : a - b;
83
74
  }).join(' ');
84
75
  });
85
-
86
76
  return node;
87
77
  });
88
-
89
78
  return tree;
90
79
  }
91
-
92
80
  function sortAttributesWithListsByFrequency(tree) {
93
81
  const tokenChainObj = {}; // <attrNameLower: AttributeTokenChain>[]
94
-
95
82
  // Traverse through tree to get frequency
96
- tree.walk(node => {
83
+ tree.walk((node)=>{
97
84
  if (!node.attrs) {
98
85
  return node;
99
86
  }
100
-
101
- Object.entries(node.attrs).forEach(([attrName, attrValues]) => {
87
+ Object.entries(node.attrs).forEach(([attrName, attrValues])=>{
102
88
  const attrNameLower = attrName.toLowerCase();
103
-
104
89
  if (!attributesWithLists.has(attrNameLower)) {
105
90
  return;
106
91
  }
107
-
108
92
  tokenChainObj[attrNameLower] = tokenChainObj[attrNameLower] || new AttributeTokenChain();
109
93
  tokenChainObj[attrNameLower].addFromNodeAttrsArray(attrValues.split(/\s/));
110
94
  });
111
-
112
95
  return node;
113
96
  });
114
-
115
97
  // Traverse through tree again, this time sort the attribute values
116
- tree.walk(node => {
98
+ tree.walk((node)=>{
117
99
  if (!node.attrs) {
118
100
  return node;
119
101
  }
120
-
121
- Object.entries(node.attrs).forEach(([attrName, attrValues]) => {
102
+ Object.entries(node.attrs).forEach(([attrName, attrValues])=>{
122
103
  const attrNameLower = attrName.toLowerCase();
123
-
124
104
  if (!attributesWithLists.has(attrNameLower)) {
125
105
  return;
126
106
  }
127
-
128
107
  if (tokenChainObj[attrNameLower]) {
129
108
  node.attrs[attrName] = tokenChainObj[attrNameLower].sortFromNodeAttrsArray(attrValues.split(/\s/)).join(' ');
130
109
  }
131
110
  });
132
-
133
111
  return node;
134
112
  });
113
+ return tree;
135
114
  }
115
+
116
+ export { mod as default };