htmlnano 2.1.2 → 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 -58
  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 -59
  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 +48 -56
  105. package/.eslintignore +0 -3
  106. package/CHANGELOG.md +0 -409
  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 -21630
  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 -94
  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 -202
  143. package/lib/htmlnano.mjs +0 -198
  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 -98
  182. package/lib/modules/sortAttributesWithLists.cjs +0 -114
  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 -23
@@ -1,225 +1,188 @@
1
1
  import { isComment } from '../helpers.mjs';
2
2
 
3
3
  const startWithWhitespacePattern = /^\s+/;
4
-
5
- const bodyStartTagCantBeOmittedWithFirstChildTags = new Set(['meta', 'link', 'script', 'style']);
6
- const tbodyStartTagCantBeOmittedWithPrecededTags = new Set(['tbody', 'thead', 'tfoot']);
7
- const tbodyEndTagCantBeOmittedWithFollowedTags = new Set(['tbody', 'tfoot']);
8
-
4
+ const bodyStartTagCantBeOmittedWithFirstChildTags = new Set([
5
+ 'meta',
6
+ 'link',
7
+ 'script',
8
+ 'style'
9
+ ]);
10
+ const tbodyStartTagCantBeOmittedWithPrecededTags = new Set([
11
+ 'tbody',
12
+ 'thead',
13
+ 'tfoot'
14
+ ]);
15
+ const tbodyEndTagCantBeOmittedWithFollowedTags = new Set([
16
+ 'tbody',
17
+ 'tfoot'
18
+ ]);
9
19
  function isEmptyTextNode(node) {
10
20
  if (typeof node === 'string' && node.trim() === '') {
11
21
  return true;
12
22
  }
13
-
14
23
  return false;
15
24
  }
16
-
17
25
  function isEmptyNode(node) {
18
26
  if (!node.content) {
19
27
  return true;
20
28
  }
21
-
22
29
  if (node.content.length) {
23
- return !node.content.filter(n => typeof n === 'string' && isEmptyTextNode(n) ? false : true).length;
30
+ return !node.content.filter((n)=>typeof n === 'string' && isEmptyTextNode(n) ? false : true).length;
24
31
  }
25
-
26
32
  return true;
27
33
  }
