htmlnano 0.2.5 → 0.2.9

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.
@@ -1,147 +1,125 @@
1
- 'use strict';
1
+ "use strict";
2
2
 
3
3
  Object.defineProperty(exports, "__esModule", {
4
- value: true
4
+ value: true
5
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
6
  exports.default = removeUnusedCss;
10
7
 
11
- var _helpers = require('../helpers');
12
-
13
- var _uncss = require('uncss');
14
-
15
- var _uncss2 = _interopRequireDefault(_uncss);
8
+ var _helpers = require("../helpers");
16
9
 
17
- var _purgecss = require('purgecss');
10
+ var _uncss = _interopRequireDefault(require("uncss"));
18
11
 
19
- var _purgecss2 = _interopRequireDefault(_purgecss);
20
-
21
- var _posthtmlRender = require('posthtml-render');
22
-
23
- var _posthtmlRender2 = _interopRequireDefault(_posthtmlRender);
12
+ var _purgecss = _interopRequireDefault(require("purgecss"));
24
13
 
25
14
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
26
15
 
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
-
29
16
  // These options must be set and shouldn't be overriden to ensure uncss doesn't look at linked stylesheets.
30
- var uncssOptions = {
31
- ignoreSheets: [/\s*/],
32
- stylesheets: []
17
+ const uncssOptions = {
18
+ ignoreSheets: [/\s*/],
19
+ stylesheets: []
33
20
  };
34
21
 
35
22
  function processStyleNodeUnCSS(html, styleNode, uncssOptions) {
36
- var css = (0, _helpers.extractCssFromStyleNode)(styleNode);
37
-
38
- return runUncss(html, css, uncssOptions).then(function (css) {
39
- // uncss may have left some style tags empty
40
- if (css.trim().length === 0) {
41
- styleNode.tag = false;
42
- styleNode.content = [];
43
- return;
44
- }
45
- styleNode.content = [css];
46
- });
47
- }
48
-
49
- function runUncss(html, css, userOptions) {
50
- if ((typeof userOptions === 'undefined' ? 'undefined' : _typeof(userOptions)) !== 'object') {
51
- userOptions = {};
23
+ const css = (0, _helpers.extractCssFromStyleNode)(styleNode);
24
+ return runUncss(html, css, uncssOptions).then(css => {
25
+ // uncss may have left some style tags empty
26
+ if (css.trim().length === 0) {
27
+ styleNode.tag = false;
28
+ styleNode.content = [];
29
+ return;
52
30
  }
53
31
 
54
- var options = Object.assign({}, userOptions, uncssOptions);
55
- return new Promise(function (resolve, reject) {
56
- options.raw = css;
57
- (0, _uncss2.default)(html, options, function (error, output) {
58
- if (error) {
59
- reject(error);
60
- return;
61
- }
62
- resolve(output);
63
- });
64
- });
32
+ styleNode.content = [css];
33
+ });
65
34
  }
66
35
 
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;
36
+ function runUncss(html, css, userOptions) {
37
+ if (typeof userOptions !== 'object') {
38
+ userOptions = {};
39
+ }
40
+
41
+ const options = { ...userOptions,
42
+ ...uncssOptions
43
+ };
44
+ return new Promise((resolve, reject) => {
45
+ options.raw = css;
46
+ (0, _uncss.default)(html, options, (error, output) => {
47
+ if (error) {
48
+ reject(error);
49
+ return;
50
+ }
51
+
52
+ resolve(output);
78
53
  });
54
+ });
55
+ }
79
56
 
80
- return function () {
81
- return selectors;
82
- };
57
+ const purgeFromHtml = function (tree) {
58
+ // content is not used as we can directly used the parsed HTML,
59
+ // making the process faster
60
+ const selectors = [];
61
+ tree.walk(node => {
62
+ const classes = node.attrs && node.attrs.class && node.attrs.class.split(' ') || [];
63
+ const ids = node.attrs && node.attrs.id && node.attrs.id.split(' ') || [];
64
+ selectors.push(...classes, ...ids);
65
+ node.tag && selectors.push(node.tag);
66
+ return node;
67
+ });
68
+ return () => selectors;
83
69
  };
84
70
 
85
71
  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 = {};
72
+ const css = (0, _helpers.extractCssFromStyleNode)(styleNode);
73
+ return runPurgecss(tree, css, purgecssOptions).then(css => {
74
+ if (css.trim().length === 0) {
75
+ styleNode.tag = false;
76
+ styleNode.content = [];
77
+ return;
100
78
  }
101
79
 
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
- });
80
+ styleNode.content = [css];
81
+ });
126
82
  }
127
83
 
84
+ function runPurgecss(tree, css, userOptions) {
85
+ if (typeof userOptions !== 'object') {
86
+ userOptions = {};
87
+ }
88
+
89
+ const options = { ...userOptions,
90
+ content: [{
91
+ raw: tree,
92
+ extension: 'html'
93
+ }],
94
+ css: [{
95
+ raw: css,
96
+ extension: 'css'
97
+ }],
98
+ extractors: [{
99
+ extractor: purgeFromHtml(tree),
100
+ extensions: ['html']
101
+ }]
102
+ };
103
+ return new _purgecss.default().purge(options).then(result => {
104
+ return result[0].css;
105
+ });
106
+ }
128
107
  /** Remove unused CSS */
108
+
109
+
129
110
  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
- });
111
+ const promises = [];
112
+ const html = userOptions.tool !== 'purgeCSS' && tree.render(tree);
113
+ tree.walk(node => {
114
+ if ((0, _helpers.isStyleNode)(node)) {
115
+ if (userOptions.tool === 'purgeCSS') {
116
+ promises.push(processStyleNodePurgeCSS(tree, node, userOptions));
117
+ } else {
118
+ promises.push(processStyleNodeUnCSS(html, node, userOptions));
119
+ }
120
+ }
143
121
 
144
- return Promise.all(promises).then(function () {
145
- return tree;
146
- });
122
+ return node;
123
+ });
124
+ return Promise.all(promises).then(() => tree);
147
125
  }
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = sortAttributes;
7
+
8
+ var _timsort = require("timsort");
9
+
10
+ const validOptions = new Set(['frequency', 'alphabetical']);
11
+
12
+ const processModuleOptions = options => {
13
+ if (options === true) return 'alphabetical';
14
+ return validOptions.has(options) ? options : false;
15
+ };
16
+
17
+ class AttributeTokenChain {
18
+ constructor() {
19
+ this.freqData = new Map(); // <attr, frequency>[]
20
+ }
21
+
22
+ addFromNodeAttrs(nodeAttrs) {
23
+ Object.keys(nodeAttrs).forEach(attrName => {
24
+ const attrNameLower = attrName.toLowerCase();
25
+
26
+ if (this.freqData.has(attrNameLower)) {
27
+ this.freqData.set(attrNameLower, this.freqData.get(attrNameLower) + 1);
28
+ } else {
29
+ this.freqData.set(attrNameLower, 1);
30
+ }
31
+ });
32
+ }
33
+
34
+ createSortOrder() {
35
+ let _sortOrder = [...this.freqData.entries()];
36
+ (0, _timsort.sort)(_sortOrder, (a, b) => b[1] - a[1]);
37
+ this.sortOrder = _sortOrder.map(i => i[0]);
38
+ }
39
+
40
+ sortFromNodeAttrs(nodeAttrs) {
41
+ const newAttrs = {}; // Convert node.attrs attrName into lower case.
42
+
43
+ const loweredNodeAttrs = {};
44
+ Object.entries(nodeAttrs).forEach(([attrName, attrValue]) => {
45
+ loweredNodeAttrs[attrName.toLowerCase()] = attrValue;
46
+ });
47
+
48
+ if (!this.sortOrder) {
49
+ this.createSortOrder();
50
+ }
51
+
52
+ this.sortOrder.forEach(attrNameLower => {
53
+ // The attrName inside "sortOrder" has been lowered
54
+ if (loweredNodeAttrs[attrNameLower]) {
55
+ newAttrs[attrNameLower] = loweredNodeAttrs[attrNameLower];
56
+ }
57
+ });
58
+ return newAttrs;
59
+ }
60
+
61
+ }
62
+ /** Sort attibutes */
63
+
64
+
65
+ function sortAttributes(tree, options, moduleOptions) {
66
+ const sortType = processModuleOptions(moduleOptions);
67
+
68
+ if (sortType === 'alphabetical') {
69
+ return sortAttributesInAlphabeticalOrder(tree);
70
+ }
71
+
72
+ if (sortType === 'frequency') {
73
+ return sortAttributesByFrequency(tree);
74
+ } // Invalid configuration
75
+
76
+
77
+ return tree;
78
+ }
79
+
80
+ function sortAttributesInAlphabeticalOrder(tree) {
81
+ tree.walk(node => {
82
+ if (!node.attrs) {
83
+ return node;
84
+ }
85
+
86
+ const newAttrs = {};
87
+ Object.keys(node.attrs).sort((a, b) => typeof a.localeCompare === 'function' ? a.localeCompare(b) : a - b).forEach(attr => newAttrs[attr] = node.attrs[attr]);
88
+ node.attrs = newAttrs;
89
+ return node;
90
+ });
91
+ return tree;
92
+ }
93
+
94
+ function sortAttributesByFrequency(tree) {
95
+ const tokenchain = new AttributeTokenChain(); // Traverse through tree to get frequency
96
+
97
+ tree.walk(node => {
98
+ if (!node.attrs) {
99
+ return node;
100
+ }
101
+
102
+ tokenchain.addFromNodeAttrs(node.attrs);
103
+ return node;
104
+ }); // Traverse through tree again, this time sort the attributes
105
+
106
+ tree.walk(node => {
107
+ if (!node.attrs) {
108
+ return node;
109
+ }
110
+
111
+ node.attrs = tokenchain.sortFromNodeAttrs(node.attrs);
112
+ return node;
113
+ });
114
+ return tree;
115
+ }
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = collapseAttributeWhitespace;
7
+
8
+ var _timsort = require("timsort");
9
+
10
+ var _collapseAttributeWhitespace = require("./collapseAttributeWhitespace");
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 = [...this.freqData.entries()];
37
+ (0, _timsort.sort)(_sortOrder, (a, b) => b[1] - a[1]);
38
+ this.sortOrder = _sortOrder.map(i => i[0]);
39
+ }
40
+
41
+ sortFromNodeAttrsArray(attrValuesArray) {
42
+ const resultArray = [];
43
+
44
+ if (!this.sortOrder) {
45
+ this.createSortOrder();
46
+ }
47
+
48
+ this.sortOrder.forEach(k => {
49
+ if (attrValuesArray.includes(k)) {
50
+ resultArray.push(k);
51
+ }
52
+ });
53
+ return resultArray;
54
+ }
55
+
56
+ }
57
+ /** Sort values inside list-like attributes (e.g. class, rel) */
58
+
59
+
60
+ function collapseAttributeWhitespace(tree, options, moduleOptions) {
61
+ const sortType = processModuleOptions(moduleOptions);
62
+
63
+ if (sortType === 'alphabetical') {
64
+ return sortAttributesWithListsInAlphabeticalOrder(tree);
65
+ }
66
+
67
+ if (sortType === 'frequency') {
68
+ return sortAttributesWithListsByFrequency(tree);
69
+ } // Invalid configuration
70
+
71
+
72
+ return tree;
73
+ }
74
+
75
+ function sortAttributesWithListsInAlphabeticalOrder(tree) {
76
+ tree.walk(node => {
77
+ if (!node.attrs) {
78
+ return node;
79
+ }
80
+
81
+ Object.keys(node.attrs).forEach(attrName => {
82
+ const attrNameLower = attrName.toLowerCase();
83
+
84
+ if (!_collapseAttributeWhitespace.attributesWithLists.has(attrNameLower)) {
85
+ return;
86
+ }
87
+
88
+ const attrValues = node.attrs[attrName].split(/\s/);
89
+ node.attrs[attrName] = attrValues.sort((a, b) => {
90
+ return typeof a.localeCompare === 'function' ? a.localeCompare(b) : a - b;
91
+ }).join(' ');
92
+ });
93
+ return node;
94
+ });
95
+ return tree;
96
+ }
97
+
98
+ function sortAttributesWithListsByFrequency(tree) {
99
+ const tokenChainObj = {}; // <attrNameLower: AttributeTokenChain>[]
100
+ // Traverse through tree to get frequency
101
+
102
+ tree.walk(node => {
103
+ if (!node.attrs) {
104
+ return node;
105
+ }
106
+
107
+ Object.entries(node.attrs).forEach(([attrName, attrValues]) => {
108
+ const attrNameLower = attrName.toLowerCase();
109
+
110
+ if (!_collapseAttributeWhitespace.attributesWithLists.has(attrNameLower)) {
111
+ return;
112
+ }
113
+
114
+ tokenChainObj[attrNameLower] = tokenChainObj[attrNameLower] || new AttributeTokenChain();
115
+ tokenChainObj[attrNameLower].addFromNodeAttrsArray(attrValues.split(/\s/));
116
+ });
117
+ return node;
118
+ }); // Traverse through tree again, this time sort the attribute values
119
+
120
+ tree.walk(node => {
121
+ if (!node.attrs) {
122
+ return node;
123
+ }
124
+
125
+ Object.entries(node.attrs).forEach(([attrName, attrValues]) => {
126
+ const attrNameLower = attrName.toLowerCase();
127
+
128
+ if (!_collapseAttributeWhitespace.attributesWithLists.has(attrNameLower)) {
129
+ return;
130
+ }
131
+
132
+ if (tokenChainObj[attrNameLower]) {
133
+ node.attrs[attrName] = tokenChainObj[attrNameLower].sortFromNodeAttrsArray(attrValues.split(/\s/)).join(' ');
134
+ }
135
+ });
136
+ return node;
137
+ });
138
+ }
@@ -1,23 +1,26 @@
1
- 'use strict';
1
+ "use strict";
2
2
 
