htmlnano 0.2.4 → 0.2.5
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 +19 -9
- package/README.md +30 -3
- package/lib/htmlnano.js +1 -5
- package/lib/modules/mergeScripts.js +4 -1
- package/lib/modules/removeUnusedCss.js +89 -17
- package/lib/presets/ampSafe.js +1 -5
- package/lib/presets/max.js +1 -5
- package/package.json +12 -12
- package/test.js +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,49 +2,56 @@
|
|
|
2
2
|
All notable changes to this project will be documented in this file.
|
|
3
3
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
|
4
4
|
|
|
5
|
+
## [0.2.5] - 2019-11-09
|
|
6
|
+
### Added
|
|
7
|
+
- Option to remove unused CSS using PurgeCSS [#84].
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- Keep the order of inline and external JS [#80].
|
|
11
|
+
|
|
5
12
|
|
|
6
13
|
## [0.2.4] - 2019-07-11
|
|
7
|
-
|
|
14
|
+
### Fixed
|
|
8
15
|
- Remove crossorigin from boolean attribute [#78], [#79].
|
|
9
16
|
- Disable SVGO plugin convertShapeToPath in safe preset [#76].
|
|
10
17
|
|
|
11
18
|
|
|
12
19
|
## [0.2.3] - 2019-02-14
|
|
13
|
-
|
|
20
|
+
### Fixed
|
|
14
21
|
- Keep `<g>` in SVG by default [#71].
|
|
15
22
|
|
|
16
23
|
|
|
17
24
|
## [0.2.2] - 2019-01-03
|
|
18
|
-
|
|
25
|
+
### Added
|
|
19
26
|
- `removeUnusedCss` module [#36].
|
|
20
27
|
|
|
21
|
-
|
|
28
|
+
### Fixed
|
|
22
29
|
- Bug when `tag === false` [#66].
|
|
23
30
|
- Add `crossorigin` to boolean attributes [#67].
|
|
24
31
|
|
|
25
32
|
|
|
26
33
|
## [0.2.1] - 2018-12-01
|
|
27
|
-
|
|
34
|
+
### Fixed
|
|
28
35
|
- Disable JS minifying on AMP pages [#65].
|
|
29
36
|
|
|
30
37
|
## [0.2.0] - 2018-09-14
|
|
31
|
-
|
|
38
|
+
### Breaking changes
|
|
32
39
|
- The API of `minifyCss` module has been changed since `cssnano` has been updated to version 4, which has a different API. Check the following resources for more info:
|
|
33
40
|
* [htmlnano docs](https://github.com/posthtml/htmlnano#minifycss)
|
|
34
41
|
* [cssnano docs](https://cssnano.co/guides/presets)
|
|
35
42
|
* Diff of commit [979f2c](https://github.com/posthtml/htmlnano/commit/979f2c821892c9e979e8b85f74ed0394330fceaf) with the changes of related docs.
|
|
36
43
|
|
|
37
|
-
|
|
44
|
+
### Added
|
|
38
45
|
- Add presets [#64].
|
|
39
46
|
- Add `collapseAttributeWhitespace` module for collapsing spaces in list-like attributes [#25].
|
|
40
47
|
- Add `deduplicateAttributeValues` module for de-duplicating values in list-like attributes [#39].
|
|
41
48
|
- Better support for AMP pages [#59].
|
|
42
49
|
- Collapse whitespaces between top-level tags [#24].
|
|
43
50
|
|
|
44
|
-
|
|
51
|
+
### Changed
|
|
45
52
|
- Improve whitespace normalization using `normalize-html-whitespace` [#21].
|
|
46
53
|
|
|
47
|
-
|
|
54
|
+
### Fixed
|
|
48
55
|
- Don't collapse `visible="false"` attributes in A-Frame pages [#62].
|
|
49
56
|
|
|
50
57
|
|
|
@@ -122,6 +129,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
|
122
129
|
|
|
123
130
|
|
|
124
131
|
|
|
132
|
+
[0.2.5]: https://github.com/posthtml/htmlnano/compare/0.2.4...0.2.5
|
|
125
133
|
[0.2.4]: https://github.com/posthtml/htmlnano/compare/0.2.3...0.2.4
|
|
126
134
|
[0.2.3]: https://github.com/posthtml/htmlnano/compare/0.2.2...0.2.3
|
|
127
135
|
[0.2.2]: https://github.com/posthtml/htmlnano/compare/0.2.1...0.2.2
|
|
@@ -139,6 +147,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
|
139
147
|
[0.1.1]: https://github.com/posthtml/htmlnano/compare/0.1.0...0.1.1
|
|
140
148
|
|
|
141
149
|
|
|
150
|
+
[#84]: https://github.com/posthtml/htmlnano/issues/84
|
|
151
|
+
[#80]: https://github.com/posthtml/htmlnano/issues/80
|
|
142
152
|
[#79]: https://github.com/posthtml/htmlnano/issues/79
|
|
143
153
|
[#78]: https://github.com/posthtml/htmlnano/issues/78
|
|
144
154
|
[#76]: https://github.com/posthtml/htmlnano/issues/76
|
package/README.md
CHANGED
|
@@ -76,8 +76,7 @@ const posthtmlPlugins = [
|
|
|
76
76
|
require('htmlnano')(options)
|
|
77
77
|
];
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
posthtml(posthtmlPlugins, preset)
|
|
79
|
+
posthtml(posthtmlPlugins)
|
|
81
80
|
.process(html)
|
|
82
81
|
.then(function (result) {
|
|
83
82
|
// result.html is minified
|
|
@@ -273,7 +272,11 @@ Minified:
|
|
|
273
272
|
```
|
|
274
273
|
|
|
275
274
|
### removeUnusedCss
|
|
276
|
-
|
|
275
|
+
|
|
276
|
+
Removes unused CSS inside `<style>` tags with either [uncss](https://github.com/uncss/uncss)
|
|
277
|
+
or [PurgeCSS](https://github.com/FullHuman/purgecss).
|
|
278
|
+
|
|
279
|
+
#### With uncss
|
|
277
280
|
|
|
278
281
|
##### Options
|
|
279
282
|
See [the documentation of uncss](https://github.com/uncss/uncss) for all supported options.
|
|
@@ -293,6 +296,30 @@ The following uncss options are ignored if passed to the module:
|
|
|
293
296
|
- `ignoreSheets`
|
|
294
297
|
- `raw`
|
|
295
298
|
|
|
299
|
+
#### With PurgeCSS
|
|
300
|
+
|
|
301
|
+
Use PurgeCSS instead of uncss by adding `tool: 'purgeCSS'` to the options.
|
|
302
|
+
|
|
303
|
+
##### Options
|
|
304
|
+
|
|
305
|
+
See [the documentation of PurgeCSS](https://www.purgecss.com) for all supported options.
|
|
306
|
+
|
|
307
|
+
PurgeCSS options can be passed directly to the `removeUnusedCss` module:
|
|
308
|
+
```js
|
|
309
|
+
htmlnano.process(html, {
|
|
310
|
+
removeUnusedCss: {
|
|
311
|
+
tool: 'purgeCSS',
|
|
312
|
+
whitelist: ['.do-not-remove']
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
The following PurgeCSS options are ignored if passed to the module:
|
|
318
|
+
|
|
319
|
+
- `content`
|
|
320
|
+
- `css`
|
|
321
|
+
- `extractors`
|
|
322
|
+
|
|
296
323
|
##### Example
|
|
297
324
|
Source:
|
|
298
325
|
```html
|
package/lib/htmlnano.js
CHANGED
|
@@ -4,10 +4,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
|
|
7
|
-
var _objectAssign = require('object-assign');
|
|
8
|
-
|
|
9
|
-
var _objectAssign2 = _interopRequireDefault(_objectAssign);
|
|
10
|
-
|
|
11
7
|
var _posthtml = require('posthtml');
|
|
12
8
|
|
|
13
9
|
var _posthtml2 = _interopRequireDefault(_posthtml);
|
|
@@ -31,7 +27,7 @@ function htmlnano() {
|
|
|
31
27
|
var preset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _safe2.default;
|
|
32
28
|
|
|
33
29
|
return function minifier(tree) {
|
|
34
|
-
options =
|
|
30
|
+
options = Object.assign({}, preset, options);
|
|
35
31
|
var promise = Promise.resolve(tree);
|
|
36
32
|
|
|
37
33
|
var _loop = function _loop(moduleName) {
|
|
@@ -7,10 +7,12 @@ exports.default = mergeScripts;
|
|
|
7
7
|
/* Merge multiple <script> into one */
|
|
8
8
|
function mergeScripts(tree) {
|
|
9
9
|
var scriptNodesIndex = {};
|
|
10
|
+
var scriptSrcIndex = 1;
|
|
10
11
|
|
|
11
12
|
tree.match({ tag: 'script' }, function (node) {
|
|
12
13
|
var nodeAttrs = node.attrs || {};
|
|
13
14
|
if (nodeAttrs.src) {
|
|
15
|
+
scriptSrcIndex++;
|
|
14
16
|
return node;
|
|
15
17
|
}
|
|
16
18
|
|
|
@@ -24,7 +26,8 @@ function mergeScripts(tree) {
|
|
|
24
26
|
class: nodeAttrs.class,
|
|
25
27
|
type: scriptType,
|
|
26
28
|
defer: nodeAttrs.defer !== undefined,
|
|
27
|
-
async: nodeAttrs.async !== undefined
|
|
29
|
+
async: nodeAttrs.async !== undefined,
|
|
30
|
+
index: scriptSrcIndex
|
|
28
31
|
});
|
|
29
32
|
if (!scriptNodesIndex[scriptKey]) {
|
|
30
33
|
scriptNodesIndex[scriptKey] = [];
|
|
@@ -14,35 +14,25 @@ var _uncss = require('uncss');
|
|
|
14
14
|
|
|
15
15
|
var _uncss2 = _interopRequireDefault(_uncss);
|
|
16
16
|
|
|
17
|
+
var _purgecss = require('purgecss');
|
|
18
|
+
|
|
19
|
+
var _purgecss2 = _interopRequireDefault(_purgecss);
|
|
20
|
+
|
|
17
21
|
var _posthtmlRender = require('posthtml-render');
|
|
18
22
|
|
|
19
23
|
var _posthtmlRender2 = _interopRequireDefault(_posthtmlRender);
|
|
20
24
|
|
|
21
25
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
22
26
|
|
|
27
|
+
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
|
|
28
|
+
|
|
23
29
|
// These options must be set and shouldn't be overriden to ensure uncss doesn't look at linked stylesheets.
|
|
24
30
|
var uncssOptions = {
|
|
25
31
|
ignoreSheets: [/\s*/],
|
|
26
32
|
stylesheets: []
|
|
27
33
|
};
|
|
28
34
|
|
|
29
|
-
|
|
30
|
-
function removeUnusedCss(tree, options, uncssOptions) {
|
|
31
|
-
var promises = [];
|
|
32
|
-
var html = (0, _posthtmlRender2.default)(tree);
|
|
33
|
-
tree.walk(function (node) {
|
|
34
|
-
if ((0, _helpers.isStyleNode)(node)) {
|
|
35
|
-
promises.push(processStyleNode(html, node, uncssOptions));
|
|
36
|
-
}
|
|
37
|
-
return node;
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
return Promise.all(promises).then(function () {
|
|
41
|
-
return tree;
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function processStyleNode(html, styleNode, uncssOptions) {
|
|
35
|
+
function processStyleNodeUnCSS(html, styleNode, uncssOptions) {
|
|
46
36
|
var css = (0, _helpers.extractCssFromStyleNode)(styleNode);
|
|
47
37
|
|
|
48
38
|
return runUncss(html, css, uncssOptions).then(function (css) {
|
|
@@ -72,4 +62,86 @@ function runUncss(html, css, userOptions) {
|
|
|
72
62
|
resolve(output);
|
|
73
63
|
});
|
|
74
64
|
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
var purgeFromHtml = function purgeFromHtml(tree) {
|
|
68
|
+
// content is not used as we can directly used the parsed HTML,
|
|
69
|
+
// making the process faster
|
|
70
|
+
var selectors = [];
|
|
71
|
+
|
|
72
|
+
tree.walk(function (node) {
|
|
73
|
+
var classes = node.attrs && node.attrs.class && node.attrs.class.split(' ') || [];
|
|
74
|
+
var ids = node.attrs && node.attrs.id && node.attrs.id.split(' ') || [];
|
|
75
|
+
selectors.push.apply(selectors, _toConsumableArray(classes).concat(_toConsumableArray(ids)));
|
|
76
|
+
node.tag && selectors.push(node.tag);
|
|
77
|
+
return node;
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
return function () {
|
|
81
|
+
return selectors;
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
function processStyleNodePurgeCSS(tree, styleNode, purgecssOptions) {
|
|
86
|
+
var css = (0, _helpers.extractCssFromStyleNode)(styleNode);
|
|
87
|
+
return runPurgecss(tree, css, purgecssOptions).then(function (css) {
|
|
88
|
+
if (css.trim().length === 0) {
|
|
89
|
+
styleNode.tag = false;
|
|
90
|
+
styleNode.content = [];
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
styleNode.content = [css];
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function runPurgecss(tree, css, userOptions) {
|
|
98
|
+
if ((typeof userOptions === 'undefined' ? 'undefined' : _typeof(userOptions)) !== 'object') {
|
|
99
|
+
userOptions = {};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
var options = Object.assign({}, userOptions, {
|
|
103
|
+
content: [{
|
|
104
|
+
raw: tree,
|
|
105
|
+
extension: 'html'
|
|
106
|
+
}],
|
|
107
|
+
css: [{
|
|
108
|
+
raw: css,
|
|
109
|
+
extension: 'css'
|
|
110
|
+
}],
|
|
111
|
+
extractors: [{
|
|
112
|
+
extractor: purgeFromHtml(tree),
|
|
113
|
+
extensions: ['html']
|
|
114
|
+
}]
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
return new Promise(function (resolve, reject) {
|
|
118
|
+
try {
|
|
119
|
+
var purgeCss = new _purgecss2.default(options);
|
|
120
|
+
var purgecssResult = purgeCss.purge()[0];
|
|
121
|
+
resolve(purgecssResult.css);
|
|
122
|
+
} catch (err) {
|
|
123
|
+
reject(err);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Remove unused CSS */
|
|
129
|
+
function removeUnusedCss(tree, options, userOptions) {
|
|
130
|
+
var promises = [];
|
|
131
|
+
var html = userOptions.tool !== 'purgeCSS' && (0, _posthtmlRender2.default)(tree);
|
|
132
|
+
|
|
133
|
+
tree.walk(function (node) {
|
|
134
|
+
if ((0, _helpers.isStyleNode)(node)) {
|
|
135
|
+
if (userOptions.tool === 'purgeCSS') {
|
|
136
|
+
promises.push(processStyleNodePurgeCSS(tree, node, userOptions));
|
|
137
|
+
} else {
|
|
138
|
+
promises.push(processStyleNodeUnCSS(html, node, userOptions));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return node;
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
return Promise.all(promises).then(function () {
|
|
145
|
+
return tree;
|
|
146
|
+
});
|
|
75
147
|
}
|
package/lib/presets/ampSafe.js
CHANGED
|
@@ -4,10 +4,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
|
|
7
|
-
var _objectAssign = require('object-assign');
|
|
8
|
-
|
|
9
|
-
var _objectAssign2 = _interopRequireDefault(_objectAssign);
|
|
10
|
-
|
|
11
7
|
var _safe = require('./safe');
|
|
12
8
|
|
|
13
9
|
var _safe2 = _interopRequireDefault(_safe);
|
|
@@ -19,7 +15,7 @@ function _interopRequireDefault(obj) {
|
|
|
19
15
|
/**
|
|
20
16
|
* A safe preset for AMP pages (https://www.ampproject.org)
|
|
21
17
|
*/
|
|
22
|
-
exports.default =
|
|
18
|
+
exports.default = Object.assign({}, _safe2.default, {
|
|
23
19
|
collapseBooleanAttributes: {
|
|
24
20
|
amphtml: true
|
|
25
21
|
},
|
package/lib/presets/max.js
CHANGED
|
@@ -4,10 +4,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
|
|
7
|
-
var _objectAssign = require('object-assign');
|
|
8
|
-
|
|
9
|
-
var _objectAssign2 = _interopRequireDefault(_objectAssign);
|
|
10
|
-
|
|
11
7
|
var _safe = require('./safe');
|
|
12
8
|
|
|
13
9
|
var _safe2 = _interopRequireDefault(_safe);
|
|
@@ -19,7 +15,7 @@ function _interopRequireDefault(obj) {
|
|
|
19
15
|
/**
|
|
20
16
|
* Maximal minification (might break some pages)
|
|
21
17
|
*/
|
|
22
|
-
exports.default =
|
|
18
|
+
exports.default = Object.assign({}, _safe2.default, {
|
|
23
19
|
collapseWhitespace: 'all',
|
|
24
20
|
removeComments: 'all',
|
|
25
21
|
removeRedundantAttributes: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "htmlnano",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
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>",
|
|
@@ -34,23 +34,23 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"cssnano": "^4.1.10",
|
|
36
36
|
"normalize-html-whitespace": "^1.0.0",
|
|
37
|
-
"
|
|
38
|
-
"posthtml": "^0.11.4",
|
|
37
|
+
"posthtml": "^0.12.0",
|
|
39
38
|
"posthtml-render": "^1.1.5",
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
39
|
+
"purgecss": "^1.4.0",
|
|
40
|
+
"svgo": "^1.3.2",
|
|
41
|
+
"terser": "^4.3.9",
|
|
42
|
+
"uncss": "^0.17.2"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"babel-cli": "^6.26.0",
|
|
46
46
|
"babel-core": "^6.26.3",
|
|
47
|
-
"babel-eslint": "^10.0.
|
|
47
|
+
"babel-eslint": "^10.0.3",
|
|
48
48
|
"babel-preset-env": "^1.7.0",
|
|
49
|
-
"eslint": "^6.0
|
|
50
|
-
"expect": "^24.
|
|
51
|
-
"mocha": "^6.
|
|
52
|
-
"release-it": "^12.
|
|
53
|
-
"rimraf": "^
|
|
49
|
+
"eslint": "^6.6.0",
|
|
50
|
+
"expect": "^24.9.0",
|
|
51
|
+
"mocha": "^6.2.2",
|
|
52
|
+
"release-it": "^12.4.3",
|
|
53
|
+
"rimraf": "^3.0.0"
|
|
54
54
|
},
|
|
55
55
|
"repository": {
|
|
56
56
|
"type": "git",
|
package/test.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
const htmlnano = require('./index');
|
|
2
2
|
const fs = require('fs');
|
|
3
|
-
const
|
|
3
|
+
const preset = require('./lib/presets/max').default;
|
|
4
4
|
|
|
5
5
|
const options = {};
|
|
6
6
|
|
|
7
7
|
const html = fs.readFileSync('./test.html', 'utf8');
|
|
8
8
|
|
|
9
9
|
htmlnano
|
|
10
|
-
.process(html, options)
|
|
10
|
+
.process(html, options, preset)
|
|
11
11
|
.then(function (result) {
|
|
12
12
|
console.log(result.html);
|
|
13
13
|
})
|