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
|
}
|
|
@@ -67,22 +67,36 @@ const safeToRemoveAttrs = {
|
|
|
67
67
|
'object',
|
|
68
68
|
'video'
|
|
69
69
|
],
|
|
70
|
-
high:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
high: [
|
|
71
|
+
'meter'
|
|
72
|
+
],
|
|
73
|
+
href: [
|
|
74
|
+
'link'
|
|
75
|
+
],
|
|
76
|
+
list: [
|
|
77
|
+
'input'
|
|
78
|
+
],
|
|
79
|
+
low: [
|
|
80
|
+
'meter'
|
|
81
|
+
],
|
|
82
|
+
manifest: [
|
|
83
|
+
'html'
|
|
84
|
+
],
|
|
75
85
|
max: [
|
|
76
86
|
'meter',
|
|
77
87
|
'progress'
|
|
78
88
|
],
|
|
79
|
-
|
|
89
|
+
maxlength: [
|
|
80
90
|
'input',
|
|
81
91
|
'textarea'
|
|
82
92
|
],
|
|
83
|
-
menu:
|
|
84
|
-
|
|
85
|
-
|
|
93
|
+
menu: [
|
|
94
|
+
'button'
|
|
95
|
+
],
|
|
96
|
+
min: [
|
|
97
|
+
'meter'
|
|
98
|
+
],
|
|
99
|
+
minlength: [
|
|
86
100
|
'input',
|
|
87
101
|
'textarea'
|
|
88
102
|
],
|
|
@@ -119,7 +133,9 @@ const safeToRemoveAttrs = {
|
|
|
119
133
|
'area',
|
|
120
134
|
'link'
|
|
121
135
|
],
|
|
122
|
-
rows:
|
|
136
|
+
rows: [
|
|
137
|
+
'textarea'
|
|
138
|
+
],
|
|
123
139
|
rowspan: [
|
|
124
140
|
'td',
|
|
125
141
|
'th'
|
|
@@ -143,7 +159,9 @@ const safeToRemoveAttrs = {
|
|
|
143
159
|
'track',
|
|
144
160
|
'video'
|
|
145
161
|
],
|
|
146
|
-
start:
|
|
162
|
+
start: [
|
|
163
|
+
'ol'
|
|
164
|
+
],
|
|
147
165
|
tabindex: null,
|
|
148
166
|
type: [
|
|
149
167
|
'a',
|
|
@@ -180,12 +198,15 @@ const mod = {
|
|
|
180
198
|
const newAttrs = {
|
|
181
199
|
...attrs
|
|
182
200
|
};
|
|
201
|
+
const tagName = typeof node.tag === 'string' ? node.tag.toLowerCase() : '';
|
|
183
202
|
Object.entries(attrs).forEach(([attrName, attrValue])=>{
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
203
|
+
const normalizedAttrName = attrName.toLowerCase();
|
|
204
|
+
const safeAttr = safeToRemoveAttrs[normalizedAttrName];
|
|
205
|
+
if (helpers_js.isEventHandler(attrName) || safeAttr !== undefined && (safeAttr === null || tagName && safeAttr.includes(tagName))) {
|
|
206
|
+
if (attrValue === null || attrValue === undefined) {
|
|
207
|
+
delete newAttrs[attrName];
|
|
208
|
+
} else if (typeof attrValue === 'string' && attrValue.trim() === '') {
|
|
209
|
+
delete newAttrs[attrName];
|
|
189
210
|
}
|
|
190
211
|
}
|
|
191
212
|
});
|
|
@@ -65,22 +65,36 @@ const safeToRemoveAttrs = {
|
|
|
65
65
|
'object',
|
|
66
66
|
'video'
|
|
67
67
|
],
|
|
68
|
-
high:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
high: [
|
|
69
|
+
'meter'
|
|
70
|
+
],
|
|
71
|
+
href: [
|
|
72
|
+
'link'
|
|
73
|
+
],
|
|
74
|
+
list: [
|
|
75
|
+
'input'
|
|
76
|
+
],
|
|
77
|
+
low: [
|
|
78
|
+
'meter'
|
|
79
|
+
],
|
|
80
|
+
manifest: [
|
|
81
|
+
'html'
|
|
82
|
+
],
|
|
73
83
|
max: [
|
|
74
84
|
'meter',
|
|
75
85
|
'progress'
|
|
76
86
|
],
|
|
77
|
-
|
|
87
|
+
maxlength: [
|
|
78
88
|
'input',
|
|
79
89
|
'textarea'
|
|
80
90
|
],
|
|
81
|
-
menu:
|
|
82
|
-
|
|
83
|
-
|
|
91
|
+
menu: [
|
|
92
|
+
'button'
|
|
93
|
+
],
|
|
94
|
+
min: [
|
|
95
|
+
'meter'
|
|
96
|
+
],
|
|
97
|
+
minlength: [
|
|
84
98
|
'input',
|
|
85
99
|
'textarea'
|
|
86
100
|
],
|
|
@@ -117,7 +131,9 @@ const safeToRemoveAttrs = {
|
|
|
117
131
|
'area',
|
|
118
132
|
'link'
|
|
119
133
|
],
|
|
120
|
-
rows:
|
|
134
|
+
rows: [
|
|
135
|
+
'textarea'
|
|
136
|
+
],
|
|
121
137
|
rowspan: [
|
|
122
138
|
'td',
|
|
123
139
|
'th'
|
|
@@ -141,7 +157,9 @@ const safeToRemoveAttrs = {
|
|
|
141
157
|
'track',
|
|
142
158
|
'video'
|
|
143
159
|
],
|
|
144
|
-
start:
|
|
160
|
+
start: [
|
|
161
|
+
'ol'
|
|
162
|
+
],
|
|
145
163
|
tabindex: null,
|
|
146
164
|
type: [
|
|
147
165
|
'a',
|
|
@@ -178,12 +196,15 @@ const mod = {
|
|
|
178
196
|
const newAttrs = {
|
|
179
197
|
...attrs
|
|
180
198
|
};
|
|
199
|
+
const tagName = typeof node.tag === 'string' ? node.tag.toLowerCase() : '';
|
|
181
200
|
Object.entries(attrs).forEach(([attrName, attrValue])=>{
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
201
|
+
const normalizedAttrName = attrName.toLowerCase();
|
|
202
|
+
const safeAttr = safeToRemoveAttrs[normalizedAttrName];
|
|
203
|
+
if (isEventHandler(attrName) || safeAttr !== undefined && (safeAttr === null || tagName && safeAttr.includes(tagName))) {
|
|
204
|
+
if (attrValue === null || attrValue === undefined) {
|
|
205
|
+
delete newAttrs[attrName];
|
|
206
|
+
} else if (typeof attrValue === 'string' && attrValue.trim() === '') {
|
|
207
|
+
delete newAttrs[attrName];
|
|
187
208
|
}
|
|
188
209
|
}
|
|
189
210
|
});
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import PostHTML from 'posthtml';
|
|
2
|
+
import { MinifyOptions } from 'terser';
|
|
3
|
+
import { Options } from 'cssnano';
|
|
4
|
+
import { Config } from 'svgo';
|
|
5
|
+
import { UserDefinedOptions } from 'purgecss';
|
|
6
|
+
|
|
7
|
+
type PostHTMLTreeLike = [PostHTML.Node] & PostHTML.NodeAPI & {
|
|
8
|
+
options?: {
|
|
9
|
+
quoteAllAttributes?: boolean | undefined;
|
|
10
|
+
quoteStyle?: 0 | 1 | 2 | undefined;
|
|
11
|
+
replaceQuote?: boolean | undefined;
|
|
12
|
+
} | undefined;
|
|
13
|
+
render(): string;
|
|
14
|
+
render(node: PostHTML.Node | PostHTMLTreeLike, renderOptions?: any): string;
|
|
15
|
+
};
|
|
16
|
+
type MaybeArray<T> = T | Array<T>;
|
|
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[];
|
|
23
|
+
interface HtmlnanoOptions {
|
|
24
|
+
skipConfigLoading?: boolean;
|
|
25
|
+
configPath?: string;
|
|
26
|
+
skipInternalWarnings?: boolean;
|
|
27
|
+
collapseAttributeWhitespace?: boolean;
|
|
28
|
+
collapseBooleanAttributes?: {
|
|
29
|
+
amphtml?: boolean;
|
|
30
|
+
};
|
|
31
|
+
collapseWhitespace?: 'conservative' | 'all' | 'aggressive';
|
|
32
|
+
custom?: MaybeArray<(tree: PostHTMLTreeLike, options?: any) => (PostHTML.Node | PostHTMLTreeLike)>;
|
|
33
|
+
deduplicateAttributeValues?: boolean;
|
|
34
|
+
minifyUrls?: URL | string | false;
|
|
35
|
+
mergeStyles?: boolean;
|
|
36
|
+
mergeScripts?: boolean;
|
|
37
|
+
minifyCss?: Options | boolean;
|
|
38
|
+
minifyHtmlTemplate?: MinifyHtmlTemplateOptions;
|
|
39
|
+
minifyConditionalComments?: boolean;
|
|
40
|
+
minifyJs?: MinifyOptions | boolean;
|
|
41
|
+
minifyJson?: boolean;
|
|
42
|
+
minifyAttributes?: boolean | {
|
|
43
|
+
metaContent?: boolean;
|
|
44
|
+
redundantWhitespaces?: 'safe' | 'agressive' | false;
|
|
45
|
+
};
|
|
46
|
+
minifySvg?: Config | boolean;
|
|
47
|
+
normalizeAttributeValues?: boolean;
|
|
48
|
+
removeAttributeQuotes?: boolean | {
|
|
49
|
+
force?: boolean;
|
|
50
|
+
};
|
|
51
|
+
removeComments?: boolean | RegExp | ((comment: string) => boolean) | string;
|
|
52
|
+
removeEmptyAttributes?: boolean;
|
|
53
|
+
removeEmptyElements?: boolean | {
|
|
54
|
+
removeWithAttributes?: boolean;
|
|
55
|
+
};
|
|
56
|
+
removeRedundantAttributes?: boolean;
|
|
57
|
+
removeOptionalTags?: 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
|
+
};
|
|
74
|
+
sortAttributes?: boolean | 'alphabetical' | 'frequency';
|
|
75
|
+
sortAttributesWithLists?: boolean | 'alphabetical' | 'frequency';
|
|
76
|
+
}
|
|
77
|
+
type HtmlnanoModuleAttrsHandler = (attrs: Record<string, string | boolean | void>, node: PostHTML.Node) => Record<string, string | boolean | void>;
|
|
78
|
+
type HtmlnanoModuleContentHandler = (content: Array<PostHTMLNodeLike>, node: PostHTML.Node) => MaybeArray<PostHTMLNodeLike>;
|
|
79
|
+
type HtmlnanoModuleNodeHandler = (node: PostHTMLNodeLike) => PostHTML.Node | string;
|
|
80
|
+
type OptionalOptions<T> = T extends boolean | string | Function | number | null | undefined ? T : T extends object ? Partial<T> : T;
|
|
81
|
+
type HtmlnanoModule<Options = any> = {
|
|
82
|
+
onAttrs?: (options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => HtmlnanoModuleAttrsHandler;
|
|
83
|
+
onContent?: (options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => HtmlnanoModuleContentHandler;
|
|
84
|
+
onNode?: (options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => HtmlnanoModuleNodeHandler;
|
|
85
|
+
default?: (tree: PostHTMLTreeLike, options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => PostHTMLTreeLike | Promise<PostHTMLTreeLike>;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
interface RemoveEmptyElementsOptions {
|
|
89
|
+
removeWithAttributes?: boolean;
|
|
90
|
+
}
|
|
91
|
+
type RemoveEmptyElementsConfig = boolean | RemoveEmptyElementsOptions;
|
|
92
|
+
declare const mod: HtmlnanoModule<RemoveEmptyElementsConfig>;
|
|
93
|
+
|
|
94
|
+
export { mod as default };
|
|
95
|
+
export type { RemoveEmptyElementsOptions };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import PostHTML from 'posthtml';
|
|
2
|
+
import { MinifyOptions } from 'terser';
|
|
3
|
+
import { Options } from 'cssnano';
|
|
4
|
+
import { Config } from 'svgo';
|
|
5
|
+
import { UserDefinedOptions } from 'purgecss';
|
|
6
|
+
|
|
7
|
+
type PostHTMLTreeLike = [PostHTML.Node] & PostHTML.NodeAPI & {
|
|
8
|
+
options?: {
|
|
9
|
+
quoteAllAttributes?: boolean | undefined;
|
|
10
|
+
quoteStyle?: 0 | 1 | 2 | undefined;
|
|
11
|
+
replaceQuote?: boolean | undefined;
|
|
12
|
+
} | undefined;
|
|
13
|
+
render(): string;
|
|
14
|
+
render(node: PostHTML.Node | PostHTMLTreeLike, renderOptions?: any): string;
|
|
15
|
+
};
|
|
16
|
+
type MaybeArray<T> = T | Array<T>;
|
|
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[];
|
|
23
|
+
interface HtmlnanoOptions {
|
|
24
|
+
skipConfigLoading?: boolean;
|
|
25
|
+
configPath?: string;
|
|
26
|
+
skipInternalWarnings?: boolean;
|
|
27
|
+
collapseAttributeWhitespace?: boolean;
|
|
28
|
+
collapseBooleanAttributes?: {
|
|
29
|
+
amphtml?: boolean;
|
|
30
|
+
};
|
|
31
|
+
collapseWhitespace?: 'conservative' | 'all' | 'aggressive';
|
|
32
|
+
custom?: MaybeArray<(tree: PostHTMLTreeLike, options?: any) => (PostHTML.Node | PostHTMLTreeLike)>;
|
|
33
|
+
deduplicateAttributeValues?: boolean;
|
|
34
|
+
minifyUrls?: URL | string | false;
|
|
35
|
+
mergeStyles?: boolean;
|
|
36
|
+
mergeScripts?: boolean;
|
|
37
|
+
minifyCss?: Options | boolean;
|
|
38
|
+
minifyHtmlTemplate?: MinifyHtmlTemplateOptions;
|
|
39
|
+
minifyConditionalComments?: boolean;
|
|
40
|
+
minifyJs?: MinifyOptions | boolean;
|
|
41
|
+
minifyJson?: boolean;
|
|
42
|
+
minifyAttributes?: boolean | {
|
|
43
|
+
metaContent?: boolean;
|
|
44
|
+
redundantWhitespaces?: 'safe' | 'agressive' | false;
|
|
45
|
+
};
|
|
46
|
+
minifySvg?: Config | boolean;
|
|
47
|
+
normalizeAttributeValues?: boolean;
|
|
48
|
+
removeAttributeQuotes?: boolean | {
|
|
49
|
+
force?: boolean;
|
|
50
|
+
};
|
|
51
|
+
removeComments?: boolean | RegExp | ((comment: string) => boolean) | string;
|
|
52
|
+
removeEmptyAttributes?: boolean;
|
|
53
|
+
removeEmptyElements?: boolean | {
|
|
54
|
+
removeWithAttributes?: boolean;
|
|
55
|
+
};
|
|
56
|
+
removeRedundantAttributes?: boolean;
|
|
57
|
+
removeOptionalTags?: 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
|
+
};
|
|
74
|
+
sortAttributes?: boolean | 'alphabetical' | 'frequency';
|
|
75
|
+
sortAttributesWithLists?: boolean | 'alphabetical' | 'frequency';
|
|
76
|
+
}
|
|
77
|
+
type HtmlnanoModuleAttrsHandler = (attrs: Record<string, string | boolean | void>, node: PostHTML.Node) => Record<string, string | boolean | void>;
|
|
78
|
+
type HtmlnanoModuleContentHandler = (content: Array<PostHTMLNodeLike>, node: PostHTML.Node) => MaybeArray<PostHTMLNodeLike>;
|
|
79
|
+
type HtmlnanoModuleNodeHandler = (node: PostHTMLNodeLike) => PostHTML.Node | string;
|
|
80
|
+
type OptionalOptions<T> = T extends boolean | string | Function | number | null | undefined ? T : T extends object ? Partial<T> : T;
|
|
81
|
+
type HtmlnanoModule<Options = any> = {
|
|
82
|
+
onAttrs?: (options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => HtmlnanoModuleAttrsHandler;
|
|
83
|
+
onContent?: (options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => HtmlnanoModuleContentHandler;
|
|
84
|
+
onNode?: (options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => HtmlnanoModuleNodeHandler;
|
|
85
|
+
default?: (tree: PostHTMLTreeLike, options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => PostHTMLTreeLike | Promise<PostHTMLTreeLike>;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
interface RemoveEmptyElementsOptions {
|
|
89
|
+
removeWithAttributes?: boolean;
|
|
90
|
+
}
|
|
91
|
+
type RemoveEmptyElementsConfig = boolean | RemoveEmptyElementsOptions;
|
|
92
|
+
declare const mod: HtmlnanoModule<RemoveEmptyElementsConfig>;
|
|
93
|
+
|
|
94
|
+
export { mod as default };
|
|
95
|
+
export type { RemoveEmptyElementsOptions };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
2
|
+
|
|
3
|
+
var helpers_js = require('../helpers.js');
|
|
4
|
+
|
|
5
|
+
const voidElements = new Set([
|
|
6
|
+
'area',
|
|
7
|
+
'base',
|
|
8
|
+
'br',
|
|
9
|
+
'col',
|
|
10
|
+
'embed',
|
|
11
|
+
'hr',
|
|
12
|
+
'img',
|
|
13
|
+
'input',
|
|
14
|
+
'link',
|
|
15
|
+
'meta',
|
|
16
|
+
'param',
|
|
17
|
+
'source',
|
|
18
|
+
'track',
|
|
19
|
+
'wbr'
|
|
20
|
+
]);
|
|
21
|
+
function normalizeOptions(moduleOptions) {
|
|
22
|
+
if (typeof moduleOptions === 'object' && moduleOptions) {
|
|
23
|
+
return {
|
|
24
|
+
removeWithAttributes: moduleOptions.removeWithAttributes === true
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
removeWithAttributes: false
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function hasAttributes(node) {
|
|
32
|
+
return !!node.attrs && Object.keys(node.attrs).length > 0;
|
|
33
|
+
}
|
|
34
|
+
function isIgnorableText(text) {
|
|
35
|
+
return helpers_js.isComment(text) || text.trim() === '';
|
|
36
|
+
}
|
|
37
|
+
function isEmptyContent(content) {
|
|
38
|
+
if (!content) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
const contentArray = Array.isArray(content) ? content : [
|
|
42
|
+
content
|
|
43
|
+
];
|
|
44
|
+
if (contentArray.length === 0) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
return contentArray.every((child)=>typeof child === 'string' && isIgnorableText(child));
|
|
48
|
+
}
|
|
49
|
+
function shouldRemoveNode(node, options) {
|
|
50
|
+
if (!node.tag || typeof node.tag !== 'string') {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
if (voidElements.has(node.tag.toLowerCase())) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
if (!options.removeWithAttributes && hasAttributes(node)) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
return isEmptyContent(node.content);
|
|
60
|
+
}
|
|
61
|
+
function pruneNodes(nodes, options) {
|
|
62
|
+
const result = [];
|
|
63
|
+
for (const node of nodes){
|
|
64
|
+
if (typeof node === 'string') {
|
|
65
|
+
result.push(node);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (node.content) {
|
|
69
|
+
const contentArray = Array.isArray(node.content) ? node.content : [
|
|
70
|
+
node.content
|
|
71
|
+
];
|
|
72
|
+
node.content = pruneNodes(contentArray, options);
|
|
73
|
+
}
|
|
74
|
+
if (shouldRemoveNode(node, options)) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
result.push(node);
|
|
78
|
+
}
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
const mod = {
|
|
82
|
+
default (tree, _options, moduleOptions) {
|
|
83
|
+
const normalizedOptions = normalizeOptions(moduleOptions);
|
|
84
|
+
const pruned = pruneNodes(tree, normalizedOptions);
|
|
85
|
+
tree.splice(0, tree.length, ...pruned);
|
|
86
|
+
return tree;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
exports.default = mod;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { isComment } from '../helpers.mjs';
|
|
2
|
+
|
|
3
|
+
const voidElements = new Set([
|
|
4
|
+
'area',
|
|
5
|
+
'base',
|
|
6
|
+
'br',
|
|
7
|
+
'col',
|
|
8
|
+
'embed',
|
|
9
|
+
'hr',
|
|
10
|
+
'img',
|
|
11
|
+
'input',
|
|
12
|
+
'link',
|
|
13
|
+
'meta',
|
|
14
|
+
'param',
|
|
15
|
+
'source',
|
|
16
|
+
'track',
|
|
17
|
+
'wbr'
|
|
18
|
+
]);
|
|
19
|
+
function normalizeOptions(moduleOptions) {
|
|
20
|
+
if (typeof moduleOptions === 'object' && moduleOptions) {
|
|
21
|
+
return {
|
|
22
|
+
removeWithAttributes: moduleOptions.removeWithAttributes === true
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
removeWithAttributes: false
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function hasAttributes(node) {
|
|
30
|
+
return !!node.attrs && Object.keys(node.attrs).length > 0;
|
|
31
|
+
}
|
|
32
|
+
function isIgnorableText(text) {
|
|
33
|
+
return isComment(text) || text.trim() === '';
|
|
34
|
+
}
|
|
35
|
+
function isEmptyContent(content) {
|
|
36
|
+
if (!content) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
const contentArray = Array.isArray(content) ? content : [
|
|
40
|
+
content
|
|
41
|
+
];
|
|
42
|
+
if (contentArray.length === 0) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
return contentArray.every((child)=>typeof child === 'string' && isIgnorableText(child));
|
|
46
|
+
}
|
|
47
|
+
function shouldRemoveNode(node, options) {
|
|
48
|
+
if (!node.tag || typeof node.tag !== 'string') {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
if (voidElements.has(node.tag.toLowerCase())) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
if (!options.removeWithAttributes && hasAttributes(node)) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return isEmptyContent(node.content);
|
|
58
|
+
}
|
|
59
|
+
function pruneNodes(nodes, options) {
|
|
60
|
+
const result = [];
|
|
61
|
+
for (const node of nodes){
|
|
62
|
+
if (typeof node === 'string') {
|
|
63
|
+
result.push(node);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (node.content) {
|
|
67
|
+
const contentArray = Array.isArray(node.content) ? node.content : [
|
|
68
|
+
node.content
|
|
69
|
+
];
|
|
70
|
+
node.content = pruneNodes(contentArray, options);
|
|
71
|
+
}
|
|
72
|
+
if (shouldRemoveNode(node, options)) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
result.push(node);
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
const mod = {
|
|
80
|
+
default (tree, _options, moduleOptions) {
|
|
81
|
+
const normalizedOptions = normalizeOptions(moduleOptions);
|
|
82
|
+
const pruned = pruneNodes(tree, normalizedOptions);
|
|
83
|
+
tree.splice(0, tree.length, ...pruned);
|
|
84
|
+
return tree;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export { mod as default };
|