3
3
  Object.defineProperty(exports, "__esModule", {
4
- value: true
4
+ value: true
5
5
  });
6
+ exports.default = void 0;
6
7
 
7
- var _safe = require('./safe');
8
-
9
- var _safe2 = _interopRequireDefault(_safe);
8
+ var _safe = _interopRequireDefault(require("./safe"));
10
9
 
11
10
  function _interopRequireDefault(obj) {
12
- return obj && obj.__esModule ? obj : { default: obj };
11
+ return obj && obj.__esModule ? obj : {
12
+ default: obj
13
+ };
13
14
  }
14
-
15
15
  /**
16
16
  * A safe preset for AMP pages (https://www.ampproject.org)
17
17
  */
18
- exports.default = Object.assign({}, _safe2.default, {
19
- collapseBooleanAttributes: {
20
- amphtml: true
21
- },
22
- minifyJs: false
23
- });
18
+
19
+
20
+ var _default = { ..._safe.default,
21
+ collapseBooleanAttributes: {
22
+ amphtml: true
23
+ },
24
+ minifyJs: false
25
+ };
26
+ exports.default = _default;
@@ -1,27 +1,33 @@
1
- 'use strict';
1
+ "use strict";
2
2
 
3
3
  Object.defineProperty(exports, "__esModule", {
4
- value: true
4
+ value: true
5
5
  });
