htmlnano 0.1.10 → 0.2.3
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/.eslintignore +1 -0
- package/CHANGELOG.md +56 -0
- package/README.md +239 -112
- package/lib/helpers.js +47 -0
- package/lib/htmlnano.js +40 -40
- package/lib/modules/collapseAttributeWhitespace.js +30 -0
- package/lib/modules/collapseBooleanAttributes.js +15 -7
- package/lib/modules/collapseWhitespace.js +14 -7
- package/lib/modules/deduplicateAttributeValues.js +48 -0
- package/lib/modules/mergeScripts.js +19 -19
- package/lib/modules/mergeStyles.js +7 -0
- package/lib/modules/minifyCss.js +13 -3
- package/lib/modules/removeEmptyAttributes.js +2 -2
- package/lib/modules/removeUnusedCss.js +75 -0
- package/lib/presets/ampSafe.js +27 -0
- package/lib/presets/hard.js +26 -0
- package/lib/presets/max.js +31 -0
- package/lib/presets/safe.js +31 -0
- package/package.json +20 -11
- package/test.js +18 -15
- package/test.html +0 -14
package/lib/helpers.js
CHANGED
|
@@ -3,12 +3,59 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
exports.isAmpBoilerplate = isAmpBoilerplate;
|
|
6
7
|
exports.isComment = isComment;
|
|
7
8
|
exports.isConditionalComment = isConditionalComment;
|
|
9
|
+
exports.isStyleNode = isStyleNode;
|
|
10
|
+
exports.extractCssFromStyleNode = extractCssFromStyleNode;
|
|
11
|
+
var ampBoilerplateAttributes = ['amp-boilerplate', 'amp4ads-boilerplate', 'amp4email-boilerplate'];
|
|
12
|
+
|
|
13
|
+
function isAmpBoilerplate(node) {
|
|
14
|
+
if (!node.attrs) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
var _iteratorNormalCompletion = true;
|
|
18
|
+
var _didIteratorError = false;
|
|
19
|
+
var _iteratorError = undefined;
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
for (var _iterator = ampBoilerplateAttributes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
23
|
+
var attr = _step.value;
|
|
24
|
+
|
|
25
|
+
if (attr in node.attrs) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
} catch (err) {
|
|
30
|
+
_didIteratorError = true;
|
|
31
|
+
_iteratorError = err;
|
|
32
|
+
} finally {
|
|
33
|
+
try {
|
|
34
|
+
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
35
|
+
_iterator.return();
|
|
36
|
+
}
|
|
37
|
+
} finally {
|
|
38
|
+
if (_didIteratorError) {
|
|
39
|
+
throw _iteratorError;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
8
47
|
function isComment(content) {
|
|
9
48
|
return (content || '').trim().search('<!--') === 0;
|
|
10
49
|
}
|
|
11
50
|
|
|
12
51
|
function isConditionalComment(content) {
|
|
13
52
|
return (content || '').trim().search(/<!--\[if/) === 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isStyleNode(node) {
|
|
56
|
+
return node.tag === 'style' && !isAmpBoilerplate(node) && 'content' in node && node.content.length > 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function extractCssFromStyleNode(node) {
|
|
60
|
+
return Array.isArray(node.content) ? node.content.join(' ') : node.content;
|
|
14
61
|
}
|
package/lib/htmlnano.js
CHANGED
|
@@ -12,57 +12,53 @@ var _posthtml = require('posthtml');
|
|
|
12
12
|
|
|
13
13
|
var _posthtml2 = _interopRequireDefault(_posthtml);
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
var _safe = require('./presets/safe');
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
},
|
|
29
|
-
minifyJs: {},
|
|
30
|
-
minifyJson: {},
|
|
31
|
-
minifySvg: {},
|
|
32
|
-
custom: []
|
|
33
|
-
};
|
|
17
|
+
var _safe2 = _interopRequireDefault(_safe);
|
|
18
|
+
|
|
19
|
+
var _ampSafe = require('./presets/ampSafe');
|
|
20
|
+
|
|
21
|
+
var _ampSafe2 = _interopRequireDefault(_ampSafe);
|
|
22
|
+
|
|
23
|
+
var _max = require('./presets/max');
|
|
24
|
+
|
|
25
|
+
var _max2 = _interopRequireDefault(_max);
|
|
26
|
+
|
|
27
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
34
28
|
|
|
35
29
|
function htmlnano() {
|
|
36
30
|
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
31
|
+
var preset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _safe2.default;
|
|
37
32
|
|
|
38
33
|
return function minifier(tree) {
|
|
39
|
-
options = (0, _objectAssign2.default)({},
|
|
34
|
+
options = (0, _objectAssign2.default)({}, preset, options);
|
|
40
35
|
var promise = Promise.resolve(tree);
|
|
36
|
+
|
|
37
|
+
var _loop = function _loop(moduleName) {
|
|
38
|
+
if (!options[moduleName]) {
|
|
39
|
+
// The module is disabled
|
|
40
|
+
return 'continue';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (_safe2.default[moduleName] === undefined) {
|
|
44
|
+
throw new Error('Module "' + moduleName + '" is not defined');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
var module = require('./modules/' + moduleName);
|
|
48
|
+
promise = promise.then(function (tree) {
|
|
49
|
+
return module.default(tree, options, options[moduleName]);
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
|
|
41
53
|
var _iteratorNormalCompletion = true;
|
|
42
54
|
var _didIteratorError = false;
|
|
43
55
|
var _iteratorError = undefined;
|
|
44
56
|
|
|
45
57
|
try {
|
|
46
|
-
var
|
|
58
|
+
for (var _iterator = Object.keys(options)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
47
59
|
var moduleName = _step.value;
|
|
48
60
|
|
|
49
|
-
|
|
50
|
-
// The module is disabled
|
|
51
|
-
return 'continue';
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (defaultOptions[moduleName] === undefined) {
|
|
55
|
-
throw new Error('Module "' + moduleName + '" is not defined');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
var module = require('./modules/' + moduleName);
|
|
59
|
-
promise = promise.then(function (tree) {
|
|
60
|
-
return module.default(tree, options, options[moduleName]);
|
|
61
|
-
});
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
for (var _iterator = Object.keys(options)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
65
|
-
var _ret = _loop();
|
|
61
|
+
var _ret = _loop(moduleName);
|
|
66
62
|
|
|
67
63
|
if (_ret === 'continue') continue;
|
|
68
64
|
}
|
|
@@ -85,10 +81,14 @@ function htmlnano() {
|
|
|
85
81
|
};
|
|
86
82
|
}
|
|
87
83
|
|
|
88
|
-
htmlnano.process = function (html, options) {
|
|
89
|
-
return (0, _posthtml2.default)([htmlnano(options)]).process(html);
|
|
84
|
+
htmlnano.process = function (html, options, preset) {
|
|
85
|
+
return (0, _posthtml2.default)([htmlnano(options, preset)]).process(html);
|
|
90
86
|
};
|
|
91
87
|
|
|
92
|
-
htmlnano.
|
|
88
|
+
htmlnano.presets = {
|
|
89
|
+
safe: _safe2.default,
|
|
90
|
+
ampSafe: _ampSafe2.default,
|
|
91
|
+
max: _max2.default
|
|
92
|
+
};
|
|
93
93
|
|
|
94
94
|
exports.default = htmlnano;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = collapseAttributeWhitespace;
|
|
7
|
+
var attributesWithLists = exports.attributesWithLists = new Set(['class', 'rel', 'ping']);
|
|
8
|
+
|
|
9
|
+
/** Collapse whitespaces inside list-like attributes (e.g. class, rel) */
|
|
10
|
+
function collapseAttributeWhitespace(tree) {
|
|
11
|
+
tree.walk(function (node) {
|
|
12
|
+
if (!node.attrs) {
|
|
13
|
+
return node;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
Object.keys(node.attrs).forEach(function (attrName) {
|
|
17
|
+
var attrNameLower = attrName.toLowerCase();
|
|
18
|
+
if (!attributesWithLists.has(attrNameLower)) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
var attrValue = node.attrs[attrName].replace(/\s+/g, ' ').trim();
|
|
23
|
+
node.attrs[attrName] = attrValue;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return node;
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return tree;
|
|
30
|
+
}
|
|
@@ -5,14 +5,11 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = collapseBooleanAttributes;
|
|
7
7
|
// Source: https://github.com/kangax/html-minifier/issues/63
|
|
8
|
-
var
|
|
8
|
+
var htmlBooleanAttributes = new Set(['allowfullscreen', 'allowpaymentrequest', 'allowtransparency', 'async', 'autofocus', 'autoplay', 'checked', 'compact', 'controls', 'crossorigin', 'declare', 'default', 'defaultchecked', 'defaultmuted', 'defaultselected', 'defer', 'disabled', 'enabled', 'formnovalidate', 'hidden', 'indeterminate', 'inert', 'ismap', 'itemscope', 'loop', 'multiple', 'muted', 'nohref', 'noresize', 'noshade', 'novalidate', 'nowrap', 'open', 'pauseonexit', 'readonly', 'required', 'reversed', 'scoped', 'seamless', 'selected', 'sortable', 'truespeed', 'typemustmatch', 'visible']);
|
|
9
9
|
|
|
10
|
-
var
|
|
11
|
-
booleanAttributes.forEach(function (attributeName) {
|
|
12
|
-
return booleanAttributesIndex[attributeName] = true;
|
|
13
|
-
});
|
|
10
|
+
var amphtmlBooleanAttributes = new Set(['⚡', 'amp', '⚡4ads', 'amp4ads', '⚡4email', 'amp4email', 'amp-custom', 'amp-boilerplate', 'amp4ads-boilerplate', 'amp4email-boilerplate', 'allow-blocked-ranges', 'amp-access-hide', 'amp-access-template', 'amp-keyframes', 'animate', 'arrows', 'data-block-on-consent', 'data-enable-refresh', 'data-multi-size', 'date-template', 'disable-double-tap', 'disable-session-states', 'disableremoteplayback', 'dots', 'expand-single-section', 'expanded', 'fallback', 'first', 'fullscreen', 'inline', 'lightbox', 'noaudio', 'noautoplay', 'noloading', 'once', 'open-after-clear', 'open-after-select', 'open-button', 'placeholder', 'preload', 'reset-on-refresh', 'reset-on-resize', 'resizable', 'rotate-to-fullscreen', 'second', 'standalone', 'stereo', 'submit-error', 'submit-success', 'submitting', 'subscriptions-actions', 'subscriptions-dialog']);
|
|
14
11
|
|
|
15
|
-
function collapseBooleanAttributes(tree) {
|
|
12
|
+
function collapseBooleanAttributes(tree, options, moduleOptions) {
|
|
16
13
|
tree.match({ attrs: true }, function (node) {
|
|
17
14
|
var _iteratorNormalCompletion = true;
|
|
18
15
|
var _didIteratorError = false;
|
|
@@ -22,7 +19,18 @@ function collapseBooleanAttributes(tree) {
|
|
|
22
19
|
for (var _iterator = Object.keys(node.attrs)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
23
20
|
var attrName = _step.value;
|
|
24
21
|
|
|
25
|
-
if (
|
|
22
|
+
if (!node.tag) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (node.tag.search('a-') === 0 && attrName === 'visible') {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (htmlBooleanAttributes.has(attrName)) {
|
|
31
|
+
node.attrs[attrName] = true;
|
|
32
|
+
}
|
|
33
|
+
if (moduleOptions.amphtml && node.attrs[attrName] === '' && amphtmlBooleanAttributes.has(attrName)) {
|
|
26
34
|
node.attrs[attrName] = true;
|
|
27
35
|
}
|
|
28
36
|
}
|
|
@@ -5,24 +5,31 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = collapseWhitespace;
|
|
7
7
|
|
|
8
|
+
var _normalizeHtmlWhitespace = require('normalize-html-whitespace');
|
|
9
|
+
|
|
10
|
+
var _normalizeHtmlWhitespace2 = _interopRequireDefault(_normalizeHtmlWhitespace);
|
|
11
|
+
|
|
8
12
|
var _helpers = require('../helpers');
|
|
9
13
|
|
|
10
|
-
|
|
14
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
|
+
|
|
16
|
+
var noWhitespaceCollapseElements = new Set(['script', 'style', 'pre', 'textarea']);
|
|
11
17
|
|
|
12
18
|
/** Collapses redundant whitespaces */
|
|
13
|
-
function collapseWhitespace(tree, options, collapseType) {
|
|
14
|
-
if (collapseType !== '
|
|
19
|
+
function collapseWhitespace(tree, options, collapseType, tag) {
|
|
20
|
+
if (collapseType !== 'all') {
|
|
15
21
|
collapseType = 'conservative';
|
|
16
22
|
}
|
|
17
23
|
|
|
18
24
|
tree.forEach(function (node, index) {
|
|
19
25
|
if (typeof node === 'string' && !(0, _helpers.isComment)(node)) {
|
|
20
|
-
|
|
26
|
+
var isTopLevel = !tag || tag === 'html' || tag === 'head';
|
|
27
|
+
node = collapseRedundantWhitespaces(node, collapseType, isTopLevel);
|
|
21
28
|
}
|
|
22
29
|
|
|
23
|
-
var isAllowCollapseWhitespace = noWhitespaceCollapseElements.
|
|
30
|
+
var isAllowCollapseWhitespace = !noWhitespaceCollapseElements.has(node.tag);
|
|
24
31
|
if (node.content && node.content.length && isAllowCollapseWhitespace) {
|
|
25
|
-
node.content = collapseWhitespace(node.content, options, collapseType);
|
|
32
|
+
node.content = collapseWhitespace(node.content, options, collapseType, node.tag);
|
|
26
33
|
}
|
|
27
34
|
|
|
28
35
|
tree[index] = node;
|
|
@@ -34,7 +41,7 @@ function collapseWhitespace(tree, options, collapseType) {
|
|
|
34
41
|
function collapseRedundantWhitespaces(text, collapseType) {
|
|
35
42
|
var isTopLevel = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
36
43
|
|
|
37
|
-
text = text && text.length > 0 ?
|
|
44
|
+
text = text && text.length > 0 ? (0, _normalizeHtmlWhitespace2.default)(text) : '';
|
|
38
45
|
if (collapseType === 'all' || isTopLevel) {
|
|
39
46
|
text = text.trim();
|
|
40
47
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = collapseAttributeWhitespace;
|
|
7
|
+
|
|
8
|
+
var _collapseAttributeWhitespace = require('./collapseAttributeWhitespace');
|
|
9
|
+
|
|
10
|
+
/** Deduplicate values inside list-like attributes (e.g. class, rel) */
|
|
11
|
+
function collapseAttributeWhitespace(tree) {
|
|
12
|
+
tree.walk(function (node) {
|
|
13
|
+
if (!node.attrs) {
|
|
14
|
+
return node;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
Object.keys(node.attrs).forEach(function (attrName) {
|
|
18
|
+
var attrNameLower = attrName.toLowerCase();
|
|
19
|
+
if (!_collapseAttributeWhitespace.attributesWithLists.has(attrNameLower)) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
var attrValues = node.attrs[attrName].split(/\s/);
|
|
24
|
+
var uniqeAttrValues = new Set();
|
|
25
|
+
var deduplicatedAttrValues = [];
|
|
26
|
+
attrValues.forEach(function (attrValue) {
|
|
27
|
+
if (!attrValue) {
|
|
28
|
+
// Keep whitespaces
|
|
29
|
+
deduplicatedAttrValues.push('');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (uniqeAttrValues.has(attrValue)) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
deduplicatedAttrValues.push(attrValue);
|
|
38
|
+
uniqeAttrValues.add(attrValue);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
node.attrs[attrName] = deduplicatedAttrValues.join(' ');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return node;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return tree;
|
|
48
|
+
}
|
|
@@ -34,32 +34,32 @@ function mergeScripts(tree) {
|
|
|
34
34
|
return node;
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
+
var _loop = function _loop(scriptKey) {
|
|
38
|
+
var scriptNodes = scriptNodesIndex[scriptKey];
|
|
39
|
+
var lastScriptNode = scriptNodes.pop();
|
|
40
|
+
scriptNodes.reverse().forEach(function (scriptNode) {
|
|
41
|
+
var scriptContent = (scriptNode.content || []).join(' ');
|
|
42
|
+
scriptContent = scriptContent.trim();
|
|
43
|
+
if (scriptContent.slice(-1) !== ';') {
|
|
44
|
+
scriptContent += ';';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
lastScriptNode.content.unshift(scriptContent);
|
|
48
|
+
|
|
49
|
+
scriptNode.tag = false;
|
|
50
|
+
scriptNode.content = [];
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
37
54
|
var _iteratorNormalCompletion = true;
|
|
38
55
|
var _didIteratorError = false;
|
|
39
56
|
var _iteratorError = undefined;
|
|
40
57
|
|
|
41
58
|
try {
|
|
42
|
-
var
|
|
59
|
+
for (var _iterator = Object.keys(scriptNodesIndex)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
43
60
|
var scriptKey = _step.value;
|
|
44
61
|
|
|
45
|
-
|
|
46
|
-
var lastScriptNode = scriptNodes.pop();
|
|
47
|
-
scriptNodes.reverse().forEach(function (scriptNode) {
|
|
48
|
-
var scriptContent = (scriptNode.content || []).join(' ');
|
|
49
|
-
scriptContent = scriptContent.trim();
|
|
50
|
-
if (scriptContent.slice(-1) !== ';') {
|
|
51
|
-
scriptContent += ';';
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
lastScriptNode.content.unshift(scriptContent);
|
|
55
|
-
|
|
56
|
-
scriptNode.tag = false;
|
|
57
|
-
scriptNode.content = [];
|
|
58
|
-
});
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
for (var _iterator = Object.keys(scriptNodesIndex)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
62
|
-
_loop();
|
|
62
|
+
_loop(scriptKey);
|
|
63
63
|
}
|
|
64
64
|
} catch (err) {
|
|
65
65
|
_didIteratorError = true;
|
|
@@ -4,6 +4,9 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.default = mergeStyles;
|
|
7
|
+
|
|
8
|
+
var _helpers = require('../helpers');
|
|
9
|
+
|
|
7
10
|
/* Merge multiple <style> into one */
|
|
8
11
|
function mergeStyles(tree) {
|
|
9
12
|
var styleNodes = {};
|
|
@@ -16,6 +19,10 @@ function mergeStyles(tree) {
|
|
|
16
19
|
return node;
|
|
17
20
|
}
|
|
18
21
|
|
|
22
|
+
if ((0, _helpers.isAmpBoilerplate)(node)) {
|
|
23
|
+
return node;
|
|
24
|
+
}
|
|
25
|
+
|
|
19
26
|
var styleType = nodeAttrs.type || 'text/css';
|
|
20
27
|
var styleMedia = nodeAttrs.media || 'all';
|
|
21
28
|
var styleKey = styleType + '_' + styleMedia;
|
package/lib/modules/minifyCss.js
CHANGED
|
@@ -5,17 +5,26 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = minifyCss;
|
|
7
7
|
|
|
8
|
+
var _helpers = require('../helpers');
|
|
9
|
+
|
|
8
10
|
var _cssnano = require('cssnano');
|
|
9
11
|
|
|
10
12
|
var _cssnano2 = _interopRequireDefault(_cssnano);
|
|
11
13
|
|
|
12
14
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
15
|
|
|
16
|
+
var postcssOptions = {
|
|
17
|
+
// Prevent the following warning from being shown:
|
|
18
|
+
// > Without `from` option PostCSS could generate wrong source map and will not find Browserslist config.
|
|
19
|
+
// > Set it to CSS file path or to `undefined` to prevent this warning.
|
|
20
|
+
from: undefined
|
|
21
|
+
};
|
|
22
|
+
|
|
14
23
|
/** Minify CSS with cssnano */
|
|
15
24
|
function minifyCss(tree, options, cssnanoOptions) {
|
|
16
25
|
var promises = [];
|
|
17
26
|
tree.walk(function (node) {
|
|
18
|
-
if (
|
|
27
|
+
if ((0, _helpers.isStyleNode)(node)) {
|
|
19
28
|
promises.push(processStyleNode(node, cssnanoOptions));
|
|
20
29
|
} else if (node.attrs && node.attrs.style) {
|
|
21
30
|
promises.push(processStyleAttr(node, cssnanoOptions));
|
|
@@ -30,7 +39,8 @@ function minifyCss(tree, options, cssnanoOptions) {
|
|
|
30
39
|
}
|
|
31
40
|
|
|
32
41
|
function processStyleNode(styleNode, cssnanoOptions) {
|
|
33
|
-
|
|
42
|
+
var css = (0, _helpers.extractCssFromStyleNode)(styleNode);
|
|
43
|
+
return _cssnano2.default.process(css, postcssOptions, cssnanoOptions).then(function (result) {
|
|
34
44
|
return styleNode.content = [result.css];
|
|
35
45
|
});
|
|
36
46
|
}
|
|
@@ -42,7 +52,7 @@ function processStyleAttr(node, cssnanoOptions) {
|
|
|
42
52
|
var wrapperEnd = '}';
|
|
43
53
|
var wrappedStyle = wrapperStart + (node.attrs.style || '') + wrapperEnd;
|
|
44
54
|
|
|
45
|
-
return _cssnano2.default.process(wrappedStyle, cssnanoOptions).then(function (result) {
|
|
55
|
+
return _cssnano2.default.process(wrappedStyle, postcssOptions, cssnanoOptions).then(function (result) {
|
|
46
56
|
var minifiedCss = result.css;
|
|
47
57
|
// Remove wrapperStart at the start and wrapperEnd at the end of minifiedCss
|
|
48
58
|
node.attrs.style = minifiedCss.substring(wrapperStart.length, minifiedCss.length - wrapperEnd.length);
|
|
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = removeEmptyAttributes;
|
|
7
7
|
// Source: https://www.w3.org/TR/html4/sgml/dtd.html#events (Generic Attributes)
|
|
8
|
-
var safeToRemoveAttrs = ['id', 'class', 'style', 'title', 'lang', 'dir', 'onclick', 'ondblclick', 'onmousedown', 'onmouseup', 'onmouseover', 'onmousemove', 'onmouseout', 'onkeypress', 'onkeydown', 'onkeyup'];
|
|
8
|
+
var safeToRemoveAttrs = new Set(['id', 'class', 'style', 'title', 'lang', 'dir', 'onclick', 'ondblclick', 'onmousedown', 'onmouseup', 'onmouseover', 'onmousemove', 'onmouseout', 'onkeypress', 'onkeydown', 'onkeyup']);
|
|
9
9
|
|
|
10
10
|
/** Removes empty attributes */
|
|
11
11
|
function removeEmptyAttributes(tree) {
|
|
@@ -16,7 +16,7 @@ function removeEmptyAttributes(tree) {
|
|
|
16
16
|
|
|
17
17
|
Object.keys(node.attrs).forEach(function (attrName) {
|
|
18
18
|
var attrNameLower = attrName.toLowerCase();
|
|
19
|
-
if (safeToRemoveAttrs.
|
|
19
|
+
if (!safeToRemoveAttrs.has(attrNameLower)) {
|
|
20
20
|
return;
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
|
|
8
|
+
|
|
9
|
+
exports.default = removeUnusedCss;
|
|
10
|
+
|
|
11
|
+
var _helpers = require('../helpers');
|
|
12
|
+
|
|
13
|
+
var _uncss = require('uncss');
|
|
14
|
+
|
|
15
|
+
var _uncss2 = _interopRequireDefault(_uncss);
|
|
16
|
+
|
|
17
|
+
var _posthtmlRender = require('posthtml-render');
|
|
18
|
+
|
|
19
|
+
var _posthtmlRender2 = _interopRequireDefault(_posthtmlRender);
|
|
20
|
+
|
|
21
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
22
|
+
|
|
23
|
+
// These options must be set and shouldn't be overriden to ensure uncss doesn't look at linked stylesheets.
|
|
24
|
+
var uncssOptions = {
|
|
25
|
+
ignoreSheets: [/\s*/],
|
|
26
|
+
stylesheets: []
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/** Remove unused CSS using uncss */
|
|
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) {
|
|
46
|
+
var css = (0, _helpers.extractCssFromStyleNode)(styleNode);
|
|
47
|
+
|
|
48
|
+
return runUncss(html, css, uncssOptions).then(function (css) {
|
|
49
|
+
// uncss may have left some style tags empty
|
|
50
|
+
if (css.trim().length === 0) {
|
|
51
|
+
styleNode.tag = false;
|
|
52
|
+
styleNode.content = [];
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
styleNode.content = [css];
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function runUncss(html, css, userOptions) {
|
|
60
|
+
if ((typeof userOptions === 'undefined' ? 'undefined' : _typeof(userOptions)) !== 'object') {
|
|
61
|
+
userOptions = {};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
var options = Object.assign({}, userOptions, uncssOptions);
|
|
65
|
+
return new Promise(function (resolve, reject) {
|
|
66
|
+
options.raw = css;
|
|
67
|
+
(0, _uncss2.default)(html, options, function (error, output) {
|
|
68
|
+
if (error) {
|
|
69
|
+
reject(error);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
resolve(output);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
var _objectAssign = require('object-assign');
|
|
8
|
+
|
|
9
|
+
var _objectAssign2 = _interopRequireDefault(_objectAssign);
|
|
10
|
+
|
|
11
|
+
var _safe = require('./safe');
|
|
12
|
+
|
|
13
|
+
var _safe2 = _interopRequireDefault(_safe);
|
|
14
|
+
|
|
15
|
+
function _interopRequireDefault(obj) {
|
|
16
|
+
return obj && obj.__esModule ? obj : { default: obj };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A safe preset for AMP pages (https://www.ampproject.org)
|
|
21
|
+
*/
|
|
22
|
+
exports.default = (0, _objectAssign2.default)({}, _safe2.default, {
|
|
23
|
+
collapseBooleanAttributes: {
|
|
24
|
+
amphtml: true
|
|
25
|
+
},
|
|
26
|
+
minifyJs: false
|
|
27
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
var _objectAssign = require('object-assign');
|
|
8
|
+
|
|
9
|
+
var _objectAssign2 = _interopRequireDefault(_objectAssign);
|
|
10
|
+
|
|
11
|
+
var _safe = require('./safe');
|
|
12
|
+
|
|
13
|
+
var _safe2 = _interopRequireDefault(_safe);
|
|
14
|
+
|
|
15
|
+
function _interopRequireDefault(obj) {
|
|
16
|
+
return obj && obj.__esModule ? obj : { default: obj };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
*
|
|
21
|
+
*/
|
|
22
|
+
exports.default = (0, _objectAssign2.default)({}, _safe2.default, {
|
|
23
|
+
collapseWhitespace: 'all',
|
|
24
|
+
removeComments: 'all',
|
|
25
|
+
removeRedundantAttributes: true
|
|
26
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
var _objectAssign = require('object-assign');
|
|
8
|
+
|
|
9
|
+
var _objectAssign2 = _interopRequireDefault(_objectAssign);
|
|
10
|
+
|
|
11
|
+
var _safe = require('./safe');
|
|
12
|
+
|
|
13
|
+
var _safe2 = _interopRequireDefault(_safe);
|
|
14
|
+
|
|
15
|
+
function _interopRequireDefault(obj) {
|
|
16
|
+
return obj && obj.__esModule ? obj : { default: obj };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Maximal minification (might break some pages)
|
|
21
|
+
*/
|
|
22
|
+
exports.default = (0, _objectAssign2.default)({}, _safe2.default, {
|
|
23
|
+
collapseWhitespace: 'all',
|
|
24
|
+
removeComments: 'all',
|
|
25
|
+
removeRedundantAttributes: true,
|
|
26
|
+
removeUnusedCss: {},
|
|
27
|
+
minifyCss: {
|
|
28
|
+
preset: 'default'
|
|
29
|
+
},
|
|
30
|
+
minifySvg: {}
|
|
31
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
/**
|
|
7
|
+
* Minify HTML in a safe way without breaking anything.
|
|
8
|
+
*/
|
|
9
|
+
exports.default = {
|
|
10
|
+
collapseAttributeWhitespace: true,
|
|
11
|
+
collapseBooleanAttributes: {
|
|
12
|
+
amphtml: false
|
|
13
|
+
},
|
|
14
|
+
collapseWhitespace: 'conservative',
|
|
15
|
+
custom: [],
|
|
16
|
+
deduplicateAttributeValues: true,
|
|
17
|
+
mergeScripts: true,
|
|
18
|
+
mergeStyles: true,
|
|
19
|
+
removeUnusedCss: false,
|
|
20
|
+
minifyCss: {
|
|
21
|
+
preset: 'default'
|
|
22
|
+
},
|
|
23
|
+
minifyJs: {},
|
|
24
|
+
minifyJson: {},
|
|
25
|
+
minifySvg: {
|
|
26
|
+
plugins: [{ collapseGroups: false }]
|
|
27
|
+
},
|
|
28
|
+
removeEmptyAttributes: true,
|
|
29
|
+
removeRedundantAttributes: false,
|
|
30
|
+
removeComments: 'safe'
|
|
31
|
+
};
|