htmlnano 0.2.7 → 0.2.8
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/CHANGELOG.md +25 -0
- package/README.md +128 -0
- package/lib/helpers.js +4 -22
- package/lib/htmlnano.js +17 -47
- package/lib/modules/collapseAttributeWhitespace.js +6 -23
- package/lib/modules/collapseBooleanAttributes.js +5 -7
- package/lib/modules/collapseWhitespace.js +19 -24
- package/lib/modules/custom.js +2 -2
- package/lib/modules/deduplicateAttributeValues.js +8 -8
- package/lib/modules/mergeScripts.js +12 -17
- package/lib/modules/mergeStyles.js +8 -8
- package/lib/modules/minifyConditionalComments.js +52 -0
- package/lib/modules/minifyCss.js +13 -17
- package/lib/modules/minifyJs.js +19 -21
- package/lib/modules/minifyJson.js +3 -3
- package/lib/modules/minifySvg.js +9 -12
- package/lib/modules/minifyUrls.js +58 -55
- package/lib/modules/removeAttributeQuotes.js +1 -1
- package/lib/modules/removeComments.js +7 -9
- package/lib/modules/removeEmptyAttributes.js +5 -22
- package/lib/modules/removeOptionalTags.js +220 -0
- package/lib/modules/removeRedundantAttributes.js +33 -32
- package/lib/modules/removeUnusedCss.js +28 -52
- package/lib/modules/sortAttributes.js +120 -0
- package/lib/modules/sortAttributesWithLists.js +120 -7
- package/lib/presets/ampSafe.js +5 -55
- package/lib/presets/max.js +8 -56
- package/lib/presets/safe.js +7 -4
- package/package.json +16 -7
|
@@ -3,28 +3,141 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports
|
|
6
|
+
exports.default = collapseAttributeWhitespace;
|
|
7
|
+
|
|
8
|
+
var _timsort = require("timsort");
|
|
7
9
|
|
|
8
10
|
var _collapseAttributeWhitespace = require("./collapseAttributeWhitespace");
|
|
9
11
|
|
|
12
|
+
// class, rel, ping
|
|
13
|
+
const validOptions = new Set(['frequency', 'alphabetical']);
|
|
14
|
+
|
|
15
|
+
const processModuleOptions = options => {
|
|
16
|
+
if (options === true) return 'alphabetical';
|
|
17
|
+
return validOptions.has(options) ? options : false;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
class AttributeTokenChain {
|
|
21
|
+
constructor() {
|
|
22
|
+
this.freqData = new Map(); // <attrValue, frequency>[]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
addFromNodeAttrsArray(attrValuesArray) {
|
|
26
|
+
attrValuesArray.forEach(attrValue => {
|
|
27
|
+
if (this.freqData.has(attrValue)) {
|
|
28
|
+
this.freqData.set(attrValue, this.freqData.get(attrValue) + 1);
|
|
29
|
+
} else {
|
|
30
|
+
this.freqData.set(attrValue, 1);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
createSortOrder() {
|
|
36
|
+
let _sortOrder = [];
|
|
37
|
+
|
|
38
|
+
for (const item of this.freqData.entries()) {
|
|
39
|
+
_sortOrder.push(item);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
(0, _timsort.sort)(_sortOrder, (a, b) => b[1] - a[1]);
|
|
43
|
+
this.sortOrder = _sortOrder.map(i => i[0]);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
sortFromNodeAttrsArray(attrValuesArray) {
|
|
47
|
+
const resultArray = [];
|
|
48
|
+
|
|
49
|
+
if (!this.sortOrder) {
|
|
50
|
+
this.createSortOrder();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
this.sortOrder.forEach(k => {
|
|
54
|
+
if (attrValuesArray.includes(k)) {
|
|
55
|
+
resultArray.push(k);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
return resultArray;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
}
|
|
10
62
|
/** Sort values inside list-like attributes (e.g. class, rel) */
|
|
11
|
-
|
|
12
|
-
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
function collapseAttributeWhitespace(tree, options, moduleOptions) {
|
|
66
|
+
const sortType = processModuleOptions(moduleOptions);
|
|
67
|
+
|
|
68
|
+
if (sortType === 'alphabetical') {
|
|
69
|
+
return sortAttributesWithListsInAlphabeticalOrder(tree);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (sortType === 'frequency') {
|
|
73
|
+
return sortAttributesWithListsByFrequency(tree);
|
|
74
|
+
} // Invalid configuration
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
return tree;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function sortAttributesWithListsInAlphabeticalOrder(tree) {
|
|
81
|
+
tree.walk(node => {
|
|
13
82
|
if (!node.attrs) {
|
|
14
83
|
return node;
|
|
15
84
|
}
|
|
16
85
|
|
|
17
|
-
Object.keys(node.attrs).forEach(
|
|
18
|
-
|
|
86
|
+
Object.keys(node.attrs).forEach(attrName => {
|
|
87
|
+
const attrNameLower = attrName.toLowerCase();
|
|
19
88
|
|
|
20
89
|
if (!_collapseAttributeWhitespace.attributesWithLists.has(attrNameLower)) {
|
|
21
90
|
return;
|
|
22
91
|
}
|
|
23
92
|
|
|
24
|
-
|
|
25
|
-
node.attrs[attrName] = attrValues.sort(
|
|
93
|
+
const attrValues = node.attrs[attrName].split(/\s/);
|
|
94
|
+
node.attrs[attrName] = attrValues.sort((a, b) => {
|
|
95
|
+
return typeof a.localeCompare === 'function' ? a.localeCompare(b) : a - b;
|
|
96
|
+
}).join(' ');
|
|
26
97
|
});
|
|
27
98
|
return node;
|
|
28
99
|
});
|
|
29
100
|
return tree;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function sortAttributesWithListsByFrequency(tree) {
|
|
104
|
+
const tokenChainObj = {}; // <attrNameLower: AttributeTokenChain>[]
|
|
105
|
+
// Traverse through tree to get frequency
|
|
106
|
+
|
|
107
|
+
tree.walk(node => {
|
|
108
|
+
if (!node.attrs) {
|
|
109
|
+
return node;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
Object.entries(node.attrs).forEach(([attrName, attrValues]) => {
|
|
113
|
+
const attrNameLower = attrName.toLowerCase();
|
|
114
|
+
|
|
115
|
+
if (!_collapseAttributeWhitespace.attributesWithLists.has(attrNameLower)) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
tokenChainObj[attrNameLower] = tokenChainObj[attrNameLower] || new AttributeTokenChain();
|
|
120
|
+
tokenChainObj[attrNameLower].addFromNodeAttrsArray(attrValues.split(/\s/));
|
|
121
|
+
});
|
|
122
|
+
return node;
|
|
123
|
+
}); // Traverse through tree again, this time sort the attribute values
|
|
124
|
+
|
|
125
|
+
tree.walk(node => {
|
|
126
|
+
if (!node.attrs) {
|
|
127
|
+
return node;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
Object.entries(node.attrs).forEach(([attrName, attrValues]) => {
|
|
131
|
+
const attrNameLower = attrName.toLowerCase();
|
|
132
|
+
|
|
133
|
+
if (!_collapseAttributeWhitespace.attributesWithLists.has(attrNameLower)) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (tokenChainObj[attrNameLower]) {
|
|
138
|
+
node.attrs[attrName] = tokenChainObj[attrNameLower].sortFromNodeAttrsArray(attrValues.split(/\s/)).join(' ');
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
return node;
|
|
142
|
+
});
|
|
30
143
|
}
|
package/lib/presets/ampSafe.js
CHANGED
|
@@ -3,74 +3,24 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports
|
|
6
|
+
exports.default = void 0;
|
|
7
7
|
|
|
8
8
|
var _safe = _interopRequireDefault(require("./safe"));
|
|
9
9
|
|
|
10
10
|
function _interopRequireDefault(obj) {
|
|
11
11
|
return obj && obj.__esModule ? obj : {
|
|
12
|
-
|
|
12
|
+
default: obj
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
|
-
|
|
16
|
-
function ownKeys(object, enumerableOnly) {
|
|
17
|
-
var keys = Object.keys(object);
|
|
18
|
-
|
|
19
|
-
if (Object.getOwnPropertySymbols) {
|
|
20
|
-
var symbols = Object.getOwnPropertySymbols(object);
|
|
21
|
-
if (enumerableOnly) symbols = symbols.filter(function (sym) {
|
|
22
|
-
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
|
23
|
-
});
|
|
24
|
-
keys.push.apply(keys, symbols);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return keys;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function _objectSpread(target) {
|
|
31
|
-
for (var i = 1; i < arguments.length; i++) {
|
|
32
|
-
var source = arguments[i] != null ? arguments[i] : {};
|
|
33
|
-
|
|
34
|
-
if (i % 2) {
|
|
35
|
-
ownKeys(Object(source), true).forEach(function (key) {
|
|
36
|
-
_defineProperty(target, key, source[key]);
|
|
37
|
-
});
|
|
38
|
-
} else if (Object.getOwnPropertyDescriptors) {
|
|
39
|
-
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
|
|
40
|
-
} else {
|
|
41
|
-
ownKeys(Object(source)).forEach(function (key) {
|
|
42
|
-
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return target;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function _defineProperty(obj, key, value) {
|
|
51
|
-
if (key in obj) {
|
|
52
|
-
Object.defineProperty(obj, key, {
|
|
53
|
-
value: value,
|
|
54
|
-
enumerable: true,
|
|
55
|
-
configurable: true,
|
|
56
|
-
writable: true
|
|
57
|
-
});
|
|
58
|
-
} else {
|
|
59
|
-
obj[key] = value;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return obj;
|
|
63
|
-
}
|
|
64
15
|
/**
|
|
65
16
|
* A safe preset for AMP pages (https://www.ampproject.org)
|
|
66
17
|
*/
|
|
67
18
|
|
|
68
19
|
|
|
69
|
-
var _default =
|
|
20
|
+
var _default = { ..._safe.default,
|
|
70
21
|
collapseBooleanAttributes: {
|
|
71
22
|
amphtml: true
|
|
72
23
|
},
|
|
73
24
|
minifyJs: false
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
exports["default"] = _default;
|
|
25
|
+
};
|
|
26
|
+
exports.default = _default;
|
package/lib/presets/max.js
CHANGED
|
@@ -3,70 +3,21 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports
|
|
6
|
+
exports.default = void 0;
|
|
7
7
|
|
|
8
8
|
var _safe = _interopRequireDefault(require("./safe"));
|
|
9
9
|
|
|
10
10
|
function _interopRequireDefault(obj) {
|
|
11
11
|
return obj && obj.__esModule ? obj : {
|
|
12
|
-
|
|
12
|
+
default: obj
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
|
-
|
|
16
|
-
function ownKeys(object, enumerableOnly) {
|
|
17
|
-
var keys = Object.keys(object);
|
|
18
|
-
|
|
19
|
-
if (Object.getOwnPropertySymbols) {
|
|
20
|
-
var symbols = Object.getOwnPropertySymbols(object);
|
|
21
|
-
if (enumerableOnly) symbols = symbols.filter(function (sym) {
|
|
22
|
-
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
|
23
|
-
});
|
|
24
|
-
keys.push.apply(keys, symbols);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return keys;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function _objectSpread(target) {
|
|
31
|
-
for (var i = 1; i < arguments.length; i++) {
|
|
32
|
-
var source = arguments[i] != null ? arguments[i] : {};
|
|
33
|
-
|
|
34
|
-
if (i % 2) {
|
|
35
|
-
ownKeys(Object(source), true).forEach(function (key) {
|
|
36
|
-
_defineProperty(target, key, source[key]);
|
|
37
|
-
});
|
|
38
|
-
} else if (Object.getOwnPropertyDescriptors) {
|
|
39
|
-
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
|
|
40
|
-
} else {
|
|
41
|
-
ownKeys(Object(source)).forEach(function (key) {
|
|
42
|
-
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return target;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function _defineProperty(obj, key, value) {
|
|
51
|
-
if (key in obj) {
|
|
52
|
-
Object.defineProperty(obj, key, {
|
|
53
|
-
value: value,
|
|
54
|
-
enumerable: true,
|
|
55
|
-
configurable: true,
|
|
56
|
-
writable: true
|
|
57
|
-
});
|
|
58
|
-
} else {
|
|
59
|
-
obj[key] = value;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return obj;
|
|
63
|
-
}
|
|
64
15
|
/**
|
|
65
16
|
* Maximal minification (might break some pages)
|
|
66
17
|
*/
|
|
67
18
|
|
|
68
19
|
|
|
69
|
-
var _default =
|
|
20
|
+
var _default = { ..._safe.default,
|
|
70
21
|
collapseWhitespace: 'all',
|
|
71
22
|
removeComments: 'all',
|
|
72
23
|
removeAttributeQuotes: true,
|
|
@@ -75,7 +26,8 @@ var _default = _objectSpread(_objectSpread({}, _safe["default"]), {}, {
|
|
|
75
26
|
minifyCss: {
|
|
76
27
|
preset: 'default'
|
|
77
28
|
},
|
|
78
|
-
minifySvg: {}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
29
|
+
minifySvg: {},
|
|
30
|
+
minifyConditionalComments: true,
|
|
31
|
+
removeOptionalTags: true
|
|
32
|
+
};
|
|
33
|
+
exports.default = _default;
|
package/lib/presets/safe.js
CHANGED
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports
|
|
6
|
+
exports.default = void 0;
|
|
7
7
|
/**
|
|
8
8
|
* Minify HTML in a safe way without breaking anything.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
var _default = {
|
|
12
|
+
sortAttributes: false,
|
|
12
13
|
collapseAttributeWhitespace: true,
|
|
13
14
|
collapseBooleanAttributes: {
|
|
14
15
|
amphtml: false
|
|
@@ -31,11 +32,13 @@ var _default = {
|
|
|
31
32
|
convertShapeToPath: false
|
|
32
33
|
}]
|
|
33
34
|
},
|
|
35
|
+
minifyConditionalComments: false,
|
|
34
36
|
removeEmptyAttributes: true,
|
|
35
37
|
removeRedundantAttributes: false,
|
|
36
38
|
removeComments: 'safe',
|
|
37
39
|
removeAttributeQuotes: false,
|
|
38
|
-
sortAttributesWithLists:
|
|
39
|
-
minifyUrls: false
|
|
40
|
+
sortAttributesWithLists: 'alphabetical',
|
|
41
|
+
minifyUrls: false,
|
|
42
|
+
removeOptionalTags: false
|
|
40
43
|
};
|
|
41
|
-
exports
|
|
44
|
+
exports.default = _default;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "htmlnano",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"description": "Modular HTML minifier, built on top of the PostHTML",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"author": "Kirill Maltsev <maltsevkirill@gmail.com>",
|
|
@@ -28,17 +28,26 @@
|
|
|
28
28
|
],
|
|
29
29
|
"babel": {
|
|
30
30
|
"presets": [
|
|
31
|
-
|
|
31
|
+
[
|
|
32
|
+
"@babel/env",
|
|
33
|
+
{
|
|
34
|
+
"targets": {
|
|
35
|
+
"node": 10
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
]
|
|
32
39
|
]
|
|
33
40
|
},
|
|
34
41
|
"dependencies": {
|
|
35
42
|
"cssnano": "^4.1.10",
|
|
36
43
|
"posthtml": "^0.13.4",
|
|
37
|
-
"posthtml-render": "^1.
|
|
44
|
+
"posthtml-render": "^1.3.0",
|
|
38
45
|
"purgecss": "^2.3.0",
|
|
39
46
|
"relateurl": "^0.2.7",
|
|
47
|
+
"srcset": "^3.0.0",
|
|
40
48
|
"svgo": "^1.3.2",
|
|
41
49
|
"terser": "^4.8.0",
|
|
50
|
+
"timsort": "^0.3.0",
|
|
42
51
|
"uncss": "^0.17.3"
|
|
43
52
|
},
|
|
44
53
|
"devDependencies": {
|
|
@@ -47,10 +56,10 @@
|
|
|
47
56
|
"@babel/preset-env": "^7.12.1",
|
|
48
57
|
"@babel/register": "^7.12.1",
|
|
49
58
|
"babel-eslint": "^10.1.0",
|
|
50
|
-
"eslint": "^7.
|
|
51
|
-
"expect": "^26.
|
|
52
|
-
"mocha": "^8.2.
|
|
53
|
-
"release-it": "^14.1
|
|
59
|
+
"eslint": "^7.13.0",
|
|
60
|
+
"expect": "^26.6.2",
|
|
61
|
+
"mocha": "^8.2.1",
|
|
62
|
+
"release-it": "^14.2.1",
|
|
54
63
|
"rimraf": "^3.0.2"
|
|
55
64
|
},
|
|
56
65
|
"repository": {
|