28
-
29
34
  function getFirstChildTag(node, nonEmpty = true) {
30
35
  if (node.content && node.content.length) {
31
36
  if (nonEmpty) {
32
- for (const childNode of node.content) {
33
- if (childNode.tag) return childNode;
34
- if (typeof childNode === 'string' && !isEmptyTextNode(childNode)) return childNode;
37
+ for (const childNode of node.content){
38
+ if (typeof childNode !== 'string') return childNode;
39
+ if (!isEmptyTextNode(childNode)) return childNode;
35
40
  }
36
41
  } else {
37
42
  return node.content[0] || null;
38
43
  }
39
44
  }
40
-
41
45
  return null;
42
46
  }
43
-
44
47
  function getPrevNode(tree, currentNodeIndex, nonEmpty = false) {
45
48
  if (nonEmpty) {
46
- for (let i = currentNodeIndex - 1; i >= 0; i--) {
49
+ for(let i = currentNodeIndex - 1; i >= 0; i--){
47
50
  const node = tree[i];
48
- if (node.tag) return node;
49
- if (typeof node === 'string' && !isEmptyTextNode(node)) return node;
51
+ if (typeof node !== 'string' && node.tag) return node;
52
+ if (!isEmptyTextNode(node)) return node;
50
53
  }
51
54
  } else {
52
55
  return tree[currentNodeIndex - 1] || null;
53
56
  }
54
-
55
57
  return null;
56
58
  }
57
-
58
59
  function getNextNode(tree, currentNodeIndex, nonEmpty = false) {
59
60
  if (nonEmpty) {
60
- for (let i = currentNodeIndex + 1; i < tree.length; i++) {
61
+ for(let i = currentNodeIndex + 1; i < tree.length; i++){
61
62
  const node = tree[i];
62
- if (node.tag) return node;
63
- if (typeof node === 'string' && !isEmptyTextNode(node)) return node;
63
+ if (typeof node !== 'string') return node;
64
+ if (!isEmptyTextNode(node)) return node;
64
65
  }
65
66
  } else {
66
67
  return tree[currentNodeIndex + 1] || null;
67
68
  }
68
-
69
69
  return null;
70
70
  }
71
-
72
- // Specification https://html.spec.whatwg.org/multipage/syntax.html#optional-tags
73
- /** Remove optional tag in the DOM */
74
- export default function removeOptionalTags(tree) {
75
- tree.forEach((node, index) => {
71
+ function removeOptionalTags(tree) {
72
+ tree.forEach((node, index)=>{
73
+ if (typeof node === 'string') return node;
76
74
  if (!node.tag) return node;
77
-
78
75
  if (node.attrs && Object.keys(node.attrs).length) return node;
79
-
80
76
  // const prevNode = getPrevNode(tree, index);
81
77
  const prevNonEmptyNode = getPrevNode(tree, index, true);
82
78
  const nextNode = getNextNode(tree, index);
83
79
  const nextNonEmptyNode = getNextNode(tree, index, true);
84
80
  const firstChildNode = getFirstChildTag(node, false);
85
81
  const firstNonEmptyChildNode = getFirstChildTag(node);
86
-
87
82
  /**
88
83
  * An "html" element's start tag may be omitted if the first thing inside the "html" element is not a comment.
89
84
  * An "html" element's end tag may be omitted if the "html" element is not IMMEDIATELY followed by a comment.
90
- */
91
- if (node.tag === 'html') {
85
+ */ if (node.tag === 'html') {
92
86
  let isHtmlStartTagCanBeOmitted = true;
93
87
  let isHtmlEndTagCanBeOmitted = true;
94
-
95
88
  if (typeof firstNonEmptyChildNode === 'string' && isComment(firstNonEmptyChildNode)) {
96
89
  isHtmlStartTagCanBeOmitted = false;
97
90
  }
98
-
99
91
  if (typeof nextNonEmptyNode === 'string' && isComment(nextNonEmptyNode)) {
100
92
  isHtmlEndTagCanBeOmitted = false;
101
93
  }
102
-
103
94
  if (isHtmlStartTagCanBeOmitted && isHtmlEndTagCanBeOmitted) {
95
+ // @ts-expect-error -- deliberately set tag to false
104
96
  node.tag = false;
105
97
  }
106
98
  }
107
-
108
99
  /**
109
100
  * A "head" element's start tag may be omitted if the element is empty, or if the first thing inside the "head" element is an element.
110
101
  * A "head" element's end tag may be omitted if the "head" element is not IMMEDIATELY followed by ASCII whitespace or a comment.
111
- */
112
- if (node.tag === 'head') {
102
+ */ if (node.tag === 'head') {
113
103
  let isHeadStartTagCanBeOmitted = false;
114
104
  let isHeadEndTagCanBeOmitted = true;
115
-
116
- if (
117
- isEmptyNode(node) ||
118
- firstNonEmptyChildNode && firstNonEmptyChildNode.tag
119
- ) {
105
+ if (isEmptyNode(node) || firstNonEmptyChildNode && typeof firstNonEmptyChildNode === 'object' && firstNonEmptyChildNode.tag) {
120
106
  isHeadStartTagCanBeOmitted = true;
121
107
  }
122
-
123
- if (
124
- (nextNode && typeof nextNode === 'string' && startWithWhitespacePattern.test(nextNode)) ||
125
- (nextNonEmptyNode && typeof nextNonEmptyNode === 'string' && isComment(nextNode))
126
- ) {
108
+ if (nextNode && typeof nextNode === 'string' && startWithWhitespacePattern.test(nextNode) || nextNonEmptyNode && typeof nextNonEmptyNode === 'string' && isComment(nextNode)) {
127
109
  isHeadEndTagCanBeOmitted = false;
128
110
  }
129
-
130
111
  if (isHeadStartTagCanBeOmitted && isHeadEndTagCanBeOmitted) {
112
+ // @ts-expect-error -- deliberately set tag to false
131
113
  node.tag = false;
132
114
  }
133
115
  }
134
-
135
-
136
116
  /**
137
117
  * A "body" element's start tag may be omitted if the element is empty, or if the first thing inside the "body" element is not ASCII whitespace or a comment, except if the first thing inside the "body" element is a "meta", "link", "script", "style", or "template" element.
138
118
  * A "body" element's end tag may be omitted if the "body" element is not IMMEDIATELY followed by a comment.
139
- */
140
- if (node.tag === 'body') {
119
+ */ if (node.tag === 'body') {
141
120
  let isBodyStartTagCanBeOmitted = true;
142
121
  let isBodyEndTagCanBeOmitted = true;
143
-
144
- if (
145
- (typeof firstChildNode === 'string' && startWithWhitespacePattern.test(firstChildNode)) ||
146
- (typeof firstNonEmptyChildNode === 'string' && isComment(firstNonEmptyChildNode))
147
- ) {
122
+ if (typeof firstChildNode === 'string' && startWithWhitespacePattern.test(firstChildNode) || typeof firstNonEmptyChildNode === 'string' && isComment(firstNonEmptyChildNode)) {
148
123
  isBodyStartTagCanBeOmitted = false;
149
124
  }
150
-
151
- if (firstNonEmptyChildNode && firstNonEmptyChildNode.tag && bodyStartTagCantBeOmittedWithFirstChildTags.has(firstNonEmptyChildNode.tag)) {
125
+ if (firstNonEmptyChildNode && typeof firstNonEmptyChildNode === 'object' && firstNonEmptyChildNode.tag && bodyStartTagCantBeOmittedWithFirstChildTags.has(firstNonEmptyChildNode.tag)) {
152
126
  isBodyStartTagCanBeOmitted = false;
153
127
  }
154
-
155
128
  if (nextNode && typeof nextNode === 'string' && isComment(nextNode)) {
156
129
  isBodyEndTagCanBeOmitted = false;
157
130
  }
158
-
159
131
  if (isBodyStartTagCanBeOmitted && isBodyEndTagCanBeOmitted) {
132
+ // @ts-expect-error -- deliberately set tag to false
160
133
  node.tag = false;
161
134
  }
162
135
  }
163
-
164
136
  /**
165
137
  * A "colgroup" element's start tag may be omitted if the first thing inside the "colgroup" element is a "col" element, and if the element is not IMMEDIATELY preceded by another "colgroup" element. It can't be omitted if the element is empty.
166
138
  * A "colgroup" element's end tag may be omitted if the "colgroup" element is not IMMEDIATELY followed by ASCII whitespace or a comment.
167
- */
168
- if (node.tag === 'colgroup') {
139
+ */ if (node.tag === 'colgroup') {
169
140
  let isColgroupStartTagCanBeOmitted = false;
170
141
  let isColgroupEndTagCanBeOmitted = true;
171
-
172
- if (firstNonEmptyChildNode && firstNonEmptyChildNode.tag && firstNonEmptyChildNode.tag === 'col') {
142
+ if (firstNonEmptyChildNode && typeof firstNonEmptyChildNode === 'object' && firstNonEmptyChildNode.tag && firstNonEmptyChildNode.tag === 'col') {
173
143
  isColgroupStartTagCanBeOmitted = true;
174
144
  }
175
-
176
- if (prevNonEmptyNode && prevNonEmptyNode.tag && prevNonEmptyNode.tag === 'colgroup') {
145
+ if (prevNonEmptyNode && typeof prevNonEmptyNode === 'object' && prevNonEmptyNode.tag && prevNonEmptyNode.tag === 'colgroup') {
177
146
  isColgroupStartTagCanBeOmitted = false;
178
147
  }
179
-
180
- if (
181
- (nextNode && typeof nextNode === 'string' && startWithWhitespacePattern.test(nextNode)) ||
182
- (nextNonEmptyNode && typeof nextNonEmptyNode === 'string' && isComment(nextNonEmptyNode))
183
- ) {
148
+ if (nextNode && typeof nextNode === 'string' && startWithWhitespacePattern.test(nextNode) || nextNonEmptyNode && typeof nextNonEmptyNode === 'string' && isComment(nextNonEmptyNode)) {
184
149
  isColgroupEndTagCanBeOmitted = false;
185
150
  }
186
-
187
151
  if (isColgroupStartTagCanBeOmitted && isColgroupEndTagCanBeOmitted) {
152
+ // @ts-expect-error -- deliberately set tag to false
188
153
  node.tag = false;
189
154
  }
190
155
  }
191
-
192
156
  /**
193
157
  * A "tbody" element's start tag may be omitted if the first thing inside the "tbody" element is a "tr" element, and if the element is not immediately preceded by another "tbody", "thead" or "tfoot" element. It can't be omitted if the element is empty.
194
158
  * A "tbody" element's end tag may be omitted if the "tbody" element is not IMMEDIATELY followed by a "tbody" or "tfoot" element.
195
- */
196
- if (node.tag === 'tbody') {
159
+ */ if (node.tag === 'tbody') {
197
160
  let isTbodyStartTagCanBeOmitted = false;
198
161
  let isTbodyEndTagCanBeOmitted = true;
199
-
200
- if (firstNonEmptyChildNode && firstNonEmptyChildNode.tag && firstNonEmptyChildNode.tag === 'tr') {
162
+ if (firstNonEmptyChildNode && typeof firstNonEmptyChildNode === 'object' && firstNonEmptyChildNode.tag && firstNonEmptyChildNode.tag === 'tr') {
201
163
  isTbodyStartTagCanBeOmitted = true;
202
164
  }
203
-
204
- if (prevNonEmptyNode && prevNonEmptyNode.tag && tbodyStartTagCantBeOmittedWithPrecededTags.has(prevNonEmptyNode.tag)) {
165
+ if (prevNonEmptyNode && typeof prevNonEmptyNode === 'object' && prevNonEmptyNode.tag && tbodyStartTagCantBeOmittedWithPrecededTags.has(prevNonEmptyNode.tag)) {
205
166
  isTbodyStartTagCanBeOmitted = false;
206
167
  }
207
-
208
- if (nextNonEmptyNode && nextNonEmptyNode.tag && tbodyEndTagCantBeOmittedWithFollowedTags.has(nextNonEmptyNode.tag)) {
168
+ if (nextNonEmptyNode && typeof nextNonEmptyNode === 'object' && nextNonEmptyNode.tag && tbodyEndTagCantBeOmittedWithFollowedTags.has(nextNonEmptyNode.tag)) {
209
169
  isTbodyEndTagCanBeOmitted = false;
210
170
  }
211
-
212
171
  if (isTbodyStartTagCanBeOmitted && isTbodyEndTagCanBeOmitted) {
172
+ // @ts-expect-error -- deliberately set tag to false
213
173
  node.tag = false;
214
174
  }
215
175
  }
216
-
217
176
  if (node.content && node.content.length) {
218
177
  removeOptionalTags(node.content);
219
178
  }
220
-
221
179
  return node;
222
180
  });
223
-
224
181
  return tree;
225
182
  }
183
+ // Specification https://html.spec.whatwg.org/multipage/syntax.html#optional-tags
184
+ /** Remove optional tag in the DOM */ const mod = {
185
+ default: removeOptionalTags
186
+ };
187
+
188
+ export { mod as default };
@@ -0,0 +1,57 @@
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 redundantScriptTypes: Set<string>;
55
+ declare const mod: HtmlnanoModule;
56
+
57
+ export { mod as default, redundantScriptTypes };
@@ -0,0 +1,57 @@
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 redundantScriptTypes: Set<string>;
55
+ declare const mod: HtmlnanoModule;
56
+
57
+ export { mod as default, redundantScriptTypes };
@@ -0,0 +1,128 @@
1
+ Object.defineProperty(exports, '__esModule', { value: true });
2
+
3
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#JavaScript_types
4
+ const redundantScriptTypes = new Set([
5
+ 'application/javascript',
6
+ 'application/ecmascript',
7
+ 'application/x-ecmascript',
8
+ 'application/x-javascript',
9
+ 'text/javascript',
10
+ 'text/ecmascript',
11
+ 'text/javascript1.0',
12
+ 'text/javascript1.1',
13
+ 'text/javascript1.2',
14
+ 'text/javascript1.3',
15
+ 'text/javascript1.4',
16
+ 'text/javascript1.5',
17
+ 'text/jscript',
18
+ 'text/livescript',
19
+ 'text/x-ecmascript',
20
+ 'text/x-javascript'
21
+ ]);
22
+ // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#missing-value-default
23
+ const missingValueDefaultAttributes = {
24
+ form: {
25
+ method: 'get'
26
+ },
27
+ input: {
28
+ type: 'text'
29
+ },
30
+ button: {
31
+ // https://html.spec.whatwg.org/multipage/form-elements.html#attr-button-type
32
+ type: 'submit'
33
+ },
34
+ script: {
35
+ language: 'javascript',
36
+ type: (attrs)=>{
37
+ for (const [attrName, attrValue] of Object.entries(attrs)){
38
+ if (attrName.toLowerCase() !== 'type') {
39
+ continue;
40
+ }
41
+ if (typeof attrValue === 'string') {
42
+ return redundantScriptTypes.has(attrValue);
43
+ }
44
+ return false;
45
+ }
46
+ return false;
47
+ },
48
+ // Remove attribute if the function returns false
49
+ charset: (attrs)=>{
50
+ // The charset attribute only really makes sense on “external” SCRIPT elements:
51
+ // http://perfectionkills.com/optimizing-html/#8_script_charset
52
+ return !attrs.src;
53
+ }
54
+ },
55
+ style: {
56
+ media: 'all',
57
+ type: 'text/css'
58
+ },
59
+ link: {
60
+ media: 'all',
61
+ type: (attrs)=>{
62
+ // https://html.spec.whatwg.org/multipage/links.html#link-type-stylesheet
63
+ let isRelStyleSheet = false;
64
+ let isTypeTextCSS = false;
65
+ if (attrs) {
66
+ for (const [attrName, attrValue] of Object.entries(attrs)){
67
+ if (attrName.toLowerCase() === 'rel' && attrValue === 'stylesheet') {
68
+ isRelStyleSheet = true;
69
+ }
70
+ if (attrName.toLowerCase() === 'type' && attrValue === 'text/css') {
71
+ isTypeTextCSS = true;
72
+ }
73
+ }
74
+ }
75
+ // Only "text/css" is redudant for link[rel=stylesheet]. Otherwise "type" shouldn't be removed
76
+ return isRelStyleSheet && isTypeTextCSS;
77
+ }
78
+ },
79
+ // See: https://html.spec.whatwg.org/#lazy-loading-attributes
80
+ img: {
81
+ loading: 'eager',
82
+ // https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-decoding
83
+ decoding: 'auto'
84
+ },
85
+ iframe: {
86
+ loading: 'eager'
87
+ },
88
+ // https://html.spec.whatwg.org/multipage/media.html#htmltrackelement
89
+ track: {
90
+ kind: 'subtitles'
91
+ },
92
+ textarea: {
93
+ // https://html.spec.whatwg.org/multipage/form-elements.html#dom-textarea-wrap
94
+ wrap: 'soft'
95
+ },
96
+ area: {
97
+ // https://html.spec.whatwg.org/multipage/image-maps.html#attr-area-shape
98
+ shape: 'rect'
99
+ }
100
+ };
101
+ const tagsHaveMissingValueDefaultAttributes = new Set(Object.keys(missingValueDefaultAttributes));
102
+ /** Removes redundant attributes */ const mod = {
103
+ onAttrs () {
104
+ return (attrs, node)=>{
105
+ if (!node.tag) return attrs;
106
+ const newAttrs = attrs;
107
+ if (tagsHaveMissingValueDefaultAttributes.has(node.tag)) {
108
+ const tagRedundantAttributes = missingValueDefaultAttributes[node.tag];
109
+ for (const redundantAttributeName of Object.keys(tagRedundantAttributes)){
110
+ const tagRedundantAttributeValue = tagRedundantAttributes[redundantAttributeName];
111
+ let isRemove = false;
112
+ if (typeof tagRedundantAttributeValue === 'function') {
113
+ isRemove = tagRedundantAttributeValue(attrs);
114
+ } else if (attrs[redundantAttributeName] === tagRedundantAttributeValue) {
115
+ isRemove = true;
116
+ }
117
+ if (isRemove) {
118
+ delete newAttrs[redundantAttributeName];
119
+ }
120
+ }
121
+ }
122
+ return newAttrs;
123
+ };
124
+ }
125
+ };
126
+
127
+ exports.default = mod;
128
+ exports.redundantScriptTypes = redundantScriptTypes;