htmlnano 3.0.0 → 3.1.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 +18 -17
- 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 +99 -24
- package/dist/_modules/mergeScripts.mjs +99 -24
- package/dist/_modules/mergeStyles.d.mts +36 -3
- package/dist/_modules/mergeStyles.d.ts +36 -3
- package/dist/_modules/mergeStyles.js +56 -4
- package/dist/_modules/mergeStyles.mjs +56 -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 +94 -5
- package/dist/_modules/minifyJs.mjs +95 -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 +38 -13
- package/dist/_modules/removeUnusedCss.mjs +39 -14
- package/dist/_modules/sortAttributes.d.mts +36 -3
- package/dist/_modules/sortAttributes.d.ts +36 -3
- package/dist/_modules/sortAttributes.js +23 -5
- package/dist/_modules/sortAttributes.mjs +23 -5
- package/dist/_modules/sortAttributesWithLists.d.mts +36 -3
- package/dist/_modules/sortAttributesWithLists.d.ts +36 -3
- package/dist/_modules/sortAttributesWithLists.js +30 -8
- package/dist/_modules/sortAttributesWithLists.mjs +31 -9
- 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 +4 -0
- package/dist/presets/max.mjs +4 -0
- 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 +21 -13
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { normalizeMimeType } from '../helpers.mjs';
|
|
2
|
+
|
|
3
|
+
const defaultRules = [
|
|
4
|
+
{
|
|
5
|
+
tag: 'script',
|
|
6
|
+
attrs: {
|
|
7
|
+
type: 'text/html'
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
tag: 'script',
|
|
12
|
+
attrs: {
|
|
13
|
+
type: 'text/template'
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
tag: 'script',
|
|
18
|
+
attrs: {
|
|
19
|
+
type: 'text/x-template'
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
tag: 'script',
|
|
24
|
+
attrs: {
|
|
25
|
+
type: 'text/x-handlebars-template'
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
tag: 'script',
|
|
30
|
+
attrs: {
|
|
31
|
+
type: 'text/x-handlebars'
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
tag: 'script',
|
|
36
|
+
attrs: {
|
|
37
|
+
type: 'text/x-mustache-template'
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
tag: 'script',
|
|
42
|
+
attrs: {
|
|
43
|
+
type: 'text/x-underscore-template'
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
tag: 'script',
|
|
48
|
+
attrs: {
|
|
49
|
+
type: 'text/x-jsrender'
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
tag: 'script',
|
|
54
|
+
attrs: {
|
|
55
|
+
type: 'text/x-jquery-tmpl'
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
tag: 'script',
|
|
60
|
+
attrs: {
|
|
61
|
+
type: 'text/x-kendo-template'
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
tag: 'script',
|
|
66
|
+
attrs: {
|
|
67
|
+
type: 'text/ng-template'
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
tag: 'template'
|
|
72
|
+
}
|
|
73
|
+
];
|
|
74
|
+
const mod = {
|
|
75
|
+
async default (tree, options, moduleOptions) {
|
|
76
|
+
const rules = resolveTemplateRules(moduleOptions);
|
|
77
|
+
if (!rules.length) {
|
|
78
|
+
return tree;
|
|
79
|
+
}
|
|
80
|
+
const { default: htmlnano } = await import('../index.mjs');
|
|
81
|
+
const innerOptions = {
|
|
82
|
+
...options,
|
|
83
|
+
minifyHtmlTemplate: false,
|
|
84
|
+
skipConfigLoading: true
|
|
85
|
+
};
|
|
86
|
+
const promises = [];
|
|
87
|
+
tree.walk((node)=>{
|
|
88
|
+
if (!node.tag) {
|
|
89
|
+
return node;
|
|
90
|
+
}
|
|
91
|
+
if (!matchesTemplateRule(node, rules)) {
|
|
92
|
+
return node;
|
|
93
|
+
}
|
|
94
|
+
if (node.attrs && 'integrity' in node.attrs) {
|
|
95
|
+
return node;
|
|
96
|
+
}
|
|
97
|
+
if (node.tag === 'script' && node.attrs && 'src' in node.attrs) {
|
|
98
|
+
return node;
|
|
99
|
+
}
|
|
100
|
+
const rawContent = collectNodeContent(tree, node);
|
|
101
|
+
if (rawContent.trim().length === 0) {
|
|
102
|
+
return node;
|
|
103
|
+
}
|
|
104
|
+
const promise = htmlnano.process(rawContent, innerOptions, {}, {}).then((result)=>{
|
|
105
|
+
node.content = [
|
|
106
|
+
result.html
|
|
107
|
+
];
|
|
108
|
+
});
|
|
109
|
+
promises.push(promise);
|
|
110
|
+
return node;
|
|
111
|
+
});
|
|
112
|
+
return Promise.all(promises).then(()=>tree);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
function resolveTemplateRules(moduleOptions) {
|
|
116
|
+
if (!moduleOptions || moduleOptions === true) {
|
|
117
|
+
return normalizeRules(defaultRules);
|
|
118
|
+
}
|
|
119
|
+
if (Array.isArray(moduleOptions)) {
|
|
120
|
+
return normalizeRules(moduleOptions);
|
|
121
|
+
}
|
|
122
|
+
return normalizeRules(defaultRules);
|
|
123
|
+
}
|
|
124
|
+
function normalizeRules(rules) {
|
|
125
|
+
const normalized = [];
|
|
126
|
+
for (const rule of rules){
|
|
127
|
+
const resolved = normalizeRule(rule);
|
|
128
|
+
if (resolved) {
|
|
129
|
+
normalized.push(resolved);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return normalized;
|
|
133
|
+
}
|
|
134
|
+
function normalizeRule(rule) {
|
|
135
|
+
if (rule && typeof rule === 'object') {
|
|
136
|
+
if (typeof rule.tag !== 'string' || !rule.tag.trim()) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
const attrs = {};
|
|
140
|
+
if (rule.attrs && typeof rule.attrs === 'object') {
|
|
141
|
+
for (const [name, value] of Object.entries(rule.attrs)){
|
|
142
|
+
if (typeof name !== 'string' || !name.trim()) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
attrs[name.toLowerCase()] = value != null ? value : true;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
tag: rule.tag.toLowerCase(),
|
|
150
|
+
attrs: Object.keys(attrs).length ? attrs : undefined
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
function matchesTemplateRule(node, rules) {
|
|
156
|
+
var _node_tag;
|
|
157
|
+
const tag = (_node_tag = node.tag) == null ? void 0 : _node_tag.toLowerCase();
|
|
158
|
+
if (!tag) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
const attrs = normalizeNodeAttrs(node);
|
|
162
|
+
for (const rule of rules){
|
|
163
|
+
if (tag !== rule.tag) {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (!rule.attrs) {
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
let match = true;
|
|
170
|
+
for (const [attrName, ruleValue] of Object.entries(rule.attrs)){
|
|
171
|
+
if (!(attrName in attrs)) {
|
|
172
|
+
match = false;
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
if (ruleValue === true) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
const nodeValue = normalizeAttrValue(attrName, attrs[attrName]);
|
|
179
|
+
const normalizedRuleValue = normalizeAttrValue(attrName, ruleValue);
|
|
180
|
+
if (nodeValue !== normalizedRuleValue) {
|
|
181
|
+
match = false;
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (match) {
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
function normalizeNodeAttrs(node) {
|
|
192
|
+
if (!node.attrs) {
|
|
193
|
+
return {};
|
|
194
|
+
}
|
|
195
|
+
const normalized = {};
|
|
196
|
+
for (const [key, value] of Object.entries(node.attrs)){
|
|
197
|
+
normalized[key.toLowerCase()] = value;
|
|
198
|
+
}
|
|
199
|
+
return normalized;
|
|
200
|
+
}
|
|
201
|
+
function normalizeAttrValue(attrName, value) {
|
|
202
|
+
if (typeof value !== 'string') {
|
|
203
|
+
return value;
|
|
204
|
+
}
|
|
205
|
+
if (attrName === 'type') {
|
|
206
|
+
return normalizeMimeType(value);
|
|
207
|
+
}
|
|
208
|
+
return value;
|
|
209
|
+
}
|
|
210
|
+
function collectNodeContent(tree, node) {
|
|
211
|
+
if (!node.content) {
|
|
212
|
+
return '';
|
|
213
|
+
}
|
|
214
|
+
const content = typeof node.content === 'string' ? [
|
|
215
|
+
node.content
|
|
216
|
+
] : node.content;
|
|
217
|
+
let html = '';
|
|
218
|
+
for (const child of content){
|
|
219
|
+
if (typeof child === 'string') {
|
|
220
|
+
html += child;
|
|
221
|
+
} else {
|
|
222
|
+
html += tree.render(child);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return html;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export { mod as default, defaultRules };
|
|
@@ -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
|
}
|
|
@@ -26,20 +26,25 @@ var removeRedundantAttributes_js = require('./removeRedundantAttributes.js');
|
|
|
26
26
|
return node;
|
|
27
27
|
}
|
|
28
28
|
if (node.tag && node.tag === 'script') {
|
|
29
|
-
const
|
|
29
|
+
const rawMimeType = typeof nodeAttrs.type === 'string' ? helpers_js.normalizeMimeType(nodeAttrs.type) : undefined;
|
|
30
|
+
const mimeType = rawMimeType || 'text/javascript';
|
|
30
31
|
if (removeRedundantAttributes_js.redundantScriptTypes.has(mimeType) || mimeType === 'module') {
|
|
31
|
-
|
|
32
|
+
const scriptTerserOptions = resolveScriptTerserOptions(terserOptions, mimeType);
|
|
33
|
+
p = processScriptNode(node, scriptTerserOptions, terser);
|
|
32
34
|
if (p) {
|
|
33
35
|
promises.push(p);
|
|
34
36
|
}
|
|
35
37
|
}
|
|
36
38
|
}
|
|
37
39
|
if (node.attrs) {
|
|
38
|
-
promises.push
|
|
40
|
+
promises.push(...processNodeWithOnAttrs(node, terserOptions, terser));
|
|
39
41
|
}
|
|
40
42
|
return node;
|
|
41
43
|
});
|
|
42
|
-
return Promise.all(promises).then(()=>
|
|
44
|
+
return Promise.all(promises).then(()=>{
|
|
45
|
+
applySmartQuoteOptions(tree);
|
|
46
|
+
return tree;
|
|
47
|
+
});
|
|
43
48
|
}
|
|
44
49
|
};
|
|
45
50
|
function stripCdata(js) {
|
|
@@ -50,6 +55,89 @@ function stripCdata(js) {
|
|
|
50
55
|
const strippedJs = leftStrippedJs.replace(/\/\/\s*\]\]>/, '').replace(/\/\*\s*\]\]>\s*\*\//, '');
|
|
51
56
|
return leftStrippedJs === strippedJs ? js : strippedJs;
|
|
52
57
|
}
|
|
58
|
+
function resolveScriptTerserOptions(terserOptions, mimeType) {
|
|
59
|
+
var _terserOptions_toplevel, _terserOptions_compress, _terserOptions_mangle;
|
|
60
|
+
if (mimeType !== 'module' || terserOptions.module !== undefined) {
|
|
61
|
+
return terserOptions;
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
...terserOptions,
|
|
65
|
+
module: true,
|
|
66
|
+
toplevel: (_terserOptions_toplevel = terserOptions.toplevel) != null ? _terserOptions_toplevel : false,
|
|
67
|
+
compress: (_terserOptions_compress = terserOptions.compress) != null ? _terserOptions_compress : false,
|
|
68
|
+
mangle: (_terserOptions_mangle = terserOptions.mangle) != null ? _terserOptions_mangle : false
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function resolveOnAttrTerserOptions(terserOptions) {
|
|
72
|
+
const output = terserOptions.output;
|
|
73
|
+
const format = terserOptions.format;
|
|
74
|
+
const outputHasQuoteStyle = !!(output && typeof output === 'object' && 'quote_style' in output);
|
|
75
|
+
const formatHasQuoteStyle = !!(format && typeof format === 'object' && 'quote_style' in format);
|
|
76
|
+
if (outputHasQuoteStyle || formatHasQuoteStyle) {
|
|
77
|
+
return terserOptions;
|
|
78
|
+
}
|
|
79
|
+
const resolved = {
|
|
80
|
+
...terserOptions
|
|
81
|
+
};
|
|
82
|
+
if (format && typeof format === 'object') {
|
|
83
|
+
resolved.format = {
|
|
84
|
+
...format,
|
|
85
|
+
['quote_style']: 3
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (output && typeof output === 'object') {
|
|
89
|
+
resolved.output = {
|
|
90
|
+
...output,
|
|
91
|
+
['quote_style']: 3
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
if (!format && !output) {
|
|
95
|
+
resolved.output = {
|
|
96
|
+
['quote_style']: 3
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return resolved;
|
|
100
|
+
}
|
|
101
|
+
function applySmartQuoteOptions(tree) {
|
|
102
|
+
var _tree, _options, _tree_options, _quoteStyle, _tree_options1, _replaceQuote;
|
|
103
|
+
const quoteState = analyzeTreeQuotes(tree);
|
|
104
|
+
if (!quoteState.needsSmartQuotes || quoteState.hasMixedQuotes) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
(_options = (_tree = tree).options) != null ? _options : _tree.options = {};
|
|
108
|
+
(_quoteStyle = (_tree_options = tree.options).quoteStyle) != null ? _quoteStyle : _tree_options.quoteStyle = 0;
|
|
109
|
+
(_replaceQuote = (_tree_options1 = tree.options).replaceQuote) != null ? _replaceQuote : _tree_options1.replaceQuote = false;
|
|
110
|
+
}
|
|
111
|
+
function analyzeTreeQuotes(tree) {
|
|
112
|
+
let needsSmartQuotes = false;
|
|
113
|
+
let hasMixedQuotes = false;
|
|
114
|
+
tree.walk((node)=>{
|
|
115
|
+
if (!node || !node.attrs) {
|
|
116
|
+
return node;
|
|
117
|
+
}
|
|
118
|
+
for (const [attrName, attrValue] of Object.entries(node.attrs)){
|
|
119
|
+
if (typeof attrValue !== 'string') {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const hasDoubleQuote = attrValue.includes('"');
|
|
123
|
+
const hasSingleQuote = attrValue.includes('\'');
|
|
124
|
+
if (hasDoubleQuote && helpers_js.isEventHandler(attrName)) {
|
|
125
|
+
needsSmartQuotes = true;
|
|
126
|
+
}
|
|
127
|
+
if (hasDoubleQuote && hasSingleQuote) {
|
|
128
|
+
hasMixedQuotes = true;
|
|
129
|
+
}
|
|
130
|
+
if (needsSmartQuotes && hasMixedQuotes) {
|
|
131
|
+
return node;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return node;
|
|
135
|
+
});
|
|
136
|
+
return {
|
|
137
|
+
needsSmartQuotes,
|
|
138
|
+
hasMixedQuotes
|
|
139
|
+
};
|
|
140
|
+
}
|
|
53
141
|
function processScriptNode(scriptNode, terserOptions, terser) {
|
|
54
142
|
let js = helpers_js.extractTextContentFromNode(scriptNode).trim();
|
|
55
143
|
if (!js.length) {
|
|
@@ -81,6 +169,7 @@ function processScriptNode(scriptNode, terserOptions, terser) {
|
|
|
81
169
|
function processNodeWithOnAttrs(node, terserOptions, terser) {
|
|
82
170
|
const jsWrapperStart = 'a=function(){';
|
|
83
171
|
const jsWrapperEnd = '};a();';
|
|
172
|
+
const onAttrTerserOptions = resolveOnAttrTerserOptions(terserOptions);
|
|
84
173
|
const promises = [];
|
|
85
174
|
if (!node.attrs) {
|
|
86
175
|
return promises;
|
|
@@ -98,7 +187,7 @@ function processNodeWithOnAttrs(node, terserOptions, terser) {
|
|
|
98
187
|
// Therefore the attribute's code should be wrapped inside function:
|
|
99
188
|
// "function _(){return false;}"
|
|
100
189
|
const wrappedJs = jsWrapperStart + node.attrs[attrName] + jsWrapperEnd;
|
|
101
|
-
const promise = terser.minify(wrappedJs,
|
|
190
|
+
const promise = terser.minify(wrappedJs, onAttrTerserOptions).then(({ code })=>{
|
|
102
191
|
if (code) {
|
|
103
192
|
const minifiedJs = code.substring(jsWrapperStart.length, code.length - jsWrapperEnd.length);
|
|
104
193
|
node.attrs[attrName] = minifiedJs;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { optionalImport,
|
|
1
|
+
import { optionalImport, normalizeMimeType, isEventHandler, extractTextContentFromNode } from '../helpers.mjs';
|
|
2
2
|
import { redundantScriptTypes } from './removeRedundantAttributes.mjs';
|
|
3
3
|
|
|
4
4
|
/** Minify JS with Terser */ const mod = {
|
|
@@ -24,20 +24,25 @@ import { redundantScriptTypes } from './removeRedundantAttributes.mjs';
|
|
|
24
24
|
return node;
|
|
25
25
|
}
|
|
26
26
|
if (node.tag && node.tag === 'script') {
|
|
27
|
-
const
|
|
27
|
+
const rawMimeType = typeof nodeAttrs.type === 'string' ? normalizeMimeType(nodeAttrs.type) : undefined;
|
|
28
|
+
const mimeType = rawMimeType || 'text/javascript';
|
|
28
29
|
if (redundantScriptTypes.has(mimeType) || mimeType === 'module') {
|
|
29
|
-
|
|
30
|
+
const scriptTerserOptions = resolveScriptTerserOptions(terserOptions, mimeType);
|
|
31
|
+
p = processScriptNode(node, scriptTerserOptions, terser);
|
|
30
32
|
if (p) {
|
|
31
33
|
promises.push(p);
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
if (node.attrs) {
|
|
36
|
-
promises.push
|
|
38
|
+
promises.push(...processNodeWithOnAttrs(node, terserOptions, terser));
|
|
37
39
|
}
|
|
38
40
|
return node;
|
|
39
41
|
});
|
|
40
|
-
return Promise.all(promises).then(()=>
|
|
42
|
+
return Promise.all(promises).then(()=>{
|
|
43
|
+
applySmartQuoteOptions(tree);
|
|
44
|
+
return tree;
|
|
45
|
+
});
|
|
41
46
|
}
|
|
42
47
|
};
|
|
43
48
|
function stripCdata(js) {
|
|
@@ -48,6 +53,89 @@ function stripCdata(js) {
|
|
|
48
53
|
const strippedJs = leftStrippedJs.replace(/\/\/\s*\]\]>/, '').replace(/\/\*\s*\]\]>\s*\*\//, '');
|
|
49
54
|
return leftStrippedJs === strippedJs ? js : strippedJs;
|
|
50
55
|
}
|
|
56
|
+
function resolveScriptTerserOptions(terserOptions, mimeType) {
|
|
57
|
+
var _terserOptions_toplevel, _terserOptions_compress, _terserOptions_mangle;
|
|
58
|
+
if (mimeType !== 'module' || terserOptions.module !== undefined) {
|
|
59
|
+
return terserOptions;
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
...terserOptions,
|
|
63
|
+
module: true,
|
|
64
|
+
toplevel: (_terserOptions_toplevel = terserOptions.toplevel) != null ? _terserOptions_toplevel : false,
|
|
65
|
+
compress: (_terserOptions_compress = terserOptions.compress) != null ? _terserOptions_compress : false,
|
|
66
|
+
mangle: (_terserOptions_mangle = terserOptions.mangle) != null ? _terserOptions_mangle : false
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function resolveOnAttrTerserOptions(terserOptions) {
|
|
70
|
+
const output = terserOptions.output;
|
|
71
|
+
const format = terserOptions.format;
|
|
72
|
+
const outputHasQuoteStyle = !!(output && typeof output === 'object' && 'quote_style' in output);
|
|
73
|
+
const formatHasQuoteStyle = !!(format && typeof format === 'object' && 'quote_style' in format);
|
|
74
|
+
if (outputHasQuoteStyle || formatHasQuoteStyle) {
|
|
75
|
+
return terserOptions;
|
|
76
|
+
}
|
|
77
|
+
const resolved = {
|
|
78
|
+
...terserOptions
|
|
79
|
+
};
|
|
80
|
+
if (format && typeof format === 'object') {
|
|
81
|
+
resolved.format = {
|
|
82
|
+
...format,
|
|
83
|
+
['quote_style']: 3
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
if (output && typeof output === 'object') {
|
|
87
|
+
resolved.output = {
|
|
88
|
+
...output,
|
|
89
|
+
['quote_style']: 3
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
if (!format && !output) {
|
|
93
|
+
resolved.output = {
|
|
94
|
+
['quote_style']: 3
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return resolved;
|
|
98
|
+
}
|
|
99
|
+
function applySmartQuoteOptions(tree) {
|
|
100
|
+
var _tree, _options, _tree_options, _quoteStyle, _tree_options1, _replaceQuote;
|
|
101
|
+
const quoteState = analyzeTreeQuotes(tree);
|
|
102
|
+
if (!quoteState.needsSmartQuotes || quoteState.hasMixedQuotes) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
(_options = (_tree = tree).options) != null ? _options : _tree.options = {};
|
|
106
|
+
(_quoteStyle = (_tree_options = tree.options).quoteStyle) != null ? _quoteStyle : _tree_options.quoteStyle = 0;
|
|
107
|
+
(_replaceQuote = (_tree_options1 = tree.options).replaceQuote) != null ? _replaceQuote : _tree_options1.replaceQuote = false;
|
|
108
|
+
}
|
|
109
|
+
function analyzeTreeQuotes(tree) {
|
|
110
|
+
let needsSmartQuotes = false;
|
|
111
|
+
let hasMixedQuotes = false;
|
|
112
|
+
tree.walk((node)=>{
|
|
113
|
+
if (!node || !node.attrs) {
|
|
114
|
+
return node;
|
|
115
|
+
}
|
|
116
|
+
for (const [attrName, attrValue] of Object.entries(node.attrs)){
|
|
117
|
+
if (typeof attrValue !== 'string') {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const hasDoubleQuote = attrValue.includes('"');
|
|
121
|
+
const hasSingleQuote = attrValue.includes('\'');
|
|
122
|
+
if (hasDoubleQuote && isEventHandler(attrName)) {
|
|
123
|
+
needsSmartQuotes = true;
|
|
124
|
+
}
|
|
125
|
+
if (hasDoubleQuote && hasSingleQuote) {
|
|
126
|
+
hasMixedQuotes = true;
|
|
127
|
+
}
|
|
128
|
+
if (needsSmartQuotes && hasMixedQuotes) {
|
|
129
|
+
return node;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return node;
|
|
133
|
+
});
|
|
134
|
+
return {
|
|
135
|
+
needsSmartQuotes,
|
|
136
|
+
hasMixedQuotes
|
|
137
|
+
};
|
|
138
|
+
}
|
|
51
139
|
function processScriptNode(scriptNode, terserOptions, terser) {
|
|
52
140
|
let js = extractTextContentFromNode(scriptNode).trim();
|
|
53
141
|
if (!js.length) {
|
|
@@ -79,6 +167,7 @@ function processScriptNode(scriptNode, terserOptions, terser) {
|
|
|
79
167
|
function processNodeWithOnAttrs(node, terserOptions, terser) {
|
|
80
168
|
const jsWrapperStart = 'a=function(){';
|
|
81
169
|
const jsWrapperEnd = '};a();';
|
|
170
|
+
const onAttrTerserOptions = resolveOnAttrTerserOptions(terserOptions);
|
|
82
171
|
const promises = [];
|
|
83
172
|
if (!node.attrs) {
|
|
84
173
|
return promises;
|
|
@@ -96,7 +185,7 @@ function processNodeWithOnAttrs(node, terserOptions, terser) {
|
|
|
96
185
|
// Therefore the attribute's code should be wrapped inside function:
|
|
97
186
|
// "function _(){return false;}"
|
|
98
187
|
const wrappedJs = jsWrapperStart + node.attrs[attrName] + jsWrapperEnd;
|
|
99
|
-
const promise = terser.minify(wrappedJs,
|
|
188
|
+
const promise = terser.minify(wrappedJs, onAttrTerserOptions).then(({ code })=>{
|
|
100
189
|
if (code) {
|
|
101
190
|
const minifiedJs = code.substring(jsWrapperStart.length, code.length - jsWrapperEnd.length);
|
|
102
191
|
node.attrs[attrName] = minifiedJs;
|