6
+ exports.default = void 0;
6
7
 
7
- var _safe = require('./safe');
8
-
9
- var _safe2 = _interopRequireDefault(_safe);
8
+ var _safe = _interopRequireDefault(require("./safe"));
10
9
 
11
10
  function _interopRequireDefault(obj) {
12
- return obj && obj.__esModule ? obj : { default: obj };
11
+ return obj && obj.__esModule ? obj : {
12
+ default: obj
13
+ };
13
14
  }
14
-
15
15
  /**
16
16
  * Maximal minification (might break some pages)
17
17
  */
18
- exports.default = Object.assign({}, _safe2.default, {
19
- collapseWhitespace: 'all',
20
- removeComments: 'all',
21
- removeRedundantAttributes: true,
22
- removeUnusedCss: {},
23
- minifyCss: {
24
- preset: 'default'
25
- },
26
- minifySvg: {}
27
- });
18
+
19
+
20
+ var _default = { ..._safe.default,
21
+ collapseWhitespace: 'all',
22
+ removeComments: 'all',
23
+ removeAttributeQuotes: true,
24
+ removeRedundantAttributes: true,
25
+ removeUnusedCss: {},
26
+ minifyCss: {
27
+ preset: 'default'
28
+ },
29
+ minifySvg: {},
30
+ minifyConditionalComments: true,
31
+ removeOptionalTags: true
32
+ };
33
+ exports.default = _default;
@@ -1,31 +1,44 @@
1
- 'use strict';
1
+ "use strict";
2
2
 
