htmlnano 2.1.2 → 2.1.4
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 +54 -12
- package/dist/_modules/collapseAttributeWhitespace.d.mts +57 -0
- package/dist/_modules/collapseAttributeWhitespace.d.ts +57 -0
- package/dist/_modules/collapseAttributeWhitespace.js +296 -0
- package/dist/_modules/collapseAttributeWhitespace.mjs +293 -0
- package/dist/_modules/collapseBooleanAttributes.d.mts +60 -0
- package/dist/_modules/collapseBooleanAttributes.d.ts +60 -0
- package/dist/_modules/collapseBooleanAttributes.js +159 -0
- package/{lib/modules → dist/_modules}/collapseBooleanAttributes.mjs +39 -57
- package/dist/_modules/collapseWhitespace.d.mts +57 -0
- package/dist/_modules/collapseWhitespace.d.ts +57 -0
- package/dist/_modules/collapseWhitespace.js +172 -0
- package/dist/_modules/collapseWhitespace.mjs +170 -0
- package/dist/_modules/custom.d.mts +57 -0
- package/dist/_modules/custom.d.ts +57 -0
- package/dist/_modules/custom.js +22 -0
- package/dist/_modules/custom.mjs +20 -0
- package/dist/_modules/deduplicateAttributeValues.d.mts +56 -0
- package/dist/_modules/deduplicateAttributeValues.d.ts +56 -0
- package/dist/_modules/deduplicateAttributeValues.js +38 -0
- package/dist/_modules/deduplicateAttributeValues.mjs +36 -0
- package/dist/_modules/example.d.mts +59 -0
- package/dist/_modules/example.d.ts +59 -0
- package/dist/_modules/example.js +67 -0
- package/dist/_modules/example.mjs +65 -0
- package/dist/_modules/mergeScripts.d.mts +56 -0
- package/dist/_modules/mergeScripts.d.ts +56 -0
- package/dist/_modules/mergeScripts.js +53 -0
- package/dist/_modules/mergeScripts.mjs +51 -0
- package/dist/_modules/mergeStyles.d.mts +56 -0
- package/dist/_modules/mergeStyles.d.ts +56 -0
- package/dist/_modules/mergeStyles.js +42 -0
- package/dist/_modules/mergeStyles.mjs +40 -0
- package/dist/_modules/minifyConditionalComments.d.mts +56 -0
- package/dist/_modules/minifyConditionalComments.d.ts +56 -0
- package/dist/_modules/minifyConditionalComments.js +54 -0
- package/{lib/modules → dist/_modules}/minifyConditionalComments.mjs +21 -22
- package/dist/_modules/minifyCss.d.mts +56 -0
- package/dist/_modules/minifyCss.d.ts +56 -0
- package/dist/_modules/minifyCss.js +84 -0
- package/dist/_modules/minifyCss.mjs +82 -0
- package/dist/_modules/minifyJs.d.mts +56 -0
- package/dist/_modules/minifyJs.d.ts +56 -0
- package/dist/_modules/minifyJs.js +108 -0
- package/dist/_modules/minifyJs.mjs +106 -0
- package/dist/_modules/minifyJson.d.mts +56 -0
- package/dist/_modules/minifyJson.d.ts +56 -0
- package/dist/_modules/minifyJson.js +35 -0
- package/dist/_modules/minifyJson.mjs +33 -0
- package/dist/_modules/minifySvg.d.mts +56 -0
- package/dist/_modules/minifySvg.d.ts +56 -0
- package/dist/_modules/minifySvg.js +40 -0
- package/dist/_modules/minifySvg.mjs +38 -0
- package/dist/_modules/minifyUrls.d.mts +56 -0
- package/dist/_modules/minifyUrls.d.ts +56 -0
- package/dist/_modules/minifyUrls.js +180 -0
- package/dist/_modules/minifyUrls.mjs +178 -0
- package/dist/_modules/normalizeAttributeValues.d.mts +56 -0
- package/dist/_modules/normalizeAttributeValues.d.ts +56 -0
- package/dist/_modules/normalizeAttributeValues.js +234 -0
- package/dist/_modules/normalizeAttributeValues.mjs +232 -0
- package/dist/_modules/removeAttributeQuotes.d.mts +56 -0
- package/dist/_modules/removeAttributeQuotes.d.ts +56 -0
- package/dist/_modules/removeAttributeQuotes.js +15 -0
- package/dist/_modules/removeAttributeQuotes.mjs +13 -0
- package/dist/_modules/removeComments.d.mts +58 -0
- package/dist/_modules/removeComments.d.ts +58 -0
- package/dist/_modules/removeComments.js +83 -0
- package/{lib/modules → dist/_modules}/removeComments.mjs +24 -35
- package/dist/_modules/removeEmptyAttributes.d.mts +56 -0
- package/dist/_modules/removeEmptyAttributes.d.ts +56 -0
- package/dist/_modules/removeEmptyAttributes.js +197 -0
- package/dist/_modules/removeEmptyAttributes.mjs +195 -0
- package/dist/_modules/removeOptionalTags.d.mts +56 -0
- package/dist/_modules/removeOptionalTags.d.ts +56 -0
- package/dist/_modules/removeOptionalTags.js +190 -0
- package/{lib/modules → dist/_modules}/removeOptionalTags.mjs +54 -91
- package/dist/_modules/removeRedundantAttributes.d.mts +57 -0
- package/dist/_modules/removeRedundantAttributes.d.ts +57 -0
- package/dist/_modules/removeRedundantAttributes.js +128 -0
- package/{lib/modules → dist/_modules}/removeRedundantAttributes.mjs +43 -59
- package/dist/_modules/removeUnusedCss.d.mts +60 -0
- package/dist/_modules/removeUnusedCss.d.ts +60 -0
- package/dist/_modules/removeUnusedCss.js +134 -0
- package/dist/_modules/removeUnusedCss.mjs +132 -0
- package/dist/_modules/sortAttributes.d.mts +57 -0
- package/dist/_modules/sortAttributes.d.ts +57 -0
- package/dist/_modules/sortAttributes.js +102 -0
- package/{lib/modules → dist/_modules}/sortAttributes.mjs +39 -58
- package/dist/_modules/sortAttributesWithLists.d.mts +56 -0
- package/dist/_modules/sortAttributesWithLists.d.ts +56 -0
- package/dist/_modules/sortAttributesWithLists.js +118 -0
- package/{lib/modules → dist/_modules}/sortAttributesWithLists.mjs +41 -59
- package/dist/helpers.d.ts +16 -0
- package/dist/helpers.js +72 -0
- package/dist/helpers.mjs +63 -0
- package/dist/index.d.ts +79 -0
- package/dist/index.js +223 -0
- package/dist/index.mjs +209 -0
- package/dist/presets/ampSafe.d.ts +47 -0
- package/dist/presets/ampSafe.js +19 -0
- package/{lib → dist}/presets/ampSafe.mjs +6 -4
- package/dist/presets/max.d.ts +47 -0
- package/dist/presets/max.js +28 -0
- package/{lib → dist}/presets/max.mjs +6 -4
- package/dist/presets/safe.d.ts +47 -0
- package/dist/presets/safe.js +60 -0
- package/{lib → dist}/presets/safe.mjs +13 -20
- package/package.json +53 -56
- package/.eslintignore +0 -3
- package/CHANGELOG.md +0 -409
- package/docs/README.md +0 -33
- package/docs/babel.config.js +0 -3
- package/docs/docs/010-introduction.md +0 -22
- package/docs/docs/020-usage.md +0 -117
- package/docs/docs/030-config.md +0 -21
- package/docs/docs/040-presets.md +0 -75
- package/docs/docs/050-modules.md +0 -855
- package/docs/docs/060-contribute.md +0 -16
- package/docs/docusaurus.config.js +0 -65
- package/docs/netlify.toml +0 -4
- package/docs/package-lock.json +0 -21630
- package/docs/package.json +0 -40
- package/docs/sidebars.js +0 -26
- package/docs/versioned_docs/version-1.1.1/010-introduction.md +0 -22
- package/docs/versioned_docs/version-1.1.1/020-usage.md +0 -77
- package/docs/versioned_docs/version-1.1.1/030-config.md +0 -21
- package/docs/versioned_docs/version-1.1.1/040-presets.md +0 -75
- package/docs/versioned_docs/version-1.1.1/050-modules.md +0 -785
- package/docs/versioned_docs/version-1.1.1/060-contribute.md +0 -16
- package/docs/versioned_docs/version-2.0.0/010-introduction.md +0 -22
- package/docs/versioned_docs/version-2.0.0/020-usage.md +0 -77
- package/docs/versioned_docs/version-2.0.0/030-config.md +0 -21
- package/docs/versioned_docs/version-2.0.0/040-presets.md +0 -75
- package/docs/versioned_docs/version-2.0.0/050-modules.md +0 -838
- package/docs/versioned_docs/version-2.0.0/060-contribute.md +0 -16
- package/docs/versioned_sidebars/version-1.1.1-sidebars.json +0 -8
- package/docs/versioned_sidebars/version-2.0.0-sidebars.json +0 -8
- package/docs/versions.json +0 -4
- package/index.cjs +0 -11
- package/index.d.cts +0 -3
- package/index.d.mts +0 -3
- package/index.d.ts +0 -94
- package/index.mjs +0 -2
- package/lib/helpers.cjs +0 -79
- package/lib/helpers.mjs +0 -53
- package/lib/htmlnano.cjs +0 -202
- package/lib/htmlnano.mjs +0 -198
- package/lib/modules/collapseAttributeWhitespace.cjs +0 -86
- package/lib/modules/collapseAttributeWhitespace.mjs +0 -104
- package/lib/modules/collapseBooleanAttributes.cjs +0 -62
- package/lib/modules/collapseWhitespace.cjs +0 -100
- package/lib/modules/collapseWhitespace.mjs +0 -132
- package/lib/modules/custom.cjs +0 -19
- package/lib/modules/custom.mjs +0 -16
- package/lib/modules/deduplicateAttributeValues.cjs +0 -38
- package/lib/modules/deduplicateAttributeValues.mjs +0 -40
- package/lib/modules/example.cjs +0 -85
- package/lib/modules/example.mjs +0 -75
- package/lib/modules/mergeScripts.cjs +0 -54
- package/lib/modules/mergeScripts.mjs +0 -56
- package/lib/modules/mergeStyles.cjs +0 -38
- package/lib/modules/mergeStyles.mjs +0 -36
- package/lib/modules/minifyConditionalComments.cjs +0 -47
- package/lib/modules/minifyCss.cjs +0 -73
- package/lib/modules/minifyCss.mjs +0 -88
- package/lib/modules/minifyJs.cjs +0 -103
- package/lib/modules/minifyJs.mjs +0 -121
- package/lib/modules/minifyJson.cjs +0 -24
- package/lib/modules/minifyJson.mjs +0 -21
- package/lib/modules/minifySvg.cjs +0 -37
- package/lib/modules/minifySvg.mjs +0 -30
- package/lib/modules/minifyUrls.cjs +0 -141
- package/lib/modules/minifyUrls.mjs +0 -229
- package/lib/modules/normalizeAttributeValues.cjs +0 -120
- package/lib/modules/normalizeAttributeValues.mjs +0 -140
- package/lib/modules/removeAttributeQuotes.cjs +0 -17
- package/lib/modules/removeAttributeQuotes.mjs +0 -12
- package/lib/modules/removeComments.cjs +0 -86
- package/lib/modules/removeEmptyAttributes.cjs +0 -72
- package/lib/modules/removeEmptyAttributes.mjs +0 -121
- package/lib/modules/removeOptionalTags.cjs +0 -183
- package/lib/modules/removeRedundantAttributes.cjs +0 -112
- package/lib/modules/removeUnusedCss.cjs +0 -113
- package/lib/modules/removeUnusedCss.mjs +0 -122
- package/lib/modules/sortAttributes.cjs +0 -98
- package/lib/modules/sortAttributesWithLists.cjs +0 -114
- package/lib/presets/ampSafe.cjs +0 -18
- package/lib/presets/max.cjs +0 -27
- package/lib/presets/safe.cjs +0 -65
- 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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
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
|
|
34
|
-
if (
|
|
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
|
|
49
|
+
for(let i = currentNodeIndex - 1; i >= 0; i--){
|
|
47
50
|
const node = tree[i];
|
|
48
|
-
if (node.tag) return node;
|
|
49
|
-
if (
|
|
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
|
|
61
|
+
for(let i = currentNodeIndex + 1; i < tree.length; i++){
|
|
61
62
|
const node = tree[i];
|
|
62
|
-
if (node
|
|
63
|
-
if (
|
|
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
|
-
|
|
73
|
-
|
|
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;
|