htmlnano 2.1.2 → 2.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -12
- package/dist/_modules/collapseAttributeWhitespace.d.mts +57 -0
- package/dist/_modules/collapseAttributeWhitespace.d.ts +57 -0
- package/dist/_modules/collapseAttributeWhitespace.js +296 -0
- package/dist/_modules/collapseAttributeWhitespace.mjs +293 -0
- package/dist/_modules/collapseBooleanAttributes.d.mts +60 -0
- package/dist/_modules/collapseBooleanAttributes.d.ts +60 -0
- package/dist/_modules/collapseBooleanAttributes.js +159 -0
- package/{lib/modules → dist/_modules}/collapseBooleanAttributes.mjs +39 -57
- package/dist/_modules/collapseWhitespace.d.mts +57 -0
- package/dist/_modules/collapseWhitespace.d.ts +57 -0
- package/dist/_modules/collapseWhitespace.js +172 -0
- package/dist/_modules/collapseWhitespace.mjs +170 -0
- package/dist/_modules/custom.d.mts +57 -0
- package/dist/_modules/custom.d.ts +57 -0
- package/dist/_modules/custom.js +22 -0
- package/dist/_modules/custom.mjs +20 -0
- package/dist/_modules/deduplicateAttributeValues.d.mts +56 -0
- package/dist/_modules/deduplicateAttributeValues.d.ts +56 -0
- package/dist/_modules/deduplicateAttributeValues.js +38 -0
- package/dist/_modules/deduplicateAttributeValues.mjs +36 -0
- package/dist/_modules/example.d.mts +59 -0
- package/dist/_modules/example.d.ts +59 -0
- package/dist/_modules/example.js +67 -0
- package/dist/_modules/example.mjs +65 -0
- package/dist/_modules/mergeScripts.d.mts +56 -0
- package/dist/_modules/mergeScripts.d.ts +56 -0
- package/dist/_modules/mergeScripts.js +53 -0
- package/dist/_modules/mergeScripts.mjs +51 -0
- package/dist/_modules/mergeStyles.d.mts +56 -0
- package/dist/_modules/mergeStyles.d.ts +56 -0
- package/dist/_modules/mergeStyles.js +42 -0
- package/dist/_modules/mergeStyles.mjs +40 -0
- package/dist/_modules/minifyConditionalComments.d.mts +56 -0
- package/dist/_modules/minifyConditionalComments.d.ts +56 -0
- package/dist/_modules/minifyConditionalComments.js +54 -0
- package/{lib/modules → dist/_modules}/minifyConditionalComments.mjs +21 -22
- package/dist/_modules/minifyCss.d.mts +56 -0
- package/dist/_modules/minifyCss.d.ts +56 -0
- package/dist/_modules/minifyCss.js +84 -0
- package/dist/_modules/minifyCss.mjs +82 -0
- package/dist/_modules/minifyJs.d.mts +56 -0
- package/dist/_modules/minifyJs.d.ts +56 -0
- package/dist/_modules/minifyJs.js +108 -0
- package/dist/_modules/minifyJs.mjs +106 -0
- package/dist/_modules/minifyJson.d.mts +56 -0
- package/dist/_modules/minifyJson.d.ts +56 -0
- package/dist/_modules/minifyJson.js +35 -0
- package/dist/_modules/minifyJson.mjs +33 -0
- package/dist/_modules/minifySvg.d.mts +56 -0
- package/dist/_modules/minifySvg.d.ts +56 -0
- package/dist/_modules/minifySvg.js +40 -0
- package/dist/_modules/minifySvg.mjs +38 -0
- package/dist/_modules/minifyUrls.d.mts +56 -0
- package/dist/_modules/minifyUrls.d.ts +56 -0
- package/dist/_modules/minifyUrls.js +180 -0
- package/dist/_modules/minifyUrls.mjs +178 -0
- package/dist/_modules/normalizeAttributeValues.d.mts +56 -0
- package/dist/_modules/normalizeAttributeValues.d.ts +56 -0
- package/dist/_modules/normalizeAttributeValues.js +234 -0
- package/dist/_modules/normalizeAttributeValues.mjs +232 -0
- package/dist/_modules/removeAttributeQuotes.d.mts +56 -0
- package/dist/_modules/removeAttributeQuotes.d.ts +56 -0
- package/dist/_modules/removeAttributeQuotes.js +15 -0
- package/dist/_modules/removeAttributeQuotes.mjs +13 -0
- package/dist/_modules/removeComments.d.mts +58 -0
- package/dist/_modules/removeComments.d.ts +58 -0
- package/dist/_modules/removeComments.js +83 -0
- package/{lib/modules → dist/_modules}/removeComments.mjs +24 -35
- package/dist/_modules/removeEmptyAttributes.d.mts +56 -0
- package/dist/_modules/removeEmptyAttributes.d.ts +56 -0
- package/dist/_modules/removeEmptyAttributes.js +197 -0
- package/dist/_modules/removeEmptyAttributes.mjs +195 -0
- package/dist/_modules/removeOptionalTags.d.mts +56 -0
- package/dist/_modules/removeOptionalTags.d.ts +56 -0
- package/dist/_modules/removeOptionalTags.js +190 -0
- package/{lib/modules → dist/_modules}/removeOptionalTags.mjs +54 -91
- package/dist/_modules/removeRedundantAttributes.d.mts +57 -0
- package/dist/_modules/removeRedundantAttributes.d.ts +57 -0
- package/dist/_modules/removeRedundantAttributes.js +128 -0
- package/{lib/modules → dist/_modules}/removeRedundantAttributes.mjs +43 -59
- package/dist/_modules/removeUnusedCss.d.mts +60 -0
- package/dist/_modules/removeUnusedCss.d.ts +60 -0
- package/dist/_modules/removeUnusedCss.js +134 -0
- package/dist/_modules/removeUnusedCss.mjs +132 -0
- package/dist/_modules/sortAttributes.d.mts +57 -0
- package/dist/_modules/sortAttributes.d.ts +57 -0
- package/dist/_modules/sortAttributes.js +102 -0
- package/{lib/modules → dist/_modules}/sortAttributes.mjs +39 -58
- package/dist/_modules/sortAttributesWithLists.d.mts +56 -0
- package/dist/_modules/sortAttributesWithLists.d.ts +56 -0
- package/dist/_modules/sortAttributesWithLists.js +118 -0
- package/{lib/modules → dist/_modules}/sortAttributesWithLists.mjs +41 -59
- package/dist/helpers.d.ts +16 -0
- package/dist/helpers.js +72 -0
- package/dist/helpers.mjs +63 -0
- package/dist/index.d.ts +79 -0
- package/dist/index.js +223 -0
- package/dist/index.mjs +209 -0
- package/dist/presets/ampSafe.d.ts +47 -0
- package/dist/presets/ampSafe.js +19 -0
- package/{lib → dist}/presets/ampSafe.mjs +6 -4
- package/dist/presets/max.d.ts +47 -0
- package/dist/presets/max.js +28 -0
- package/{lib → dist}/presets/max.mjs +6 -4
- package/dist/presets/safe.d.ts +47 -0
- package/dist/presets/safe.js +60 -0
- package/{lib → dist}/presets/safe.mjs +13 -20
- package/package.json +53 -56
- package/.eslintignore +0 -3
- package/CHANGELOG.md +0 -409
- package/docs/README.md +0 -33
- package/docs/babel.config.js +0 -3
- package/docs/docs/010-introduction.md +0 -22
- package/docs/docs/020-usage.md +0 -117
- package/docs/docs/030-config.md +0 -21
- package/docs/docs/040-presets.md +0 -75
- package/docs/docs/050-modules.md +0 -855
- package/docs/docs/060-contribute.md +0 -16
- package/docs/docusaurus.config.js +0 -65
- package/docs/netlify.toml +0 -4
- package/docs/package-lock.json +0 -21630
- package/docs/package.json +0 -40
- package/docs/sidebars.js +0 -26
- package/docs/versioned_docs/version-1.1.1/010-introduction.md +0 -22
- package/docs/versioned_docs/version-1.1.1/020-usage.md +0 -77
- package/docs/versioned_docs/version-1.1.1/030-config.md +0 -21
- package/docs/versioned_docs/version-1.1.1/040-presets.md +0 -75
- package/docs/versioned_docs/version-1.1.1/050-modules.md +0 -785
- package/docs/versioned_docs/version-1.1.1/060-contribute.md +0 -16
- package/docs/versioned_docs/version-2.0.0/010-introduction.md +0 -22
- package/docs/versioned_docs/version-2.0.0/020-usage.md +0 -77
- package/docs/versioned_docs/version-2.0.0/030-config.md +0 -21
- package/docs/versioned_docs/version-2.0.0/040-presets.md +0 -75
- package/docs/versioned_docs/version-2.0.0/050-modules.md +0 -838
- package/docs/versioned_docs/version-2.0.0/060-contribute.md +0 -16
- package/docs/versioned_sidebars/version-1.1.1-sidebars.json +0 -8
- package/docs/versioned_sidebars/version-2.0.0-sidebars.json +0 -8
- package/docs/versions.json +0 -4
- package/index.cjs +0 -11
- package/index.d.cts +0 -3
- package/index.d.mts +0 -3
- package/index.d.ts +0 -94
- package/index.mjs +0 -2
- package/lib/helpers.cjs +0 -79
- package/lib/helpers.mjs +0 -53
- package/lib/htmlnano.cjs +0 -202
- package/lib/htmlnano.mjs +0 -198
- package/lib/modules/collapseAttributeWhitespace.cjs +0 -86
- package/lib/modules/collapseAttributeWhitespace.mjs +0 -104
- package/lib/modules/collapseBooleanAttributes.cjs +0 -62
- package/lib/modules/collapseWhitespace.cjs +0 -100
- package/lib/modules/collapseWhitespace.mjs +0 -132
- package/lib/modules/custom.cjs +0 -19
- package/lib/modules/custom.mjs +0 -16
- package/lib/modules/deduplicateAttributeValues.cjs +0 -38
- package/lib/modules/deduplicateAttributeValues.mjs +0 -40
- package/lib/modules/example.cjs +0 -85
- package/lib/modules/example.mjs +0 -75
- package/lib/modules/mergeScripts.cjs +0 -54
- package/lib/modules/mergeScripts.mjs +0 -56
- package/lib/modules/mergeStyles.cjs +0 -38
- package/lib/modules/mergeStyles.mjs +0 -36
- package/lib/modules/minifyConditionalComments.cjs +0 -47
- package/lib/modules/minifyCss.cjs +0 -73
- package/lib/modules/minifyCss.mjs +0 -88
- package/lib/modules/minifyJs.cjs +0 -103
- package/lib/modules/minifyJs.mjs +0 -121
- package/lib/modules/minifyJson.cjs +0 -24
- package/lib/modules/minifyJson.mjs +0 -21
- package/lib/modules/minifySvg.cjs +0 -37
- package/lib/modules/minifySvg.mjs +0 -30
- package/lib/modules/minifyUrls.cjs +0 -141
- package/lib/modules/minifyUrls.mjs +0 -229
- package/lib/modules/normalizeAttributeValues.cjs +0 -120
- package/lib/modules/normalizeAttributeValues.mjs +0 -140
- package/lib/modules/removeAttributeQuotes.cjs +0 -17
- package/lib/modules/removeAttributeQuotes.mjs +0 -12
- package/lib/modules/removeComments.cjs +0 -86
- package/lib/modules/removeEmptyAttributes.cjs +0 -72
- package/lib/modules/removeEmptyAttributes.mjs +0 -121
- package/lib/modules/removeOptionalTags.cjs +0 -183
- package/lib/modules/removeRedundantAttributes.cjs +0 -112
- package/lib/modules/removeUnusedCss.cjs +0 -113
- package/lib/modules/removeUnusedCss.mjs +0 -122
- package/lib/modules/sortAttributes.cjs +0 -98
- package/lib/modules/sortAttributesWithLists.cjs +0 -114
- package/lib/presets/ampSafe.cjs +0 -18
- package/lib/presets/max.cjs +0 -27
- package/lib/presets/safe.cjs +0 -65
- package/test.js +0 -23
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { isEventHandler } from '../helpers.mjs';
|
|
2
|
-
|
|
3
|
-
const safeToRemoveAttrs = {
|
|
4
|
-
id: null,
|
|
5
|
-
class: null,
|
|
6
|
-
style: null,
|
|
7
|
-
title: null,
|
|
8
|
-
lang: null,
|
|
9
|
-
dir: null,
|
|
10
|
-
abbr: ['th'],
|
|
11
|
-
accept: ['input'],
|
|
12
|
-
'accept-charset': ['form'],
|
|
13
|
-
charset: ['meta', 'script'],
|
|
14
|
-
action: ['form'],
|
|
15
|
-
cols: ['textarea'],
|
|
16
|
-
colspan: ['td', 'th'],
|
|
17
|
-
coords: ['area'],
|
|
18
|
-
dirname: ['input', 'textarea'],
|
|
19
|
-
dropzone: null,
|
|
20
|
-
headers: ['td', 'th'],
|
|
21
|
-
form: [
|
|
22
|
-
'button',
|
|
23
|
-
'fieldset',
|
|
24
|
-
'input',
|
|
25
|
-
'keygen',
|
|
26
|
-
'object',
|
|
27
|
-
'output',
|
|
28
|
-
'select',
|
|
29
|
-
'textarea'
|
|
30
|
-
],
|
|
31
|
-
formaction: ['button', 'input'],
|
|
32
|
-
height: ['canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video'],
|
|
33
|
-
high: 'meter',
|
|
34
|
-
href: 'link',
|
|
35
|
-
list: 'input',
|
|
36
|
-
low: 'meter',
|
|
37
|
-
manifest: 'html',
|
|
38
|
-
max: ['meter', 'progress'],
|
|
39
|
-
maxLength: ['input', 'textarea'],
|
|
40
|
-
menu: 'button',
|
|
41
|
-
min: 'meter',
|
|
42
|
-
minLength: ['input', 'textarea'],
|
|
43
|
-
name: [
|
|
44
|
-
'button',
|
|
45
|
-
'fieldset',
|
|
46
|
-
'input',
|
|
47
|
-
'keygen',
|
|
48
|
-
'output',
|
|
49
|
-
'select',
|
|
50
|
-
'textarea',
|
|
51
|
-
'form',
|
|
52
|
-
'map',
|
|
53
|
-
'meta',
|
|
54
|
-
'param',
|
|
55
|
-
'slot'
|
|
56
|
-
],
|
|
57
|
-
pattern: ['input'],
|
|
58
|
-
ping: ['a', 'area'],
|
|
59
|
-
placeholder: ['input', 'textarea'],
|
|
60
|
-
poster: ['video'],
|
|
61
|
-
rel: ['a', 'area', 'link'],
|
|
62
|
-
rows: 'textarea',
|
|
63
|
-
rowspan: ['td', 'th'],
|
|
64
|
-
size: ['input', 'select'],
|
|
65
|
-
span: ['col', 'colgroup'],
|
|
66
|
-
src: [
|
|
67
|
-
'audio',
|
|
68
|
-
'embed',
|
|
69
|
-
'iframe',
|
|
70
|
-
'img',
|
|
71
|
-
'input',
|
|
72
|
-
'script',
|
|
73
|
-
'source',
|
|
74
|
-
'track',
|
|
75
|
-
'video'
|
|
76
|
-
],
|
|
77
|
-
start: 'ol',
|
|
78
|
-
tabindex: null,
|
|
79
|
-
type: [
|
|
80
|
-
'a',
|
|
81
|
-
'link',
|
|
82
|
-
'button',
|
|
83
|
-
'embed',
|
|
84
|
-
'object',
|
|
85
|
-
'script',
|
|
86
|
-
'source',
|
|
87
|
-
'style',
|
|
88
|
-
'input',
|
|
89
|
-
'menu',
|
|
90
|
-
'menuitem',
|
|
91
|
-
'ol'
|
|
92
|
-
],
|
|
93
|
-
value: ['button', 'input', 'li'],
|
|
94
|
-
width: ['canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video']
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
export function onAttrs() {
|
|
98
|
-
return (attrs, node) => {
|
|
99
|
-
const newAttrs = { ...attrs };
|
|
100
|
-
Object.entries(attrs).forEach(([attrName, attrValue]) => {
|
|
101
|
-
if (
|
|
102
|
-
isEventHandler(attrName)
|
|
103
|
-
|| (
|
|
104
|
-
Object.hasOwnProperty.call(safeToRemoveAttrs, attrName)
|
|
105
|
-
&& (
|
|
106
|
-
safeToRemoveAttrs[attrName] === null
|
|
107
|
-
|| safeToRemoveAttrs[attrName].includes(node.tag)
|
|
108
|
-
)
|
|
109
|
-
)
|
|
110
|
-
) {
|
|
111
|
-
if (typeof attrValue === 'string') {
|
|
112
|
-
if (attrValue === '' || attrValue.trim() === '') {
|
|
113
|
-
delete newAttrs[attrName];
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
return newAttrs;
|
|
120
|
-
};
|
|
121
|
-
}
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.default = removeOptionalTags;
|
|
7
|
-
var _helpers = require("../helpers.cjs");
|
|
8
|
-
const startWithWhitespacePattern = /^\s+/;
|
|
9
|
-
const bodyStartTagCantBeOmittedWithFirstChildTags = new Set(['meta', 'link', 'script', 'style']);
|
|
10
|
-
const tbodyStartTagCantBeOmittedWithPrecededTags = new Set(['tbody', 'thead', 'tfoot']);
|
|
11
|
-
const tbodyEndTagCantBeOmittedWithFollowedTags = new Set(['tbody', 'tfoot']);
|
|
12
|
-
function isEmptyTextNode(node) {
|
|
13
|
-
if (typeof node === 'string' && node.trim() === '') {
|
|
14
|
-
return true;
|
|
15
|
-
}
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
function isEmptyNode(node) {
|
|
19
|
-
if (!node.content) {
|
|
20
|
-
return true;
|
|
21
|
-
}
|
|
22
|
-
if (node.content.length) {
|
|
23
|
-
return !node.content.filter(n => typeof n === 'string' && isEmptyTextNode(n) ? false : true).length;
|
|
24
|
-
}
|
|
25
|
-
return true;
|
|
26
|
-
}
|
|
27
|
-
function getFirstChildTag(node, nonEmpty = true) {
|
|
28
|
-
if (node.content && node.content.length) {
|
|
29
|
-
if (nonEmpty) {
|
|
30
|
-
for (const childNode of node.content) {
|
|
31
|
-
if (childNode.tag) return childNode;
|
|
32
|
-
if (typeof childNode === 'string' && !isEmptyTextNode(childNode)) return childNode;
|
|
33
|
-
}
|
|
34
|
-
} else {
|
|
35
|
-
return node.content[0] || null;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
function getPrevNode(tree, currentNodeIndex, nonEmpty = false) {
|
|
41
|
-
if (nonEmpty) {
|
|
42
|
-
for (let i = currentNodeIndex - 1; i >= 0; i--) {
|
|
43
|
-
const node = tree[i];
|
|
44
|
-
if (node.tag) return node;
|
|
45
|
-
if (typeof node === 'string' && !isEmptyTextNode(node)) return node;
|
|
46
|
-
}
|
|
47
|
-
} else {
|
|
48
|
-
return tree[currentNodeIndex - 1] || null;
|
|
49
|
-
}
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
function getNextNode(tree, currentNodeIndex, nonEmpty = false) {
|
|
53
|
-
if (nonEmpty) {
|
|
54
|
-
for (let i = currentNodeIndex + 1; i < tree.length; i++) {
|
|
55
|
-
const node = tree[i];
|
|
56
|
-
if (node.tag) return node;
|
|
57
|
-
if (typeof node === 'string' && !isEmptyTextNode(node)) return node;
|
|
58
|
-
}
|
|
59
|
-
} else {
|
|
60
|
-
return tree[currentNodeIndex + 1] || null;
|
|
61
|
-
}
|
|
62
|
-
return null;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Specification https://html.spec.whatwg.org/multipage/syntax.html#optional-tags
|
|
66
|
-
/** Remove optional tag in the DOM */
|
|
67
|
-
function removeOptionalTags(tree) {
|
|
68
|
-
tree.forEach((node, index) => {
|
|
69
|
-
if (!node.tag) return node;
|
|
70
|
-
if (node.attrs && Object.keys(node.attrs).length) return node;
|
|
71
|
-
|
|
72
|
-
// const prevNode = getPrevNode(tree, index);
|
|
73
|
-
const prevNonEmptyNode = getPrevNode(tree, index, true);
|
|
74
|
-
const nextNode = getNextNode(tree, index);
|
|
75
|
-
const nextNonEmptyNode = getNextNode(tree, index, true);
|
|
76
|
-
const firstChildNode = getFirstChildTag(node, false);
|
|
77
|
-
const firstNonEmptyChildNode = getFirstChildTag(node);
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* An "html" element's start tag may be omitted if the first thing inside the "html" element is not a comment.
|
|
81
|
-
* An "html" element's end tag may be omitted if the "html" element is not IMMEDIATELY followed by a comment.
|
|
82
|
-
*/
|
|
83
|
-
if (node.tag === 'html') {
|
|
84
|
-
let isHtmlStartTagCanBeOmitted = true;
|
|
85
|
-
let isHtmlEndTagCanBeOmitted = true;
|
|
86
|
-
if (typeof firstNonEmptyChildNode === 'string' && (0, _helpers.isComment)(firstNonEmptyChildNode)) {
|
|
87
|
-
isHtmlStartTagCanBeOmitted = false;
|
|
88
|
-
}
|
|
89
|
-
if (typeof nextNonEmptyNode === 'string' && (0, _helpers.isComment)(nextNonEmptyNode)) {
|
|
90
|
-
isHtmlEndTagCanBeOmitted = false;
|
|
91
|
-
}
|
|
92
|
-
if (isHtmlStartTagCanBeOmitted && isHtmlEndTagCanBeOmitted) {
|
|
93
|
-
node.tag = false;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* A "head" element's start tag may be omitted if the element is empty, or if the first thing inside the "head" element is an element.
|
|
99
|
-
* A "head" element's end tag may be omitted if the "head" element is not IMMEDIATELY followed by ASCII whitespace or a comment.
|
|
100
|
-
*/
|
|
101
|
-
if (node.tag === 'head') {
|
|
102
|
-
let isHeadStartTagCanBeOmitted = false;
|
|
103
|
-
let isHeadEndTagCanBeOmitted = true;
|
|
104
|
-
if (isEmptyNode(node) || firstNonEmptyChildNode && firstNonEmptyChildNode.tag) {
|
|
105
|
-
isHeadStartTagCanBeOmitted = true;
|
|
106
|
-
}
|
|
107
|
-
if (nextNode && typeof nextNode === 'string' && startWithWhitespacePattern.test(nextNode) || nextNonEmptyNode && typeof nextNonEmptyNode === 'string' && (0, _helpers.isComment)(nextNode)) {
|
|
108
|
-
isHeadEndTagCanBeOmitted = false;
|
|
109
|
-
}
|
|
110
|
-
if (isHeadStartTagCanBeOmitted && isHeadEndTagCanBeOmitted) {
|
|
111
|
-
node.tag = false;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* A "body" element's start tag may be omitted if the element is empty, or if the first thing inside the "body" element is not ASCII whitespace or a comment, except if the first thing inside the "body" element is a "meta", "link", "script", "style", or "template" element.
|
|
117
|
-
* A "body" element's end tag may be omitted if the "body" element is not IMMEDIATELY followed by a comment.
|
|
118
|
-
*/
|
|
119
|
-
if (node.tag === 'body') {
|
|
120
|
-
let isBodyStartTagCanBeOmitted = true;
|
|
121
|
-
let isBodyEndTagCanBeOmitted = true;
|
|
122
|
-
if (typeof firstChildNode === 'string' && startWithWhitespacePattern.test(firstChildNode) || typeof firstNonEmptyChildNode === 'string' && (0, _helpers.isComment)(firstNonEmptyChildNode)) {
|
|
123
|
-
isBodyStartTagCanBeOmitted = false;
|
|
124
|
-
}
|
|
125
|
-
if (firstNonEmptyChildNode && firstNonEmptyChildNode.tag && bodyStartTagCantBeOmittedWithFirstChildTags.has(firstNonEmptyChildNode.tag)) {
|
|
126
|
-
isBodyStartTagCanBeOmitted = false;
|
|
127
|
-
}
|
|
128
|
-
if (nextNode && typeof nextNode === 'string' && (0, _helpers.isComment)(nextNode)) {
|
|
129
|
-
isBodyEndTagCanBeOmitted = false;
|
|
130
|
-
}
|
|
131
|
-
if (isBodyStartTagCanBeOmitted && isBodyEndTagCanBeOmitted) {
|
|
132
|
-
node.tag = false;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* A "colgroup" element's start tag may be omitted if the first thing inside the "colgroup" element is a "col" element, and if the element is not IMMEDIATELY preceded by another "colgroup" element. It can't be omitted if the element is empty.
|
|
138
|
-
* A "colgroup" element's end tag may be omitted if the "colgroup" element is not IMMEDIATELY followed by ASCII whitespace or a comment.
|
|
139
|
-
*/
|
|
140
|
-
if (node.tag === 'colgroup') {
|
|
141
|
-
let isColgroupStartTagCanBeOmitted = false;
|
|
142
|
-
let isColgroupEndTagCanBeOmitted = true;
|
|
143
|
-
if (firstNonEmptyChildNode && firstNonEmptyChildNode.tag && firstNonEmptyChildNode.tag === 'col') {
|
|
144
|
-
isColgroupStartTagCanBeOmitted = true;
|
|
145
|
-
}
|
|
146
|
-
if (prevNonEmptyNode && prevNonEmptyNode.tag && prevNonEmptyNode.tag === 'colgroup') {
|
|
147
|
-
isColgroupStartTagCanBeOmitted = false;
|
|
148
|
-
}
|
|
149
|
-
if (nextNode && typeof nextNode === 'string' && startWithWhitespacePattern.test(nextNode) || nextNonEmptyNode && typeof nextNonEmptyNode === 'string' && (0, _helpers.isComment)(nextNonEmptyNode)) {
|
|
150
|
-
isColgroupEndTagCanBeOmitted = false;
|
|
151
|
-
}
|
|
152
|
-
if (isColgroupStartTagCanBeOmitted && isColgroupEndTagCanBeOmitted) {
|
|
153
|
-
node.tag = false;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* A "tbody" element's start tag may be omitted if the first thing inside the "tbody" element is a "tr" element, and if the element is not immediately preceded by another "tbody", "thead" or "tfoot" element. It can't be omitted if the element is empty.
|
|
159
|
-
* A "tbody" element's end tag may be omitted if the "tbody" element is not IMMEDIATELY followed by a "tbody" or "tfoot" element.
|
|
160
|
-
*/
|
|
161
|
-
if (node.tag === 'tbody') {
|
|
162
|
-
let isTbodyStartTagCanBeOmitted = false;
|
|
163
|
-
let isTbodyEndTagCanBeOmitted = true;
|
|
164
|
-
if (firstNonEmptyChildNode && firstNonEmptyChildNode.tag && firstNonEmptyChildNode.tag === 'tr') {
|
|
165
|
-
isTbodyStartTagCanBeOmitted = true;
|
|
166
|
-
}
|
|
167
|
-
if (prevNonEmptyNode && prevNonEmptyNode.tag && tbodyStartTagCantBeOmittedWithPrecededTags.has(prevNonEmptyNode.tag)) {
|
|
168
|
-
isTbodyStartTagCanBeOmitted = false;
|
|
169
|
-
}
|
|
170
|
-
if (nextNonEmptyNode && nextNonEmptyNode.tag && tbodyEndTagCantBeOmittedWithFollowedTags.has(nextNonEmptyNode.tag)) {
|
|
171
|
-
isTbodyEndTagCanBeOmitted = false;
|
|
172
|
-
}
|
|
173
|
-
if (isTbodyStartTagCanBeOmitted && isTbodyEndTagCanBeOmitted) {
|
|
174
|
-
node.tag = false;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
if (node.content && node.content.length) {
|
|
178
|
-
removeOptionalTags(node.content);
|
|
179
|
-
}
|
|
180
|
-
return node;
|
|
181
|
-
});
|
|
182
|
-
return tree;
|
|
183
|
-
}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.onAttrs = onAttrs;
|
|
7
|
-
exports.redundantScriptTypes = void 0;
|
|
8
|
-
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#JavaScript_types
|
|
9
|
-
const redundantScriptTypes = exports.redundantScriptTypes = new Set(['application/javascript', 'application/ecmascript', 'application/x-ecmascript', 'application/x-javascript', 'text/javascript', 'text/ecmascript', 'text/javascript1.0', 'text/javascript1.1', 'text/javascript1.2', 'text/javascript1.3', 'text/javascript1.4', 'text/javascript1.5', 'text/jscript', 'text/livescript', 'text/x-ecmascript', 'text/x-javascript']);
|
|
10
|
-
|
|
11
|
-
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#missing-value-default
|
|
12
|
-
const missingValueDefaultAttributes = {
|
|
13
|
-
'form': {
|
|
14
|
-
'method': 'get'
|
|
15
|
-
},
|
|
16
|
-
input: {
|
|
17
|
-
type: 'text'
|
|
18
|
-
},
|
|
19
|
-
button: {
|
|
20
|
-
// https://html.spec.whatwg.org/multipage/form-elements.html#attr-button-type
|
|
21
|
-
type: 'submit'
|
|
22
|
-
},
|
|
23
|
-
'script': {
|
|
24
|
-
'language': 'javascript',
|
|
25
|
-
'type': attrs => {
|
|
26
|
-
for (const [attrName, attrValue] of Object.entries(attrs)) {
|
|
27
|
-
if (attrName.toLowerCase() !== 'type') {
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
return redundantScriptTypes.has(attrValue);
|
|
31
|
-
}
|
|
32
|
-
return false;
|
|
33
|
-
},
|
|
34
|
-
// Remove attribute if the function returns false
|
|
35
|
-
'charset': attrs => {
|
|
36
|
-
// The charset attribute only really makes sense on “external” SCRIPT elements:
|
|
37
|
-
// http://perfectionkills.com/optimizing-html/#8_script_charset
|
|
38
|
-
return !attrs.src;
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
'style': {
|
|
42
|
-
'media': 'all',
|
|
43
|
-
'type': 'text/css'
|
|
44
|
-
},
|
|
45
|
-
'link': {
|
|
46
|
-
media: 'all',
|
|
47
|
-
'type': attrs => {
|
|
48
|
-
// https://html.spec.whatwg.org/multipage/links.html#link-type-stylesheet
|
|
49
|
-
let isRelStyleSheet = false;
|
|
50
|
-
let isTypeTextCSS = false;
|
|
51
|
-
if (attrs) {
|
|
52
|
-
for (const [attrName, attrValue] of Object.entries(attrs)) {
|
|
53
|
-
if (attrName.toLowerCase() === 'rel' && attrValue === 'stylesheet') {
|
|
54
|
-
isRelStyleSheet = true;
|
|
55
|
-
}
|
|
56
|
-
if (attrName.toLowerCase() === 'type' && attrValue === 'text/css') {
|
|
57
|
-
isTypeTextCSS = true;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Only "text/css" is redudant for link[rel=stylesheet]. Otherwise "type" shouldn't be removed
|
|
63
|
-
return isRelStyleSheet && isTypeTextCSS;
|
|
64
|
-
}
|
|
65
|
-
},
|
|
66
|
-
// See: https://html.spec.whatwg.org/#lazy-loading-attributes
|
|
67
|
-
img: {
|
|
68
|
-
'loading': 'eager',
|
|
69
|
-
// https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-decoding
|
|
70
|
-
decoding: 'auto'
|
|
71
|
-
},
|
|
72
|
-
iframe: {
|
|
73
|
-
'loading': 'eager'
|
|
74
|
-
},
|
|
75
|
-
// https://html.spec.whatwg.org/multipage/media.html#htmltrackelement
|
|
76
|
-
track: {
|
|
77
|
-
kind: 'subtitles'
|
|
78
|
-
},
|
|
79
|
-
textarea: {
|
|
80
|
-
// https://html.spec.whatwg.org/multipage/form-elements.html#dom-textarea-wrap
|
|
81
|
-
wrap: 'soft'
|
|
82
|
-
},
|
|
83
|
-
area: {
|
|
84
|
-
// https://html.spec.whatwg.org/multipage/image-maps.html#attr-area-shape
|
|
85
|
-
shape: 'rect'
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
const tagsHaveMissingValueDefaultAttributes = new Set(Object.keys(missingValueDefaultAttributes));
|
|
89
|
-
|
|
90
|
-
/** Removes redundant attributes */
|
|
91
|
-
function onAttrs() {
|
|
92
|
-
return (attrs, node) => {
|
|
93
|
-
if (!node.tag) return attrs;
|
|
94
|
-
const newAttrs = attrs;
|
|
95
|
-
if (tagsHaveMissingValueDefaultAttributes.has(node.tag)) {
|
|
96
|
-
const tagRedundantAttributes = missingValueDefaultAttributes[node.tag];
|
|
97
|
-
for (const redundantAttributeName of Object.keys(tagRedundantAttributes)) {
|
|
98
|
-
let tagRedundantAttributeValue = tagRedundantAttributes[redundantAttributeName];
|
|
99
|
-
let isRemove = false;
|
|
100
|
-
if (typeof tagRedundantAttributeValue === 'function') {
|
|
101
|
-
isRemove = tagRedundantAttributeValue(attrs);
|
|
102
|
-
} else if (attrs[redundantAttributeName] === tagRedundantAttributeValue) {
|
|
103
|
-
isRemove = true;
|
|
104
|
-
}
|
|
105
|
-
if (isRemove) {
|
|
106
|
-
delete newAttrs[redundantAttributeName];
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return newAttrs;
|
|
111
|
-
};
|
|
112
|
-
}
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.default = removeUnusedCss;
|
|
7
|
-
var _helpers = require("../helpers.cjs");
|
|
8
|
-
// These options must be set and shouldn't be overriden to ensure uncss doesn't look at linked stylesheets.
|
|
9
|
-
const uncssOptions = {
|
|
10
|
-
ignoreSheets: [/\s*/],
|
|
11
|
-
stylesheets: []
|
|
12
|
-
};
|
|
13
|
-
function processStyleNodeUnCSS(html, styleNode, uncssOptions, uncss) {
|
|
14
|
-
const css = (0, _helpers.extractCssFromStyleNode)(styleNode);
|
|
15
|
-
return runUncss(html, css, uncssOptions, uncss).then(css => {
|
|
16
|
-
// uncss may have left some style tags empty
|
|
17
|
-
if (css.trim().length === 0) {
|
|
18
|
-
styleNode.tag = false;
|
|
19
|
-
styleNode.content = [];
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
styleNode.content = [css];
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
function runUncss(html, css, userOptions, uncss) {
|
|
26
|
-
if (typeof userOptions !== 'object') {
|
|
27
|
-
userOptions = {};
|
|
28
|
-
}
|
|
29
|
-
const options = {
|
|
30
|
-
...userOptions,
|
|
31
|
-
...uncssOptions
|
|
32
|
-
};
|
|
33
|
-
return new Promise((resolve, reject) => {
|
|
34
|
-
options.raw = css;
|
|
35
|
-
uncss(html, options, (error, output) => {
|
|
36
|
-
if (error) {
|
|
37
|
-
reject(error);
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
resolve(output);
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
const purgeFromHtml = function (tree) {
|
|
45
|
-
// content is not used as we can directly used the parsed HTML,
|
|
46
|
-
// making the process faster
|
|
47
|
-
const selectors = [];
|
|
48
|
-
tree.walk(node => {
|
|
49
|
-
const classes = node.attrs && node.attrs.class && node.attrs.class.split(' ') || [];
|
|
50
|
-
const ids = node.attrs && node.attrs.id && node.attrs.id.split(' ') || [];
|
|
51
|
-
selectors.push(...classes, ...ids);
|
|
52
|
-
node.tag && selectors.push(node.tag);
|
|
53
|
-
return node;
|
|
54
|
-
});
|
|
55
|
-
return () => selectors;
|
|
56
|
-
};
|
|
57
|
-
function processStyleNodePurgeCSS(tree, styleNode, purgecssOptions, purgecss) {
|
|
58
|
-
const css = (0, _helpers.extractCssFromStyleNode)(styleNode);
|
|
59
|
-
return runPurgecss(tree, css, purgecssOptions, purgecss).then(css => {
|
|
60
|
-
if (css.trim().length === 0) {
|
|
61
|
-
styleNode.tag = false;
|
|
62
|
-
styleNode.content = [];
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
styleNode.content = [css];
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
function runPurgecss(tree, css, userOptions, purgecss) {
|
|
69
|
-
if (typeof userOptions !== 'object') {
|
|
70
|
-
userOptions = {};
|
|
71
|
-
}
|
|
72
|
-
const options = {
|
|
73
|
-
...userOptions,
|
|
74
|
-
content: [{
|
|
75
|
-
raw: tree,
|
|
76
|
-
extension: 'html'
|
|
77
|
-
}],
|
|
78
|
-
css: [{
|
|
79
|
-
raw: css,
|
|
80
|
-
extension: 'css'
|
|
81
|
-
}],
|
|
82
|
-
extractors: [{
|
|
83
|
-
extractor: purgeFromHtml(tree),
|
|
84
|
-
extensions: ['html']
|
|
85
|
-
}]
|
|
86
|
-
};
|
|
87
|
-
return new purgecss.PurgeCSS().purge(options).then(result => {
|
|
88
|
-
return result[0].css;
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/** Remove unused CSS */
|
|
93
|
-
async function removeUnusedCss(tree, options, userOptions) {
|
|
94
|
-
const promises = [];
|
|
95
|
-
const html = userOptions.tool !== 'purgeCSS' && tree.render(tree);
|
|
96
|
-
const purgecss = await (0, _helpers.optionalImport)('purgecss');
|
|
97
|
-
const uncss = await (0, _helpers.optionalImport)('uncss');
|
|
98
|
-
tree.walk(node => {
|
|
99
|
-
if ((0, _helpers.isStyleNode)(node)) {
|
|
100
|
-
if (userOptions.tool === 'purgeCSS') {
|
|
101
|
-
if (purgecss) {
|
|
102
|
-
promises.push(processStyleNodePurgeCSS(tree, node, userOptions, purgecss));
|
|
103
|
-
}
|
|
104
|
-
} else {
|
|
105
|
-
if (uncss) {
|
|
106
|
-
promises.push(processStyleNodeUnCSS(html, node, userOptions, uncss));
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return node;
|
|
111
|
-
});
|
|
112
|
-
return Promise.all(promises).then(() => tree);
|
|
113
|
-
}
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import { isStyleNode, extractCssFromStyleNode, optionalImport } from '../helpers.mjs';
|
|
2
|
-
|
|
3
|
-
// These options must be set and shouldn't be overriden to ensure uncss doesn't look at linked stylesheets.
|
|
4
|
-
const uncssOptions = {
|
|
5
|
-
ignoreSheets: [/\s*/],
|
|
6
|
-
stylesheets: [],
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
function processStyleNodeUnCSS(html, styleNode, uncssOptions, uncss) {
|
|
10
|
-
const css = extractCssFromStyleNode(styleNode);
|
|
11
|
-
|
|
12
|
-
return runUncss(html, css, uncssOptions, uncss).then(css => {
|
|
13
|
-
// uncss may have left some style tags empty
|
|
14
|
-
if (css.trim().length === 0) {
|
|
15
|
-
styleNode.tag = false;
|
|
16
|
-
styleNode.content = [];
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
styleNode.content = [css];
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function runUncss(html, css, userOptions, uncss) {
|
|
24
|
-
if (typeof userOptions !== 'object') {
|
|
25
|
-
userOptions = {};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const options = { ...userOptions, ...uncssOptions };
|
|
29
|
-
return new Promise((resolve, reject) => {
|
|
30
|
-
options.raw = css;
|
|
31
|
-
uncss(html, options, (error, output) => {
|
|
32
|
-
if (error) {
|
|
33
|
-
reject(error);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
resolve(output);
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const purgeFromHtml = function (tree) {
|
|
42
|
-
// content is not used as we can directly used the parsed HTML,
|
|
43
|
-
// making the process faster
|
|
44
|
-
const selectors = [];
|
|
45
|
-
|
|
46
|
-
tree.walk(node => {
|
|
47
|
-
const classes = node.attrs && node.attrs.class && node.attrs.class.split(' ') || [];
|
|
48
|
-
const ids = node.attrs && node.attrs.id && node.attrs.id.split(' ') || [];
|
|
49
|
-
selectors.push(...classes, ...ids);
|
|
50
|
-
node.tag && selectors.push(node.tag);
|
|
51
|
-
return node;
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
return () => selectors;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
function processStyleNodePurgeCSS(tree, styleNode, purgecssOptions, purgecss) {
|
|
58
|
-
const css = extractCssFromStyleNode(styleNode);
|
|
59
|
-
return runPurgecss(tree, css, purgecssOptions, purgecss)
|
|
60
|
-
.then(css => {
|
|
61
|
-
if (css.trim().length === 0) {
|
|
62
|
-
styleNode.tag = false;
|
|
63
|
-
styleNode.content = [];
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
styleNode.content = [css];
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function runPurgecss(tree, css, userOptions, purgecss) {
|
|
71
|
-
if (typeof userOptions !== 'object') {
|
|
72
|
-
userOptions = {};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const options = {
|
|
76
|
-
...userOptions,
|
|
77
|
-
content: [{
|
|
78
|
-
raw: tree,
|
|
79
|
-
extension: 'html'
|
|
80
|
-
}],
|
|
81
|
-
css: [{
|
|
82
|
-
raw: css,
|
|
83
|
-
extension: 'css'
|
|
84
|
-
}],
|
|
85
|
-
extractors: [{
|
|
86
|
-
extractor: purgeFromHtml(tree),
|
|
87
|
-
extensions: ['html']
|
|
88
|
-
}]
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
return new purgecss.PurgeCSS()
|
|
92
|
-
.purge(options)
|
|
93
|
-
.then((result) => {
|
|
94
|
-
return result[0].css;
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/** Remove unused CSS */
|
|
99
|
-
export default async function removeUnusedCss(tree, options, userOptions) {
|
|
100
|
-
const promises = [];
|
|
101
|
-
const html = userOptions.tool !== 'purgeCSS' && tree.render(tree);
|
|
102
|
-
|
|
103
|
-
const purgecss = await optionalImport('purgecss');
|
|
104
|
-
const uncss = await optionalImport('uncss');
|
|
105
|
-
|
|
106
|
-
tree.walk(node => {
|
|
107
|
-
if (isStyleNode(node)) {
|
|
108
|
-
if (userOptions.tool === 'purgeCSS') {
|
|
109
|
-
if (purgecss) {
|
|
110
|
-
promises.push(processStyleNodePurgeCSS(tree, node, userOptions, purgecss));
|
|
111
|
-
}
|
|
112
|
-
} else {
|
|
113
|
-
if (uncss) {
|
|
114
|
-
promises.push(processStyleNodeUnCSS(html, node, userOptions, uncss));
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
return node;
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
return Promise.all(promises).then(() => tree);
|
|
122
|
-
}
|