3
3
  Object.defineProperty(exports, "__esModule", {
4
- value: true
4
+ value: true
5
5
  });
6
+ exports.default = void 0;
6
7
  /**
7
8
  * Minify HTML in a safe way without breaking anything.
8
9
  */
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 }, { convertShapeToPath: false }]
27
- },
28
- removeEmptyAttributes: true,
29
- removeRedundantAttributes: false,
30
- removeComments: 'safe'
31
- };
10
+
11
+ var _default = {
12
+ sortAttributes: false,
13
+ collapseAttributeWhitespace: true,
14
+ collapseBooleanAttributes: {
15
+ amphtml: false
16
+ },
17
+ collapseWhitespace: 'conservative',
18
+ custom: [],
19
+ deduplicateAttributeValues: true,
20
+ mergeScripts: true,
21
+ mergeStyles: true,
22
+ removeUnusedCss: false,
23
+ minifyCss: {
24
+ preset: 'default'
25
+ },
26
+ minifyJs: {},
27
+ minifyJson: {},
28
+ minifySvg: {
29
+ plugins: [{
30
+ collapseGroups: false
31
+ }, {
32
+ convertShapeToPath: false
33
+ }]
34
+ },
35
+ minifyConditionalComments: false,
36
+ removeEmptyAttributes: true,
37
+ removeRedundantAttributes: false,
38
+ removeComments: 'safe',
39
+ removeAttributeQuotes: false,
40
+ sortAttributesWithLists: 'alphabetical',
41
+ minifyUrls: false,
42
+ removeOptionalTags: false
43
+ };
44
+ exports.default = _default;