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
|
@@ -118,21 +118,19 @@ let STORED_URL_BASE;
|
|
|
118
118
|
for (const [attrName, attrValue] of Object.entries(node.attrs)){
|
|
119
119
|
const attrNameLower = attrName.toLowerCase();
|
|
120
120
|
if (isUriTypeAttribute(node.tag, attrNameLower)) {
|
|
121
|
-
if (
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
node.attrs[attrName] = relateUrlInstance.relate(attrValue);
|
|
130
|
-
}
|
|
121
|
+
if (typeof attrValue !== 'string') continue;
|
|
122
|
+
const javascriptMatch = getJavaScriptUrlMatch(attrValue);
|
|
123
|
+
if (javascriptMatch) {
|
|
124
|
+
promises.push(minifyJavaScriptUrl(node, attrName, javascriptMatch, terser));
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (relateUrlInstance) {
|
|
128
|
+
node.attrs[attrName] = relateUrlValue(relateUrlInstance, attrValue);
|
|
131
129
|
}
|
|
132
130
|
continue;
|
|
133
131
|
}
|
|
134
132
|
if (isSrcsetAttribute(node.tag, attrNameLower)) {
|
|
135
|
-
if (srcset && attrValue) {
|
|
133
|
+
if (srcset && typeof attrValue === 'string') {
|
|
136
134
|
try {
|
|
137
135
|
const parsedSrcset = srcset.parseSrcset(attrValue, {
|
|
138
136
|
strict: true
|
|
@@ -140,7 +138,7 @@ let STORED_URL_BASE;
|
|
|
140
138
|
node.attrs[attrName] = srcset.stringifySrcset(parsedSrcset.map((item)=>{
|
|
141
139
|
if (relateUrlInstance) {
|
|
142
140
|
// @ts-expect-error -- not actually readonly
|
|
143
|
-
item.url = relateUrlInstance
|
|
141
|
+
item.url = relateUrlValue(relateUrlInstance, item.url);
|
|
144
142
|
}
|
|
145
143
|
return item;
|
|
146
144
|
}));
|
|
@@ -157,24 +155,51 @@ let STORED_URL_BASE;
|
|
|
157
155
|
return Promise.resolve(tree);
|
|
158
156
|
}
|
|
159
157
|
};
|
|
160
|
-
function isJavaScriptUrl(url) {
|
|
161
|
-
return typeof url === 'string' && url.toLowerCase().startsWith(JAVASCRIPT_URL_PROTOCOL);
|
|
162
|
-
}
|
|
163
158
|
const jsWrapperStart = 'function a(){';
|
|
164
159
|
const jsWrapperEnd = '}a();';
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
160
|
+
const javascriptUrlRegex = /^(\s*)(javascript:)([\s\S]*)$/i;
|
|
161
|
+
function getJavaScriptUrlMatch(url) {
|
|
162
|
+
const match = javascriptUrlRegex.exec(url);
|
|
163
|
+
if (!match) return null;
|
|
164
|
+
return {
|
|
165
|
+
leadingWhitespace: match[1],
|
|
166
|
+
code: match[3]
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function getUrlScheme(value) {
|
|
170
|
+
const match = /^[a-z][a-z0-9+.-]*:/i.exec(value);
|
|
171
|
+
if (!match) return null;
|
|
172
|
+
return match[0].slice(0, -1).toLowerCase();
|
|
173
|
+
}
|
|
174
|
+
function shouldRelateUrlValue(value) {
|
|
175
|
+
const trimmed = value.trim();
|
|
176
|
+
if (!trimmed) return false;
|
|
177
|
+
if (trimmed.startsWith('#') || trimmed.startsWith('?')) return false;
|
|
178
|
+
const scheme = getUrlScheme(trimmed);
|
|
179
|
+
if (scheme) return scheme === 'http' || scheme === 'https';
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
function relateUrlValue(relateUrl, value) {
|
|
183
|
+
if (!shouldRelateUrlValue(value)) return value;
|
|
184
|
+
// FIXME!
|
|
185
|
+
// relateurl@1.0.0-alpha only supports URL while stable version (0.2.7) only supports string
|
|
186
|
+
// the WHATWG URL API is very strict while attrValue might not be a valid URL
|
|
187
|
+
// new URL should be used, and relateUrl#relate should be wrapped in try...catch after relateurl@1 is stable
|
|
188
|
+
try {
|
|
189
|
+
return relateUrl.relate(value);
|
|
190
|
+
} catch (unused) {
|
|
191
|
+
return value;
|
|
176
192
|
}
|
|
177
|
-
|
|
193
|
+
}
|
|
194
|
+
function minifyJavaScriptUrl(node, attrName, match, terser) {
|
|
195
|
+
if (!terser) return Promise.resolve();
|
|
196
|
+
const result = jsWrapperStart + match.code + jsWrapperEnd;
|
|
197
|
+
return terser.minify(result, {}) // Default Option is good enough
|
|
198
|
+
.then(({ code })=>{
|
|
199
|
+
if (!code) return;
|
|
200
|
+
const minifiedJs = code.substring(jsWrapperStart.length, code.length - jsWrapperEnd.length);
|
|
201
|
+
node.attrs[attrName] = match.leadingWhitespace + JAVASCRIPT_URL_PROTOCOL + minifiedJs;
|
|
202
|
+
}).catch(()=>undefined);
|
|
178
203
|
}
|
|
179
204
|
|
|
180
205
|
exports.default = mod;
|
|
@@ -116,21 +116,19 @@ let STORED_URL_BASE;
|
|
|
116
116
|
for (const [attrName, attrValue] of Object.entries(node.attrs)){
|
|
117
117
|
const attrNameLower = attrName.toLowerCase();
|
|
118
118
|
if (isUriTypeAttribute(node.tag, attrNameLower)) {
|
|
119
|
-
if (
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
node.attrs[attrName] = relateUrlInstance.relate(attrValue);
|
|
128
|
-
}
|
|
119
|
+
if (typeof attrValue !== 'string') continue;
|
|
120
|
+
const javascriptMatch = getJavaScriptUrlMatch(attrValue);
|
|
121
|
+
if (javascriptMatch) {
|
|
122
|
+
promises.push(minifyJavaScriptUrl(node, attrName, javascriptMatch, terser));
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (relateUrlInstance) {
|
|
126
|
+
node.attrs[attrName] = relateUrlValue(relateUrlInstance, attrValue);
|
|
129
127
|
}
|
|
130
128
|
continue;
|
|
131
129
|
}
|
|
132
130
|
if (isSrcsetAttribute(node.tag, attrNameLower)) {
|
|
133
|
-
if (srcset && attrValue) {
|
|
131
|
+
if (srcset && typeof attrValue === 'string') {
|
|
134
132
|
try {
|
|
135
133
|
const parsedSrcset = srcset.parseSrcset(attrValue, {
|
|
136
134
|
strict: true
|
|
@@ -138,7 +136,7 @@ let STORED_URL_BASE;
|
|
|
138
136
|
node.attrs[attrName] = srcset.stringifySrcset(parsedSrcset.map((item)=>{
|
|
139
137
|
if (relateUrlInstance) {
|
|
140
138
|
// @ts-expect-error -- not actually readonly
|
|
141
|
-
item.url = relateUrlInstance
|
|
139
|
+
item.url = relateUrlValue(relateUrlInstance, item.url);
|
|
142
140
|
}
|
|
143
141
|
return item;
|
|
144
142
|
}));
|
|
@@ -155,24 +153,51 @@ let STORED_URL_BASE;
|
|
|
155
153
|
return Promise.resolve(tree);
|
|
156
154
|
}
|
|
157
155
|
};
|
|
158
|
-
function isJavaScriptUrl(url) {
|
|
159
|
-
return typeof url === 'string' && url.toLowerCase().startsWith(JAVASCRIPT_URL_PROTOCOL);
|
|
160
|
-
}
|
|
161
156
|
const jsWrapperStart = 'function a(){';
|
|
162
157
|
const jsWrapperEnd = '}a();';
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
158
|
+
const javascriptUrlRegex = /^(\s*)(javascript:)([\s\S]*)$/i;
|
|
159
|
+
function getJavaScriptUrlMatch(url) {
|
|
160
|
+
const match = javascriptUrlRegex.exec(url);
|
|
161
|
+
if (!match) return null;
|
|
162
|
+
return {
|
|
163
|
+
leadingWhitespace: match[1],
|
|
164
|
+
code: match[3]
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function getUrlScheme(value) {
|
|
168
|
+
const match = /^[a-z][a-z0-9+.-]*:/i.exec(value);
|
|
169
|
+
if (!match) return null;
|
|
170
|
+
return match[0].slice(0, -1).toLowerCase();
|
|
171
|
+
}
|
|
172
|
+
function shouldRelateUrlValue(value) {
|
|
173
|
+
const trimmed = value.trim();
|
|
174
|
+
if (!trimmed) return false;
|
|
175
|
+
if (trimmed.startsWith('#') || trimmed.startsWith('?')) return false;
|
|
176
|
+
const scheme = getUrlScheme(trimmed);
|
|
177
|
+
if (scheme) return scheme === 'http' || scheme === 'https';
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
function relateUrlValue(relateUrl, value) {
|
|
181
|
+
if (!shouldRelateUrlValue(value)) return value;
|
|
182
|
+
// FIXME!
|
|
183
|
+
// relateurl@1.0.0-alpha only supports URL while stable version (0.2.7) only supports string
|
|
184
|
+
// the WHATWG URL API is very strict while attrValue might not be a valid URL
|
|
185
|
+
// new URL should be used, and relateUrl#relate should be wrapped in try...catch after relateurl@1 is stable
|
|
186
|
+
try {
|
|
187
|
+
return relateUrl.relate(value);
|
|
188
|
+
} catch (unused) {
|
|
189
|
+
return value;
|
|
174
190
|
}
|
|
175
|
-
|
|
191
|
+
}
|
|
192
|
+
function minifyJavaScriptUrl(node, attrName, match, terser) {
|
|
193
|
+
if (!terser) return Promise.resolve();
|
|
194
|
+
const result = jsWrapperStart + match.code + jsWrapperEnd;
|
|
195
|
+
return terser.minify(result, {}) // Default Option is good enough
|
|
196
|
+
.then(({ code })=>{
|
|
197
|
+
if (!code) return;
|
|
198
|
+
const minifiedJs = code.substring(jsWrapperStart.length, code.length - jsWrapperEnd.length);
|
|
199
|
+
node.attrs[attrName] = match.leadingWhitespace + JAVASCRIPT_URL_PROTOCOL + minifiedJs;
|
|
200
|
+
}).catch(()=>undefined);
|
|
176
201
|
}
|
|
177
202
|
|
|
178
203
|
export { mod as default };
|
|
@@ -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
|
}
|
|
@@ -213,15 +213,17 @@ const mod = {
|
|
|
213
213
|
const newAttrs = attrs;
|
|
214
214
|
Object.entries(attrs).forEach(([attrName, attrValue])=>{
|
|
215
215
|
let newAttrValue = attrValue;
|
|
216
|
-
|
|
217
|
-
|
|
216
|
+
const hasCaseInsensitive = Object.hasOwnProperty.call(caseInsensitiveAttributes, attrName) && (caseInsensitiveAttributes[attrName] === null || node.tag && caseInsensitiveAttributes[attrName].includes(node.tag));
|
|
217
|
+
const hasInvalidValueDefault = Object.hasOwnProperty.call(invalidValueDefault, attrName);
|
|
218
|
+
const invalidMeta = hasInvalidValueDefault ? invalidValueDefault[attrName] : undefined;
|
|
219
|
+
const appliesInvalidValueDefault = Boolean(invalidMeta && (invalidMeta.tag === null || node && node.tag && invalidMeta.tag.includes(node.tag)));
|
|
220
|
+
const shouldNormalizeCase = hasCaseInsensitive || appliesInvalidValueDefault;
|
|
221
|
+
if (typeof attrValue === 'string' && shouldNormalizeCase) {
|
|
222
|
+
newAttrValue = attrValue.trim().toLowerCase();
|
|
218
223
|
}
|
|
219
|
-
if (
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
if (typeof newAttrValue !== 'string' || !meta.valid.includes(newAttrValue)) {
|
|
223
|
-
newAttrValue = meta.default;
|
|
224
|
-
}
|
|
224
|
+
if (appliesInvalidValueDefault && invalidMeta) {
|
|
225
|
+
if (typeof newAttrValue !== 'string' || !invalidMeta.valid.includes(newAttrValue)) {
|
|
226
|
+
newAttrValue = invalidMeta.default;
|
|
225
227
|
}
|
|
226
228
|
}
|
|
227
229
|
newAttrs[attrName] = newAttrValue;
|
|
@@ -211,15 +211,17 @@ const mod = {
|
|
|
211
211
|
const newAttrs = attrs;
|
|
212
212
|
Object.entries(attrs).forEach(([attrName, attrValue])=>{
|
|
213
213
|
let newAttrValue = attrValue;
|
|
214
|
-
|
|
215
|
-
|
|
214
|
+
const hasCaseInsensitive = Object.hasOwnProperty.call(caseInsensitiveAttributes, attrName) && (caseInsensitiveAttributes[attrName] === null || node.tag && caseInsensitiveAttributes[attrName].includes(node.tag));
|
|
215
|
+
const hasInvalidValueDefault = Object.hasOwnProperty.call(invalidValueDefault, attrName);
|
|
216
|
+
const invalidMeta = hasInvalidValueDefault ? invalidValueDefault[attrName] : undefined;
|
|
217
|
+
const appliesInvalidValueDefault = Boolean(invalidMeta && (invalidMeta.tag === null || node && node.tag && invalidMeta.tag.includes(node.tag)));
|
|
218
|
+
const shouldNormalizeCase = hasCaseInsensitive || appliesInvalidValueDefault;
|
|
219
|
+
if (typeof attrValue === 'string' && shouldNormalizeCase) {
|
|
220
|
+
newAttrValue = attrValue.trim().toLowerCase();
|
|
216
221
|
}
|
|
217
|
-
if (
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
if (typeof newAttrValue !== 'string' || !meta.valid.includes(newAttrValue)) {
|
|
221
|
-
newAttrValue = meta.default;
|
|
222
|
-
}
|
|
222
|
+
if (appliesInvalidValueDefault && invalidMeta) {
|
|
223
|
+
if (typeof newAttrValue !== 'string' || !invalidMeta.valid.includes(newAttrValue)) {
|
|
224
|
+
newAttrValue = invalidMeta.default;
|
|
223
225
|
}
|
|
224
226
|
}
|
|
225
227
|
newAttrs[attrName] = newAttrValue;
|
|
@@ -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
|
}
|
|
@@ -52,6 +85,9 @@ type HtmlnanoModule<Options = any> = {
|
|
|
52
85
|
default?: (tree: PostHTMLTreeLike, options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => PostHTMLTreeLike | Promise<PostHTMLTreeLike>;
|
|
53
86
|
};
|
|
54
87
|
|
|
55
|
-
|
|
88
|
+
type RemoveAttributeQuotesOptions = {
|
|
89
|
+
force?: boolean;
|
|
90
|
+
};
|
|
91
|
+
declare const mod: HtmlnanoModule<RemoveAttributeQuotesOptions>;
|
|
56
92
|
|
|
57
93
|
export { mod as default };
|
|
@@ -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
|
}
|
|
@@ -52,6 +85,9 @@ type HtmlnanoModule<Options = any> = {
|
|
|
52
85
|
default?: (tree: PostHTMLTreeLike, options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => PostHTMLTreeLike | Promise<PostHTMLTreeLike>;
|
|
53
86
|
};
|
|
54
87
|
|
|
55
|
-
|
|
88
|
+
type RemoveAttributeQuotesOptions = {
|
|
89
|
+
force?: boolean;
|
|
90
|
+
};
|
|
91
|
+
declare const mod: HtmlnanoModule<RemoveAttributeQuotesOptions>;
|
|
56
92
|
|
|
57
93
|
export { mod as default };
|
|
@@ -2,11 +2,16 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
2
2
|
|
|
3
3
|
// Specification: https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
|
|
4
4
|
// See also: https://github.com/posthtml/posthtml-render/pull/30
|
|
5
|
-
// See also: https://github.com/
|
|
5
|
+
// See also: https://github.com/maltsev/htmlnano/issues/6#issuecomment-707105334
|
|
6
6
|
/** Disable quoteAllAttributes while not overriding the configuration */ const mod = {
|
|
7
|
-
default: function removeAttributeQuotes(tree) {
|
|
8
|
-
var _tree_options, _quoteAllAttributes;
|
|
9
|
-
|
|
7
|
+
default: function removeAttributeQuotes(tree, _options, moduleOptions) {
|
|
8
|
+
var _tree, _options1, _tree_options, _quoteAllAttributes;
|
|
9
|
+
(_options1 = (_tree = tree).options) != null ? _options1 : _tree.options = {};
|
|
10
|
+
if (moduleOptions && typeof moduleOptions === 'object' && moduleOptions.force) {
|
|
11
|
+
tree.options.quoteAllAttributes = false;
|
|
12
|
+
return tree;
|
|
13
|
+
}
|
|
14
|
+
(_quoteAllAttributes = (_tree_options = tree.options).quoteAllAttributes) != null ? _quoteAllAttributes : _tree_options.quoteAllAttributes = false;
|
|
10
15
|
return tree;
|
|
11
16
|
}
|
|
12
17
|
};
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
// Specification: https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
|
|
2
2
|
// See also: https://github.com/posthtml/posthtml-render/pull/30
|
|
3
|
-
// See also: https://github.com/
|
|
3
|
+
// See also: https://github.com/maltsev/htmlnano/issues/6#issuecomment-707105334
|
|
4
4
|
/** Disable quoteAllAttributes while not overriding the configuration */ const mod = {
|
|
5
|
-
default: function removeAttributeQuotes(tree) {
|
|
6
|
-
var _tree_options, _quoteAllAttributes;
|
|
7
|
-
|
|
5
|
+
default: function removeAttributeQuotes(tree, _options, moduleOptions) {
|
|
6
|
+
var _tree, _options1, _tree_options, _quoteAllAttributes;
|
|
7
|
+
(_options1 = (_tree = tree).options) != null ? _options1 : _tree.options = {};
|
|
8
|
+
if (moduleOptions && typeof moduleOptions === 'object' && moduleOptions.force) {
|
|
9
|
+
tree.options.quoteAllAttributes = false;
|
|
10
|
+
return tree;
|
|
11
|
+
}
|
|
12
|
+
(_quoteAllAttributes = (_tree_options = tree.options).quoteAllAttributes) != null ? _quoteAllAttributes : _tree_options.quoteAllAttributes = false;
|
|
8
13
|
return tree;
|
|
9
14
|
}
|
|
10
15
|
};
|