htmlnano 3.0.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -25
- package/dist/_modules/collapseAttributeWhitespace.d.mts +41 -5
- package/dist/_modules/collapseAttributeWhitespace.d.ts +41 -5
- package/dist/_modules/collapseAttributeWhitespace.js +69 -16
- package/dist/_modules/collapseAttributeWhitespace.mjs +67 -17
- package/dist/_modules/collapseBooleanAttributes.d.mts +36 -3
- package/dist/_modules/collapseBooleanAttributes.d.ts +36 -3
- package/dist/_modules/collapseBooleanAttributes.js +18 -11
- package/dist/_modules/collapseBooleanAttributes.mjs +18 -11
- package/dist/_modules/collapseWhitespace.d.mts +36 -3
- package/dist/_modules/collapseWhitespace.d.ts +36 -3
- package/dist/_modules/collapseWhitespace.js +25 -2
- package/dist/_modules/collapseWhitespace.mjs +25 -2
- package/dist/_modules/custom.d.mts +36 -3
- package/dist/_modules/custom.d.ts +36 -3
- package/dist/_modules/deduplicateAttributeValues.d.mts +36 -3
- package/dist/_modules/deduplicateAttributeValues.d.ts +36 -3
- package/dist/_modules/deduplicateAttributeValues.js +20 -5
- package/dist/_modules/deduplicateAttributeValues.mjs +21 -6
- package/dist/_modules/example.d.mts +36 -3
- package/dist/_modules/example.d.ts +36 -3
- package/dist/_modules/mergeScripts.d.mts +36 -3
- package/dist/_modules/mergeScripts.d.ts +36 -3
- package/dist/_modules/mergeScripts.js +111 -24
- package/dist/_modules/mergeScripts.mjs +111 -24
- package/dist/_modules/mergeStyles.d.mts +36 -3
- package/dist/_modules/mergeStyles.d.ts +36 -3
- package/dist/_modules/mergeStyles.js +66 -4
- package/dist/_modules/mergeStyles.mjs +66 -4
- package/dist/_modules/minifyAttributes.d.mts +95 -0
- package/dist/_modules/minifyAttributes.d.ts +95 -0
- package/dist/_modules/minifyAttributes.js +159 -0
- package/dist/_modules/minifyAttributes.mjs +157 -0
- package/dist/_modules/minifyConditionalComments.d.mts +36 -3
- package/dist/_modules/minifyConditionalComments.d.ts +36 -3
- package/dist/_modules/minifyConditionalComments.js +37 -19
- package/dist/_modules/minifyConditionalComments.mjs +37 -19
- package/dist/_modules/minifyCss.d.mts +36 -3
- package/dist/_modules/minifyCss.d.ts +36 -3
- package/dist/_modules/minifyCss.js +13 -27
- package/dist/_modules/minifyCss.mjs +14 -28
- package/dist/_modules/minifyHtmlTemplate.d.mts +91 -0
- package/dist/_modules/minifyHtmlTemplate.d.ts +91 -0
- package/dist/_modules/minifyHtmlTemplate.js +231 -0
- package/dist/_modules/minifyHtmlTemplate.mjs +228 -0
- package/dist/_modules/minifyJs.d.mts +36 -3
- package/dist/_modules/minifyJs.d.ts +36 -3
- package/dist/_modules/minifyJs.js +106 -5
- package/dist/_modules/minifyJs.mjs +107 -6
- package/dist/_modules/minifyJson.d.mts +36 -3
- package/dist/_modules/minifyJson.d.ts +36 -3
- package/dist/_modules/minifyJson.js +8 -11
- package/dist/_modules/minifyJson.mjs +8 -11
- package/dist/_modules/minifySvg.d.mts +36 -3
- package/dist/_modules/minifySvg.d.ts +36 -3
- package/dist/_modules/minifySvg.js +35 -4
- package/dist/_modules/minifySvg.mjs +35 -4
- package/dist/_modules/minifyUrls.d.mts +37 -4
- package/dist/_modules/minifyUrls.d.ts +37 -4
- package/dist/_modules/minifyUrls.js +52 -27
- package/dist/_modules/minifyUrls.mjs +52 -27
- package/dist/_modules/normalizeAttributeValues.d.mts +36 -3
- package/dist/_modules/normalizeAttributeValues.d.ts +36 -3
- package/dist/_modules/normalizeAttributeValues.js +10 -8
- package/dist/_modules/normalizeAttributeValues.mjs +10 -8
- package/dist/_modules/removeAttributeQuotes.d.mts +40 -4
- package/dist/_modules/removeAttributeQuotes.d.ts +40 -4
- package/dist/_modules/removeAttributeQuotes.js +9 -4
- package/dist/_modules/removeAttributeQuotes.mjs +9 -4
- package/dist/_modules/removeComments.d.mts +37 -4
- package/dist/_modules/removeComments.d.ts +37 -4
- package/dist/_modules/removeComments.js +44 -12
- package/dist/_modules/removeComments.mjs +44 -12
- package/dist/_modules/removeEmptyAttributes.d.mts +36 -3
- package/dist/_modules/removeEmptyAttributes.d.ts +36 -3
- package/dist/_modules/removeEmptyAttributes.js +37 -16
- package/dist/_modules/removeEmptyAttributes.mjs +37 -16
- package/dist/_modules/removeEmptyElements.d.mts +95 -0
- package/dist/_modules/removeEmptyElements.d.ts +95 -0
- package/dist/_modules/removeEmptyElements.js +90 -0
- package/dist/_modules/removeEmptyElements.mjs +88 -0
- package/dist/_modules/removeOptionalTags.d.mts +36 -3
- package/dist/_modules/removeOptionalTags.d.ts +36 -3
- package/dist/_modules/removeOptionalTags.js +39 -28
- package/dist/_modules/removeOptionalTags.mjs +39 -28
- package/dist/_modules/removeRedundantAttributes.d.mts +36 -3
- package/dist/_modules/removeRedundantAttributes.d.ts +36 -3
- package/dist/_modules/removeRedundantAttributes.js +43 -28
- package/dist/_modules/removeRedundantAttributes.mjs +43 -28
- package/dist/_modules/removeUnusedCss.d.mts +37 -3
- package/dist/_modules/removeUnusedCss.d.ts +37 -3
- package/dist/_modules/removeUnusedCss.js +40 -14
- package/dist/_modules/removeUnusedCss.mjs +41 -15
- package/dist/_modules/sortAttributes.d.mts +39 -5
- package/dist/_modules/sortAttributes.d.ts +39 -5
- package/dist/_modules/sortAttributes.js +27 -8
- package/dist/_modules/sortAttributes.mjs +27 -8
- package/dist/_modules/sortAttributesWithLists.d.mts +39 -4
- package/dist/_modules/sortAttributesWithLists.d.ts +39 -4
- package/dist/_modules/sortAttributesWithLists.js +61 -52
- package/dist/_modules/sortAttributesWithLists.mjs +62 -53
- package/dist/helpers.d.ts +8 -1
- package/dist/helpers.js +48 -0
- package/dist/helpers.mjs +45 -1
- package/dist/index.d.ts +37 -4
- package/dist/index.js +13 -0
- package/dist/index.mjs +13 -0
- package/dist/presets/ampSafe.d.ts +36 -3
- package/dist/presets/max.d.ts +36 -3
- package/dist/presets/max.js +13 -5
- package/dist/presets/max.mjs +13 -5
- package/dist/presets/safe.d.ts +36 -3
- package/dist/presets/safe.js +6 -0
- package/dist/presets/safe.mjs +6 -0
- package/package.json +27 -15
|
@@ -2,16 +2,24 @@ import PostHTML from 'posthtml';
|
|
|
2
2
|
import { MinifyOptions } from 'terser';
|
|
3
3
|
import { Options } from 'cssnano';
|
|
4
4
|
import { Config } from 'svgo';
|
|
5
|
+
import { UserDefinedOptions } from 'purgecss';
|
|
5
6
|
|
|
6
7
|
type PostHTMLTreeLike = [PostHTML.Node] & PostHTML.NodeAPI & {
|
|
7
8
|
options?: {
|
|
8
9
|
quoteAllAttributes?: boolean | undefined;
|
|
10
|
+
quoteStyle?: 0 | 1 | 2 | undefined;
|
|
11
|
+
replaceQuote?: boolean | undefined;
|
|
9
12
|
} | undefined;
|
|
10
13
|
render(): string;
|
|
11
14
|
render(node: PostHTML.Node | PostHTMLTreeLike, renderOptions?: any): string;
|
|
12
15
|
};
|
|
13
16
|
type MaybeArray<T> = T | Array<T>;
|
|
14
17
|
type PostHTMLNodeLike = PostHTML.Node | string;
|
|
18
|
+
type HtmlnanoTemplateRule = {
|
|
19
|
+
tag: string;
|
|
20
|
+
attrs?: Record<string, string | boolean | void>;
|
|
21
|
+
};
|
|
22
|
+
type MinifyHtmlTemplateOptions = boolean | HtmlnanoTemplateRule[];
|
|
15
23
|
interface HtmlnanoOptions {
|
|
16
24
|
skipConfigLoading?: boolean;
|
|
17
25
|
configPath?: string;
|
|
@@ -27,17 +35,42 @@ interface HtmlnanoOptions {
|
|
|
27
35
|
mergeStyles?: boolean;
|
|
28
36
|
mergeScripts?: boolean;
|
|
29
37
|
minifyCss?: Options | boolean;
|
|
38
|
+
minifyHtmlTemplate?: MinifyHtmlTemplateOptions;
|
|
30
39
|
minifyConditionalComments?: boolean;
|
|
31
40
|
minifyJs?: MinifyOptions | boolean;
|
|
32
41
|
minifyJson?: boolean;
|
|
42
|
+
minifyAttributes?: boolean | {
|
|
43
|
+
metaContent?: boolean;
|
|
44
|
+
redundantWhitespaces?: 'safe' | 'agressive' | false;
|
|
45
|
+
};
|
|
33
46
|
minifySvg?: Config | boolean;
|
|
34
47
|
normalizeAttributeValues?: boolean;
|
|
35
|
-
removeAttributeQuotes?: boolean
|
|
36
|
-
|
|
48
|
+
removeAttributeQuotes?: boolean | {
|
|
49
|
+
force?: boolean;
|
|
50
|
+
};
|
|
51
|
+
removeComments?: boolean | RegExp | ((comment: string) => boolean) | string;
|
|
37
52
|
removeEmptyAttributes?: boolean;
|
|
53
|
+
removeEmptyElements?: boolean | {
|
|
54
|
+
removeWithAttributes?: boolean;
|
|
55
|
+
};
|
|
38
56
|
removeRedundantAttributes?: boolean;
|
|
39
57
|
removeOptionalTags?: boolean;
|
|
40
|
-
removeUnusedCss?: boolean
|
|
58
|
+
removeUnusedCss?: boolean | ({
|
|
59
|
+
tool: 'purgeCSS';
|
|
60
|
+
} & Omit<UserDefinedOptions, 'content' | 'css' | 'extractors'>) | {
|
|
61
|
+
banner?: boolean;
|
|
62
|
+
csspath?: string;
|
|
63
|
+
htmlroot?: string;
|
|
64
|
+
ignore?: (string | RegExp)[];
|
|
65
|
+
inject?: string;
|
|
66
|
+
jsdom?: object;
|
|
67
|
+
media?: string[];
|
|
68
|
+
report?: boolean;
|
|
69
|
+
strictSSL?: boolean;
|
|
70
|
+
timeout?: number;
|
|
71
|
+
uncssrc?: string;
|
|
72
|
+
userAgent?: string;
|
|
73
|
+
};
|
|
41
74
|
sortAttributes?: boolean | 'alphabetical' | 'frequency';
|
|
42
75
|
sortAttributesWithLists?: boolean | 'alphabetical' | 'frequency';
|
|
43
76
|
}
|
|
@@ -2,16 +2,24 @@ import PostHTML from 'posthtml';
|
|
|
2
2
|
import { MinifyOptions } from 'terser';
|
|
3
3
|
import { Options } from 'cssnano';
|
|
4
4
|
import { Config } from 'svgo';
|
|
5
|
+
import { UserDefinedOptions } from 'purgecss';
|
|
5
6
|
|
|
6
7
|
type PostHTMLTreeLike = [PostHTML.Node] & PostHTML.NodeAPI & {
|
|
7
8
|
options?: {
|
|
8
9
|
quoteAllAttributes?: boolean | undefined;
|
|
10
|
+
quoteStyle?: 0 | 1 | 2 | undefined;
|
|
11
|
+
replaceQuote?: boolean | undefined;
|
|
9
12
|
} | undefined;
|
|
10
13
|
render(): string;
|
|
11
14
|
render(node: PostHTML.Node | PostHTMLTreeLike, renderOptions?: any): string;
|
|
12
15
|
};
|
|
13
16
|
type MaybeArray<T> = T | Array<T>;
|
|
14
17
|
type PostHTMLNodeLike = PostHTML.Node | string;
|
|
18
|
+
type HtmlnanoTemplateRule = {
|
|
19
|
+
tag: string;
|
|
20
|
+
attrs?: Record<string, string | boolean | void>;
|
|
21
|
+
};
|
|
22
|
+
type MinifyHtmlTemplateOptions = boolean | HtmlnanoTemplateRule[];
|
|
15
23
|
interface HtmlnanoOptions {
|
|
16
24
|
skipConfigLoading?: boolean;
|
|
17
25
|
configPath?: string;
|
|
@@ -27,17 +35,42 @@ interface HtmlnanoOptions {
|
|
|
27
35
|
mergeStyles?: boolean;
|
|
28
36
|
mergeScripts?: boolean;
|
|
29
37
|
minifyCss?: Options | boolean;
|
|
38
|
+
minifyHtmlTemplate?: MinifyHtmlTemplateOptions;
|
|
30
39
|
minifyConditionalComments?: boolean;
|
|
31
40
|
minifyJs?: MinifyOptions | boolean;
|
|
32
41
|
minifyJson?: boolean;
|
|
42
|
+
minifyAttributes?: boolean | {
|
|
43
|
+
metaContent?: boolean;
|
|
44
|
+
redundantWhitespaces?: 'safe' | 'agressive' | false;
|
|
45
|
+
};
|
|
33
46
|
minifySvg?: Config | boolean;
|
|
34
47
|
normalizeAttributeValues?: boolean;
|
|
35
|
-
removeAttributeQuotes?: boolean
|
|
36
|
-
|
|
48
|
+
removeAttributeQuotes?: boolean | {
|
|
49
|
+
force?: boolean;
|
|
50
|
+
};
|
|
51
|
+
removeComments?: boolean | RegExp | ((comment: string) => boolean) | string;
|
|
37
52
|
removeEmptyAttributes?: boolean;
|
|
53
|
+
removeEmptyElements?: boolean | {
|
|
54
|
+
removeWithAttributes?: boolean;
|
|
55
|
+
};
|
|
38
56
|
removeRedundantAttributes?: boolean;
|
|
39
57
|
removeOptionalTags?: boolean;
|
|
40
|
-
removeUnusedCss?: boolean
|
|
58
|
+
removeUnusedCss?: boolean | ({
|
|
59
|
+
tool: 'purgeCSS';
|
|
60
|
+
} & Omit<UserDefinedOptions, 'content' | 'css' | 'extractors'>) | {
|
|
61
|
+
banner?: boolean;
|
|
62
|
+
csspath?: string;
|
|
63
|
+
htmlroot?: string;
|
|
64
|
+
ignore?: (string | RegExp)[];
|
|
65
|
+
inject?: string;
|
|
66
|
+
jsdom?: object;
|
|
67
|
+
media?: string[];
|
|
68
|
+
report?: boolean;
|
|
69
|
+
strictSSL?: boolean;
|
|
70
|
+
timeout?: number;
|
|
71
|
+
uncssrc?: string;
|
|
72
|
+
userAgent?: string;
|
|
73
|
+
};
|
|
41
74
|
sortAttributes?: boolean | 'alphabetical' | 'frequency';
|
|
42
75
|
sortAttributesWithLists?: boolean | 'alphabetical' | 'frequency';
|
|
43
76
|
}
|
|
@@ -7,7 +7,8 @@ const bodyStartTagCantBeOmittedWithFirstChildTags = new Set([
|
|
|
7
7
|
'meta',
|
|
8
8
|
'link',
|
|
9
9
|
'script',
|
|
10
|
-
'style'
|
|
10
|
+
'style',
|
|
11
|
+
'template'
|
|
11
12
|
]);
|
|
12
13
|
const tbodyStartTagCantBeOmittedWithPrecededTags = new Set([
|
|
13
14
|
'tbody',
|
|
@@ -70,32 +71,40 @@ function getNextNode(tree, currentNodeIndex, nonEmpty = false) {
|
|
|
70
71
|
}
|
|
71
72
|
return null;
|
|
72
73
|
}
|
|
74
|
+
function omitTag(node) {
|
|
75
|
+
const optionalTagNode = node;
|
|
76
|
+
optionalTagNode.optionalTagName = typeof node.tag === 'string' ? node.tag : undefined;
|
|
77
|
+
// @ts-expect-error -- deliberately set tag to false
|
|
78
|
+
node.tag = false;
|
|
79
|
+
}
|
|
80
|
+
function getNodeTagName(node) {
|
|
81
|
+
if (node.tag) return node.tag;
|
|
82
|
+
const optionalTagNode = node;
|
|
83
|
+
return optionalTagNode.optionalTagName || false;
|
|
84
|
+
}
|
|
73
85
|
function removeOptionalTags(tree) {
|
|
74
86
|
tree.forEach((node, index)=>{
|
|
75
87
|
if (typeof node === 'string') return node;
|
|
76
88
|
if (!node.tag) return node;
|
|
77
89
|
if (node.attrs && Object.keys(node.attrs).length) return node;
|
|
78
90
|
// const prevNode = getPrevNode(tree, index);
|
|
79
|
-
const
|
|
91
|
+
const prevNode = getPrevNode(tree, index);
|
|
80
92
|
const nextNode = getNextNode(tree, index);
|
|
81
|
-
const nextNonEmptyNode = getNextNode(tree, index, true);
|
|
82
93
|
const firstChildNode = getFirstChildTag(node, false);
|
|
83
|
-
const firstNonEmptyChildNode = getFirstChildTag(node);
|
|
84
94
|
/**
|
|
85
95
|
* An "html" element's start tag may be omitted if the first thing inside the "html" element is not a comment.
|
|
86
96
|
* An "html" element's end tag may be omitted if the "html" element is not IMMEDIATELY followed by a comment.
|
|
87
97
|
*/ if (node.tag === 'html') {
|
|
88
98
|
let isHtmlStartTagCanBeOmitted = true;
|
|
89
99
|
let isHtmlEndTagCanBeOmitted = true;
|
|
90
|
-
if (typeof
|
|
100
|
+
if (typeof firstChildNode === 'string' && helpers_js.isComment(firstChildNode)) {
|
|
91
101
|
isHtmlStartTagCanBeOmitted = false;
|
|
92
102
|
}
|
|
93
|
-
if (typeof
|
|
103
|
+
if (typeof nextNode === 'string' && helpers_js.isComment(nextNode)) {
|
|
94
104
|
isHtmlEndTagCanBeOmitted = false;
|
|
95
105
|
}
|
|
96
106
|
if (isHtmlStartTagCanBeOmitted && isHtmlEndTagCanBeOmitted) {
|
|
97
|
-
|
|
98
|
-
node.tag = false;
|
|
107
|
+
omitTag(node);
|
|
99
108
|
}
|
|
100
109
|
}
|
|
101
110
|
/**
|
|
@@ -104,15 +113,14 @@ function removeOptionalTags(tree) {
|
|
|
104
113
|
*/ if (node.tag === 'head') {
|
|
105
114
|
let isHeadStartTagCanBeOmitted = false;
|
|
106
115
|
let isHeadEndTagCanBeOmitted = true;
|
|
107
|
-
if (isEmptyNode(node) ||
|
|
116
|
+
if (isEmptyNode(node) || firstChildNode && typeof firstChildNode === 'object' && firstChildNode.tag) {
|
|
108
117
|
isHeadStartTagCanBeOmitted = true;
|
|
109
118
|
}
|
|
110
|
-
if (nextNode && typeof nextNode === 'string' && startWithWhitespacePattern.test(nextNode) ||
|
|
119
|
+
if (nextNode && typeof nextNode === 'string' && (startWithWhitespacePattern.test(nextNode) || helpers_js.isComment(nextNode))) {
|
|
111
120
|
isHeadEndTagCanBeOmitted = false;
|
|
112
121
|
}
|
|
113
122
|
if (isHeadStartTagCanBeOmitted && isHeadEndTagCanBeOmitted) {
|
|
114
|
-
|
|
115
|
-
node.tag = false;
|
|
123
|
+
omitTag(node);
|
|
116
124
|
}
|
|
117
125
|
}
|
|
118
126
|
/**
|
|
@@ -121,18 +129,17 @@ function removeOptionalTags(tree) {
|
|
|
121
129
|
*/ if (node.tag === 'body') {
|
|
122
130
|
let isBodyStartTagCanBeOmitted = true;
|
|
123
131
|
let isBodyEndTagCanBeOmitted = true;
|
|
124
|
-
if (typeof firstChildNode === 'string' && startWithWhitespacePattern.test(firstChildNode) || typeof
|
|
132
|
+
if (typeof firstChildNode === 'string' && startWithWhitespacePattern.test(firstChildNode) || typeof firstChildNode === 'string' && helpers_js.isComment(firstChildNode)) {
|
|
125
133
|
isBodyStartTagCanBeOmitted = false;
|
|
126
134
|
}
|
|
127
|
-
if (
|
|
135
|
+
if (firstChildNode && typeof firstChildNode === 'object' && firstChildNode.tag && bodyStartTagCantBeOmittedWithFirstChildTags.has(firstChildNode.tag)) {
|
|
128
136
|
isBodyStartTagCanBeOmitted = false;
|
|
129
137
|
}
|
|
130
138
|
if (nextNode && typeof nextNode === 'string' && helpers_js.isComment(nextNode)) {
|
|
131
139
|
isBodyEndTagCanBeOmitted = false;
|
|
132
140
|
}
|
|
133
141
|
if (isBodyStartTagCanBeOmitted && isBodyEndTagCanBeOmitted) {
|
|
134
|
-
|
|
135
|
-
node.tag = false;
|
|
142
|
+
omitTag(node);
|
|
136
143
|
}
|
|
137
144
|
}
|
|
138
145
|
/**
|
|
@@ -141,18 +148,17 @@ function removeOptionalTags(tree) {
|
|
|
141
148
|
*/ if (node.tag === 'colgroup') {
|
|
142
149
|
let isColgroupStartTagCanBeOmitted = false;
|
|
143
150
|
let isColgroupEndTagCanBeOmitted = true;
|
|
144
|
-
if (
|
|
151
|
+
if (firstChildNode && typeof firstChildNode === 'object' && firstChildNode.tag && firstChildNode.tag === 'col') {
|
|
145
152
|
isColgroupStartTagCanBeOmitted = true;
|
|
146
153
|
}
|
|
147
|
-
if (
|
|
154
|
+
if (prevNode && typeof prevNode === 'object' && getNodeTagName(prevNode) === 'colgroup') {
|
|
148
155
|
isColgroupStartTagCanBeOmitted = false;
|
|
149
156
|
}
|
|
150
|
-
if (nextNode && typeof nextNode === 'string' && startWithWhitespacePattern.test(nextNode) ||
|
|
157
|
+
if (nextNode && typeof nextNode === 'string' && (startWithWhitespacePattern.test(nextNode) || helpers_js.isComment(nextNode))) {
|
|
151
158
|
isColgroupEndTagCanBeOmitted = false;
|
|
152
159
|
}
|
|
153
160
|
if (isColgroupStartTagCanBeOmitted && isColgroupEndTagCanBeOmitted) {
|
|
154
|
-
|
|
155
|
-
node.tag = false;
|
|
161
|
+
omitTag(node);
|
|
156
162
|
}
|
|
157
163
|
}
|
|
158
164
|
/**
|
|
@@ -161,18 +167,23 @@ function removeOptionalTags(tree) {
|
|
|
161
167
|
*/ if (node.tag === 'tbody') {
|
|
162
168
|
let isTbodyStartTagCanBeOmitted = false;
|
|
163
169
|
let isTbodyEndTagCanBeOmitted = true;
|
|
164
|
-
if (
|
|
170
|
+
if (firstChildNode && typeof firstChildNode === 'object' && firstChildNode.tag && firstChildNode.tag === 'tr') {
|
|
165
171
|
isTbodyStartTagCanBeOmitted = true;
|
|
166
172
|
}
|
|
167
|
-
if (
|
|
168
|
-
|
|
173
|
+
if (prevNode && typeof prevNode === 'object') {
|
|
174
|
+
const prevTagName = getNodeTagName(prevNode);
|
|
175
|
+
if (prevTagName && tbodyStartTagCantBeOmittedWithPrecededTags.has(prevTagName)) {
|
|
176
|
+
isTbodyStartTagCanBeOmitted = false;
|
|
177
|
+
}
|
|
169
178
|
}
|
|
170
|
-
if (
|
|
171
|
-
|
|
179
|
+
if (nextNode && typeof nextNode === 'object') {
|
|
180
|
+
const nextTagName = getNodeTagName(nextNode);
|
|
181
|
+
if (nextTagName && tbodyEndTagCantBeOmittedWithFollowedTags.has(nextTagName)) {
|
|
182
|
+
isTbodyEndTagCanBeOmitted = false;
|
|
183
|
+
}
|
|
172
184
|
}
|
|
173
185
|
if (isTbodyStartTagCanBeOmitted && isTbodyEndTagCanBeOmitted) {
|
|
174
|
-
|
|
175
|
-
node.tag = false;
|
|
186
|
+
omitTag(node);
|
|
176
187
|
}
|
|
177
188
|
}
|
|
178
189
|
if (node.content && node.content.length) {
|
|
@@ -5,7 +5,8 @@ const bodyStartTagCantBeOmittedWithFirstChildTags = new Set([
|
|
|
5
5
|
'meta',
|
|
6
6
|
'link',
|
|
7
7
|
'script',
|
|
8
|
-
'style'
|
|
8
|
+
'style',
|
|
9
|
+
'template'
|
|
9
10
|
]);
|
|
10
11
|
const tbodyStartTagCantBeOmittedWithPrecededTags = new Set([
|
|
11
12
|
'tbody',
|
|
@@ -68,32 +69,40 @@ function getNextNode(tree, currentNodeIndex, nonEmpty = false) {
|
|
|
68
69
|
}
|
|
69
70
|
return null;
|
|
70
71
|
}
|
|
72
|
+
function omitTag(node) {
|
|
73
|
+
const optionalTagNode = node;
|
|
74
|
+
optionalTagNode.optionalTagName = typeof node.tag === 'string' ? node.tag : undefined;
|
|
75
|
+
// @ts-expect-error -- deliberately set tag to false
|
|
76
|
+
node.tag = false;
|
|
77
|
+
}
|
|
78
|
+
function getNodeTagName(node) {
|
|
79
|
+
if (node.tag) return node.tag;
|
|
80
|
+
const optionalTagNode = node;
|
|
81
|
+
return optionalTagNode.optionalTagName || false;
|
|
82
|
+
}
|
|
71
83
|
function removeOptionalTags(tree) {
|
|
72
84
|
tree.forEach((node, index)=>{
|
|
73
85
|
if (typeof node === 'string') return node;
|
|
74
86
|
if (!node.tag) return node;
|
|
75
87
|
if (node.attrs && Object.keys(node.attrs).length) return node;
|
|
76
88
|
// const prevNode = getPrevNode(tree, index);
|
|
77
|
-
const
|
|
89
|
+
const prevNode = getPrevNode(tree, index);
|
|
78
90
|
const nextNode = getNextNode(tree, index);
|
|
79
|
-
const nextNonEmptyNode = getNextNode(tree, index, true);
|
|
80
91
|
const firstChildNode = getFirstChildTag(node, false);
|
|
81
|
-
const firstNonEmptyChildNode = getFirstChildTag(node);
|
|
82
92
|
/**
|
|
83
93
|
* An "html" element's start tag may be omitted if the first thing inside the "html" element is not a comment.
|
|
84
94
|
* An "html" element's end tag may be omitted if the "html" element is not IMMEDIATELY followed by a comment.
|
|
85
95
|
*/ if (node.tag === 'html') {
|
|
86
96
|
let isHtmlStartTagCanBeOmitted = true;
|
|
87
97
|
let isHtmlEndTagCanBeOmitted = true;
|
|
88
|
-
if (typeof
|
|
98
|
+
if (typeof firstChildNode === 'string' && isComment(firstChildNode)) {
|
|
89
99
|
isHtmlStartTagCanBeOmitted = false;
|
|
90
100
|
}
|
|
91
|
-
if (typeof
|
|
101
|
+
if (typeof nextNode === 'string' && isComment(nextNode)) {
|
|
92
102
|
isHtmlEndTagCanBeOmitted = false;
|
|
93
103
|
}
|
|
94
104
|
if (isHtmlStartTagCanBeOmitted && isHtmlEndTagCanBeOmitted) {
|
|
95
|
-
|
|
96
|
-
node.tag = false;
|
|
105
|
+
omitTag(node);
|
|
97
106
|
}
|
|
98
107
|
}
|
|
99
108
|
/**
|
|
@@ -102,15 +111,14 @@ function removeOptionalTags(tree) {
|
|
|
102
111
|
*/ if (node.tag === 'head') {
|
|
103
112
|
let isHeadStartTagCanBeOmitted = false;
|
|
104
113
|
let isHeadEndTagCanBeOmitted = true;
|
|
105
|
-
if (isEmptyNode(node) ||
|
|
114
|
+
if (isEmptyNode(node) || firstChildNode && typeof firstChildNode === 'object' && firstChildNode.tag) {
|
|
106
115
|
isHeadStartTagCanBeOmitted = true;
|
|
107
116
|
}
|
|
108
|
-
if (nextNode && typeof nextNode === 'string' && startWithWhitespacePattern.test(nextNode) ||
|
|
117
|
+
if (nextNode && typeof nextNode === 'string' && (startWithWhitespacePattern.test(nextNode) || isComment(nextNode))) {
|
|
109
118
|
isHeadEndTagCanBeOmitted = false;
|
|
110
119
|
}
|
|
111
120
|
if (isHeadStartTagCanBeOmitted && isHeadEndTagCanBeOmitted) {
|
|
112
|
-
|
|
113
|
-
node.tag = false;
|
|
121
|
+
omitTag(node);
|
|
114
122
|
}
|
|
115
123
|
}
|
|
116
124
|
/**
|
|
@@ -119,18 +127,17 @@ function removeOptionalTags(tree) {
|
|
|
119
127
|
*/ if (node.tag === 'body') {
|
|
120
128
|
let isBodyStartTagCanBeOmitted = true;
|
|
121
129
|
let isBodyEndTagCanBeOmitted = true;
|
|
122
|
-
if (typeof firstChildNode === 'string' && startWithWhitespacePattern.test(firstChildNode) || typeof
|
|
130
|
+
if (typeof firstChildNode === 'string' && startWithWhitespacePattern.test(firstChildNode) || typeof firstChildNode === 'string' && isComment(firstChildNode)) {
|
|
123
131
|
isBodyStartTagCanBeOmitted = false;
|
|
124
132
|
}
|
|
125
|
-
if (
|
|
133
|
+
if (firstChildNode && typeof firstChildNode === 'object' && firstChildNode.tag && bodyStartTagCantBeOmittedWithFirstChildTags.has(firstChildNode.tag)) {
|
|
126
134
|
isBodyStartTagCanBeOmitted = false;
|
|
127
135
|
}
|
|
128
136
|
if (nextNode && typeof nextNode === 'string' && isComment(nextNode)) {
|
|
129
137
|
isBodyEndTagCanBeOmitted = false;
|
|
130
138
|
}
|
|
131
139
|
if (isBodyStartTagCanBeOmitted && isBodyEndTagCanBeOmitted) {
|
|
132
|
-
|
|
133
|
-
node.tag = false;
|
|
140
|
+
omitTag(node);
|
|
134
141
|
}
|
|
135
142
|
}
|
|
136
143
|
/**
|
|
@@ -139,18 +146,17 @@ function removeOptionalTags(tree) {
|
|
|
139
146
|
*/ if (node.tag === 'colgroup') {
|
|
140
147
|
let isColgroupStartTagCanBeOmitted = false;
|
|
141
148
|
let isColgroupEndTagCanBeOmitted = true;
|
|
142
|
-
if (
|
|
149
|
+
if (firstChildNode && typeof firstChildNode === 'object' && firstChildNode.tag && firstChildNode.tag === 'col') {
|
|
143
150
|
isColgroupStartTagCanBeOmitted = true;
|
|
144
151
|
}
|
|
145
|
-
if (
|
|
152
|
+
if (prevNode && typeof prevNode === 'object' && getNodeTagName(prevNode) === 'colgroup') {
|
|
146
153
|
isColgroupStartTagCanBeOmitted = false;
|
|
147
154
|
}
|
|
148
|
-
if (nextNode && typeof nextNode === 'string' && startWithWhitespacePattern.test(nextNode) ||
|
|
155
|
+
if (nextNode && typeof nextNode === 'string' && (startWithWhitespacePattern.test(nextNode) || isComment(nextNode))) {
|
|
149
156
|
isColgroupEndTagCanBeOmitted = false;
|
|
150
157
|
}
|
|
151
158
|
if (isColgroupStartTagCanBeOmitted && isColgroupEndTagCanBeOmitted) {
|
|
152
|
-
|
|
153
|
-
node.tag = false;
|
|
159
|
+
omitTag(node);
|
|
154
160
|
}
|
|
155
161
|
}
|
|
156
162
|
/**
|
|
@@ -159,18 +165,23 @@ function removeOptionalTags(tree) {
|
|
|
159
165
|
*/ if (node.tag === 'tbody') {
|
|
160
166
|
let isTbodyStartTagCanBeOmitted = false;
|
|
161
167
|
let isTbodyEndTagCanBeOmitted = true;
|
|
162
|
-
if (
|
|
168
|
+
if (firstChildNode && typeof firstChildNode === 'object' && firstChildNode.tag && firstChildNode.tag === 'tr') {
|
|
163
169
|
isTbodyStartTagCanBeOmitted = true;
|
|
164
170
|
}
|
|
165
|
-
if (
|
|
166
|
-
|
|
171
|
+
if (prevNode && typeof prevNode === 'object') {
|
|
172
|
+
const prevTagName = getNodeTagName(prevNode);
|
|
173
|
+
if (prevTagName && tbodyStartTagCantBeOmittedWithPrecededTags.has(prevTagName)) {
|
|
174
|
+
isTbodyStartTagCanBeOmitted = false;
|
|
175
|
+
}
|
|
167
176
|
}
|
|
168
|
-
if (
|
|
169
|
-
|
|
177
|
+
if (nextNode && typeof nextNode === 'object') {
|
|
178
|
+
const nextTagName = getNodeTagName(nextNode);
|
|
179
|
+
if (nextTagName && tbodyEndTagCantBeOmittedWithFollowedTags.has(nextTagName)) {
|
|
180
|
+
isTbodyEndTagCanBeOmitted = false;
|
|
181
|
+
}
|
|
170
182
|
}
|
|
171
183
|
if (isTbodyStartTagCanBeOmitted && isTbodyEndTagCanBeOmitted) {
|
|
172
|
-
|
|
173
|
-
node.tag = false;
|
|
184
|
+
omitTag(node);
|
|
174
185
|
}
|
|
175
186
|
}
|
|
176
187
|
if (node.content && node.content.length) {
|
|
@@ -2,16 +2,24 @@ import PostHTML from 'posthtml';
|
|
|
2
2
|
import { MinifyOptions } from 'terser';
|
|
3
3
|
import { Options } from 'cssnano';
|
|
4
4
|
import { Config } from 'svgo';
|
|
5
|
+
import { UserDefinedOptions } from 'purgecss';
|
|
5
6
|
|
|
6
7
|
type PostHTMLTreeLike = [PostHTML.Node] & PostHTML.NodeAPI & {
|
|
7
8
|
options?: {
|
|
8
9
|
quoteAllAttributes?: boolean | undefined;
|
|
10
|
+
quoteStyle?: 0 | 1 | 2 | undefined;
|
|
11
|
+
replaceQuote?: boolean | undefined;
|
|
9
12
|
} | undefined;
|
|
10
13
|
render(): string;
|
|
11
14
|
render(node: PostHTML.Node | PostHTMLTreeLike, renderOptions?: any): string;
|
|
12
15
|
};
|
|
13
16
|
type MaybeArray<T> = T | Array<T>;
|
|
14
17
|
type PostHTMLNodeLike = PostHTML.Node | string;
|
|
18
|
+
type HtmlnanoTemplateRule = {
|
|
19
|
+
tag: string;
|
|
20
|
+
attrs?: Record<string, string | boolean | void>;
|
|
21
|
+
};
|
|
22
|
+
type MinifyHtmlTemplateOptions = boolean | HtmlnanoTemplateRule[];
|
|
15
23
|
interface HtmlnanoOptions {
|
|
16
24
|
skipConfigLoading?: boolean;
|
|
17
25
|
configPath?: string;
|
|
@@ -27,17 +35,42 @@ interface HtmlnanoOptions {
|
|
|
27
35
|
mergeStyles?: boolean;
|
|
28
36
|
mergeScripts?: boolean;
|
|
29
37
|
minifyCss?: Options | boolean;
|
|
38
|
+
minifyHtmlTemplate?: MinifyHtmlTemplateOptions;
|
|
30
39
|
minifyConditionalComments?: boolean;
|
|
31
40
|
minifyJs?: MinifyOptions | boolean;
|
|
32
41
|
minifyJson?: boolean;
|
|
42
|
+
minifyAttributes?: boolean | {
|
|
43
|
+
metaContent?: boolean;
|
|
44
|
+
redundantWhitespaces?: 'safe' | 'agressive' | false;
|
|
45
|
+
};
|
|
33
46
|
minifySvg?: Config | boolean;
|
|
34
47
|
normalizeAttributeValues?: boolean;
|
|
35
|
-
removeAttributeQuotes?: boolean
|
|
36
|
-
|
|
48
|
+
removeAttributeQuotes?: boolean | {
|
|
49
|
+
force?: boolean;
|
|
50
|
+
};
|
|
51
|
+
removeComments?: boolean | RegExp | ((comment: string) => boolean) | string;
|
|
37
52
|
removeEmptyAttributes?: boolean;
|
|
53
|
+
removeEmptyElements?: boolean | {
|
|
54
|
+
removeWithAttributes?: boolean;
|
|
55
|
+
};
|
|
38
56
|
removeRedundantAttributes?: boolean;
|
|
39
57
|
removeOptionalTags?: boolean;
|
|
40
|
-
removeUnusedCss?: boolean
|
|
58
|
+
removeUnusedCss?: boolean | ({
|
|
59
|
+
tool: 'purgeCSS';
|
|
60
|
+
} & Omit<UserDefinedOptions, 'content' | 'css' | 'extractors'>) | {
|
|
61
|
+
banner?: boolean;
|
|
62
|
+
csspath?: string;
|
|
63
|
+
htmlroot?: string;
|
|
64
|
+
ignore?: (string | RegExp)[];
|
|
65
|
+
inject?: string;
|
|
66
|
+
jsdom?: object;
|
|
67
|
+
media?: string[];
|
|
68
|
+
report?: boolean;
|
|
69
|
+
strictSSL?: boolean;
|
|
70
|
+
timeout?: number;
|
|
71
|
+
uncssrc?: string;
|
|
72
|
+
userAgent?: string;
|
|
73
|
+
};
|
|
41
74
|
sortAttributes?: boolean | 'alphabetical' | 'frequency';
|
|
42
75
|
sortAttributesWithLists?: boolean | 'alphabetical' | 'frequency';
|
|
43
76
|
}
|
|
@@ -2,16 +2,24 @@ import PostHTML from 'posthtml';
|
|
|
2
2
|
import { MinifyOptions } from 'terser';
|
|
3
3
|
import { Options } from 'cssnano';
|
|
4
4
|
import { Config } from 'svgo';
|
|
5
|
+
import { UserDefinedOptions } from 'purgecss';
|
|
5
6
|
|
|
6
7
|
type PostHTMLTreeLike = [PostHTML.Node] & PostHTML.NodeAPI & {
|
|
7
8
|
options?: {
|
|
8
9
|
quoteAllAttributes?: boolean | undefined;
|
|
10
|
+
quoteStyle?: 0 | 1 | 2 | undefined;
|
|
11
|
+
replaceQuote?: boolean | undefined;
|
|
9
12
|
} | undefined;
|
|
10
13
|
render(): string;
|
|
11
14
|
render(node: PostHTML.Node | PostHTMLTreeLike, renderOptions?: any): string;
|
|
12
15
|
};
|
|
13
16
|
type MaybeArray<T> = T | Array<T>;
|
|
14
17
|
type PostHTMLNodeLike = PostHTML.Node | string;
|
|
18
|
+
type HtmlnanoTemplateRule = {
|
|
19
|
+
tag: string;
|
|
20
|
+
attrs?: Record<string, string | boolean | void>;
|
|
21
|
+
};
|
|
22
|
+
type MinifyHtmlTemplateOptions = boolean | HtmlnanoTemplateRule[];
|
|
15
23
|
interface HtmlnanoOptions {
|
|
16
24
|
skipConfigLoading?: boolean;
|
|
17
25
|
configPath?: string;
|
|
@@ -27,17 +35,42 @@ interface HtmlnanoOptions {
|
|
|
27
35
|
mergeStyles?: boolean;
|
|
28
36
|
mergeScripts?: boolean;
|
|
29
37
|
minifyCss?: Options | boolean;
|
|
38
|
+
minifyHtmlTemplate?: MinifyHtmlTemplateOptions;
|
|
30
39
|
minifyConditionalComments?: boolean;
|
|
31
40
|
minifyJs?: MinifyOptions | boolean;
|
|
32
41
|
minifyJson?: boolean;
|
|
42
|
+
minifyAttributes?: boolean | {
|
|
43
|
+
metaContent?: boolean;
|
|
44
|
+
redundantWhitespaces?: 'safe' | 'agressive' | false;
|
|
45
|
+
};
|
|
33
46
|
minifySvg?: Config | boolean;
|
|
34
47
|
normalizeAttributeValues?: boolean;
|
|
35
|
-
removeAttributeQuotes?: boolean
|
|
36
|
-
|
|
48
|
+
removeAttributeQuotes?: boolean | {
|
|
49
|
+
force?: boolean;
|
|
50
|
+
};
|
|
51
|
+
removeComments?: boolean | RegExp | ((comment: string) => boolean) | string;
|
|
37
52
|
removeEmptyAttributes?: boolean;
|
|
53
|
+
removeEmptyElements?: boolean | {
|
|
54
|
+
removeWithAttributes?: boolean;
|
|
55
|
+
};
|
|
38
56
|
removeRedundantAttributes?: boolean;
|
|
39
57
|
removeOptionalTags?: boolean;
|
|
40
|
-
removeUnusedCss?: boolean
|
|
58
|
+
removeUnusedCss?: boolean | ({
|
|
59
|
+
tool: 'purgeCSS';
|
|
60
|
+
} & Omit<UserDefinedOptions, 'content' | 'css' | 'extractors'>) | {
|
|
61
|
+
banner?: boolean;
|
|
62
|
+
csspath?: string;
|
|
63
|
+
htmlroot?: string;
|
|
64
|
+
ignore?: (string | RegExp)[];
|
|
65
|
+
inject?: string;
|
|
66
|
+
jsdom?: object;
|
|
67
|
+
media?: string[];
|
|
68
|
+
report?: boolean;
|
|
69
|
+
strictSSL?: boolean;
|
|
70
|
+
timeout?: number;
|
|
71
|
+
uncssrc?: string;
|
|
72
|
+
userAgent?: string;
|
|
73
|
+
};
|
|
41
74
|
sortAttributes?: boolean | 'alphabetical' | 'frequency';
|
|
42
75
|
sortAttributesWithLists?: boolean | 'alphabetical' | 'frequency';
|
|
43
76
|
}
|