htmlnano 1.1.0 → 2.0.1

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.
Files changed (38) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +8 -13
  3. package/docs/docs/050-modules.md +54 -2
  4. package/docs/package-lock.json +14644 -824
  5. package/docs/versioned_docs/{version-1.1.0 → version-1.1.1}/010-introduction.md +0 -0
  6. package/docs/versioned_docs/{version-1.1.0 → version-1.1.1}/020-usage.md +0 -0
  7. package/docs/versioned_docs/{version-1.1.0 → version-1.1.1}/030-config.md +0 -0
  8. package/docs/versioned_docs/{version-1.1.0 → version-1.1.1}/040-presets.md +0 -0
  9. package/docs/versioned_docs/{version-1.1.0 → version-1.1.1}/050-modules.md +0 -0
  10. package/docs/versioned_docs/{version-1.1.0 → version-1.1.1}/060-contribute.md +0 -0
  11. package/docs/versioned_docs/version-2.0.0/010-introduction.md +22 -0
  12. package/docs/versioned_docs/version-2.0.0/020-usage.md +77 -0
  13. package/docs/versioned_docs/version-2.0.0/030-config.md +21 -0
  14. package/docs/versioned_docs/version-2.0.0/040-presets.md +75 -0
  15. package/docs/versioned_docs/version-2.0.0/050-modules.md +838 -0
  16. package/docs/versioned_docs/version-2.0.0/060-contribute.md +16 -0
  17. package/docs/versioned_sidebars/{version-1.1.0-sidebars.json → version-1.1.1-sidebars.json} +1 -1
  18. package/docs/versioned_sidebars/version-2.0.0-sidebars.json +8 -0
  19. package/docs/versions.json +2 -1
  20. package/lib/helpers.js +15 -2
  21. package/lib/htmlnano.js +78 -4
  22. package/lib/modules/collapseAttributeWhitespace.js +21 -21
  23. package/lib/modules/collapseBooleanAttributes.js +13 -19
  24. package/lib/modules/deduplicateAttributeValues.js +10 -16
  25. package/lib/modules/minifyCss.js +8 -8
  26. package/lib/modules/minifyJs.js +6 -9
  27. package/lib/modules/minifyJson.js +12 -23
  28. package/lib/modules/minifySvg.js +5 -2
  29. package/lib/modules/minifyUrls.js +29 -22
  30. package/lib/modules/normalizeAttributeValues.js +9 -15
  31. package/lib/modules/removeComments.js +17 -9
  32. package/lib/modules/removeEmptyAttributes.js +13 -16
  33. package/lib/modules/removeRedundantAttributes.js +19 -23
  34. package/lib/modules/removeUnusedCss.js +10 -10
  35. package/lib/presets/max.js +2 -0
  36. package/lib/presets/safe.js +2 -2
  37. package/package.json +49 -13
  38. package/uncss-fork.patch +0 -13
@@ -3,28 +3,36 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.default = removeComments;
6
+ exports.onContent = onContent;
7
+ exports.onNode = onNode;
7
8
 
8
9
  var _helpers = require("../helpers");
9
10
 
10
11
  const MATCH_EXCERPT_REGEXP = /<!-- ?more ?-->/i;
11
12
  /** Removes HTML comments */
12
13
 
13
- function removeComments(tree, options, removeType) {
14
+ function onNode(options, removeType) {
14
15
  if (removeType !== 'all' && removeType !== 'safe' && !isMatcher(removeType)) {
15
16
  removeType = 'safe';
16
17
  }
17
18
 
18
- tree.walk(node => {
19
- if (node.contents && node.contents.length) {
20
- node.contents = node.contents.filter(content => !isCommentToRemove(content, removeType));
21
- } else if (isCommentToRemove(node, removeType)) {
22
- node = '';
19
+ return node => {
20
+ if (isCommentToRemove(node, removeType)) {
21
+ return '';
23
22
  }
24
23
 
25
24
  return node;
26
- });
27
- return tree;
25
+ };
26
+ }
27
+
28
+ function onContent(options, removeType) {
29
+ if (removeType !== 'all' && removeType !== 'safe' && !isMatcher(removeType)) {
30
+ removeType = 'safe';
31
+ }
32
+
33
+ return contents => {
34
+ return contents.filter(content => !isCommentToRemove(content, removeType));
35
+ };
28
36
  }
29
37
 
30
38
  function isCommentToRemove(text, removeType) {
@@ -3,7 +3,10 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.default = removeEmptyAttributes;
6
+ exports.onAttrs = onAttrs;
7
+
8
+ var _helpers = require("../helpers");
9
+
7
10
  const safeToRemoveAttrs = {
8
11
  id: null,
9
12
  class: null,
@@ -52,24 +55,18 @@ const safeToRemoveAttrs = {
52
55
  value: ['button', 'input', 'li'],
53
56
  width: ['canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video']
54
57
  };
55
- /** Removes empty attributes */
56
-
57
- function removeEmptyAttributes(tree) {
58
- tree.walk(node => {
59
- if (!node.attrs) {
60
- return node;
61
- }
62
-
63
- Object.entries(node.attrs).forEach(([attrName, attrValue]) => {
64
- const attrNameLower = attrName.toLowerCase();
65
58
 
66
- if (attrNameLower.slice(0, 2).toLowerCase() === 'on' && attrName.length >= 5 || Object.hasOwnProperty.call(safeToRemoveAttrs, attrNameLower) && (safeToRemoveAttrs[attrNameLower] === null || safeToRemoveAttrs[attrNameLower].includes(node.tag))) {
59
+ function onAttrs() {
60
+ return (attrs, node) => {
61
+ const newAttrs = { ...attrs
62
+ };
63
+ Object.entries(attrs).forEach(([attrName, attrValue]) => {
64
+ if ((0, _helpers.isEventHandler)(attrName) || Object.hasOwnProperty.call(safeToRemoveAttrs, attrName) && (safeToRemoveAttrs[attrName] === null || safeToRemoveAttrs[attrName].includes(node.tag))) {
67
65
  if (attrValue === '' || (attrValue || '').match(/^\s+$/)) {
68
- delete node.attrs[attrName];
66
+ delete newAttrs[attrName];
69
67
  }
70
68
  }
71
69
  });
72
- return node;
73
- });
74
- return tree;
70
+ return newAttrs;
71
+ };
75
72
  }
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.default = removeRedundantAttributes;
6
+ exports.onAttrs = onAttrs;
7
7
  exports.redundantScriptTypes = void 0;
8
8
  // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#JavaScript_types
9
9
  const 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']);
@@ -20,8 +20,8 @@ const redundantAttributes = {
20
20
  },
21
21
  'script': {
22
22
  'language': 'javascript',
23
- 'type': node => {
24
- for (const [attrName, attrValue] of Object.entries(node.attrs)) {
23
+ 'type': attrs => {
24
+ for (const [attrName, attrValue] of Object.entries(attrs)) {
25
25
  if (attrName.toLowerCase() !== 'type') {
26
26
  continue;
27
27
  }
@@ -32,10 +32,10 @@ const redundantAttributes = {
32
32
  return false;
33
33
  },
34
34
  // Remove attribute if the function returns false
35
- 'charset': node => {
35
+ 'charset': attrs => {
36
36
  // The charset attribute only really makes sense on “external” SCRIPT elements:
37
37
  // http://perfectionkills.com/optimizing-html/#8_script_charset
38
- return node.attrs && !node.attrs.src;
38
+ return !attrs.src;
39
39
  }
40
40
  },
41
41
  'style': {
@@ -44,13 +44,13 @@ const redundantAttributes = {
44
44
  },
45
45
  'link': {
46
46
  'media': 'all',
47
- 'type': node => {
47
+ 'type': attrs => {
48
48
  // https://html.spec.whatwg.org/multipage/links.html#link-type-stylesheet
49
49
  let isRelStyleSheet = false;
50
50
  let isTypeTextCSS = false;
51
51
 
52
- if (node.attrs) {
53
- for (const [attrName, attrValue] of Object.entries(node.attrs)) {
52
+ if (attrs) {
53
+ for (const [attrName, attrValue] of Object.entries(attrs)) {
54
54
  if (attrName.toLowerCase() === 'rel' && attrValue === 'stylesheet') {
55
55
  isRelStyleSheet = true;
56
56
  }
@@ -115,13 +115,10 @@ const tagsHaveRedundantAttributes = new Set(Object.keys(redundantAttributes));
115
115
  const tagsHaveMissingValueDefaultAttributes = new Set(Object.keys(canBeReplacedWithEmptyStringAttributes));
116
116
  /** Removes redundant attributes */
117
117
 
118
- function removeRedundantAttributes(tree) {
119
- tree.walk(node => {
120
- if (!node.tag) {
121
- return node;
122
- }
123
-
124
- node.attrs = node.attrs || {};
118
+ function onAttrs() {
119
+ return (attrs, node) => {
120
+ if (!node.tag) return attrs;
121
+ const newAttrs = attrs;
125
122
 
126
123
  if (tagsHaveRedundantAttributes.has(node.tag)) {
127
124
  const tagRedundantAttributes = redundantAttributes[node.tag];
@@ -131,13 +128,13 @@ function removeRedundantAttributes(tree) {
131
128
  let isRemove = false;
132
129
 
133
130
  if (typeof tagRedundantAttributeValue === 'function') {
134
- isRemove = tagRedundantAttributeValue(node);
135
- } else if (node.attrs[redundantAttributeName] === tagRedundantAttributeValue) {
131
+ isRemove = tagRedundantAttributeValue(attrs);
132
+ } else if (attrs[redundantAttributeName] === tagRedundantAttributeValue) {
136
133
  isRemove = true;
137
134
  }
138
135
 
139
136
  if (isRemove) {
140
- delete node.attrs[redundantAttributeName];
137
+ delete newAttrs[redundantAttributeName];
141
138
  }
142
139
  }
143
140
  }
@@ -149,17 +146,16 @@ function removeRedundantAttributes(tree) {
149
146
  let tagMissingValueDefaultAttribute = tagMissingValueDefaultAttributes[canBeReplacedWithEmptyStringAttributeName];
150
147
  let isReplace = false;
151
148
 
152
- if (node.attrs[canBeReplacedWithEmptyStringAttributeName] === tagMissingValueDefaultAttribute) {
149
+ if (attrs[canBeReplacedWithEmptyStringAttributeName] === tagMissingValueDefaultAttribute) {
153
150
  isReplace = true;
154
151
  }
155
152
 
156
153
  if (isReplace) {
157
- node.attrs[canBeReplacedWithEmptyStringAttributeName] = '';
154
+ newAttrs[canBeReplacedWithEmptyStringAttributeName] = '';
158
155
  }
159
156
  }
160
157
  }
161
158
 
162
- return node;
163
- });
164
- return tree;
159
+ return newAttrs;
160
+ };
165
161
  }
@@ -7,13 +7,9 @@ exports.default = removeUnusedCss;
7
7
 
8
8
  var _helpers = require("../helpers");
9
9
 
10
- var _uncss = _interopRequireDefault(require("uncss"));
10
+ const uncss = (0, _helpers.optionalRequire)('uncss');
11
+ const purgecss = (0, _helpers.optionalRequire)('purgecss'); // These options must be set and shouldn't be overriden to ensure uncss doesn't look at linked stylesheets.
11
12
 
12
- var _purgecss = _interopRequireDefault(require("purgecss"));
13
-
14
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
-
16
- // These options must be set and shouldn't be overriden to ensure uncss doesn't look at linked stylesheets.
17
13
  const uncssOptions = {
18
14
  ignoreSheets: [/\s*/],
19
15
  stylesheets: []
@@ -43,7 +39,7 @@ function runUncss(html, css, userOptions) {
43
39
  };
44
40
  return new Promise((resolve, reject) => {
45
41
  options.raw = css;
46
- (0, _uncss.default)(html, options, (error, output) => {
42
+ uncss(html, options, (error, output) => {
47
43
  if (error) {
48
44
  reject(error);
49
45
  return;
@@ -100,7 +96,7 @@ function runPurgecss(tree, css, userOptions) {
100
96
  extensions: ['html']
101
97
  }]
102
98
  };
103
- return new _purgecss.default().purge(options).then(result => {
99
+ return new purgecss.PurgeCSS().purge(options).then(result => {
104
100
  return result[0].css;
105
101
  });
106
102
  }
@@ -113,9 +109,13 @@ function removeUnusedCss(tree, options, userOptions) {
113
109
  tree.walk(node => {
114
110
  if ((0, _helpers.isStyleNode)(node)) {
115
111
  if (userOptions.tool === 'purgeCSS') {
116
- promises.push(processStyleNodePurgeCSS(tree, node, userOptions));
112
+ if (purgecss) {
113
+ promises.push(processStyleNodePurgeCSS(tree, node, userOptions));
114
+ }
117
115
  } else {
118
- promises.push(processStyleNodeUnCSS(html, node, userOptions));
116
+ if (uncss) {
117
+ promises.push(processStyleNodeUnCSS(html, node, userOptions));
118
+ }
119
119
  }
120
120
  }
121
121
 
@@ -22,6 +22,8 @@ var _default = { ..._safe.default,
22
22
  removeComments: 'all',
23
23
  removeAttributeQuotes: true,
24
24
  removeRedundantAttributes: true,
25
+ mergeScripts: true,
26
+ mergeStyles: true,
25
27
  removeUnusedCss: {},
26
28
  minifyCss: {
27
29
  preset: 'default'
@@ -17,8 +17,8 @@ var _default = {
17
17
  collapseWhitespace: 'conservative',
18
18
  custom: [],
19
19
  deduplicateAttributeValues: true,
20
- mergeScripts: true,
21
- mergeStyles: true,
20
+ mergeScripts: false,
21
+ mergeStyles: false,
22
22
  removeUnusedCss: false,
23
23
  minifyCss: {
24
24
  preset: 'default'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "htmlnano",
3
- "version": "1.1.0",
3
+ "version": "2.0.1",
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>",
@@ -40,28 +40,64 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "cosmiconfig": "^7.0.1",
43
- "cssnano": "^5.0.8",
44
- "postcss": "^8.3.6",
45
43
  "posthtml": "^0.16.5",
46
- "purgecss": "^4.0.0",
47
- "relateurl": "^0.2.7",
48
- "srcset": "^4.0.0",
49
- "svgo": "^2.6.1",
50
- "terser": "^5.8.0",
51
- "timsort": "^0.3.0",
52
- "uncss": "^0.17.3"
44
+ "timsort": "^0.3.0"
53
45
  },
54
46
  "devDependencies": {
55
47
  "@babel/cli": "^7.15.7",
56
48
  "@babel/core": "^7.15.5",
49
+ "@babel/eslint-parser": "^7.17.0",
57
50
  "@babel/preset-env": "^7.15.6",
58
51
  "@babel/register": "^7.15.3",
59
- "babel-eslint": "^10.1.0",
60
- "eslint": "^7.32.0",
52
+ "cssnano": "^5.0.11",
53
+ "eslint": "^8.12.0",
61
54
  "expect": "^27.2.0",
62
55
  "mocha": "^9.1.0",
56
+ "postcss": "^8.3.11",
57
+ "purgecss": "^4.0.3",
58
+ "relateurl": "^0.2.7",
63
59
  "release-it": "^14.11.5",
64
- "rimraf": "^3.0.2"
60
+ "rimraf": "^3.0.2",
61
+ "srcset": "^5.0.0",
62
+ "svgo": "^2.8.0",
63
+ "terser": "^5.10.0",
64
+ "uncss": "^0.17.3"
65
+ },
66
+ "peerDependencies": {
67
+ "cssnano": "^5.0.11",
68
+ "postcss": "^8.3.11",
69
+ "purgecss": "^4.0.3",
70
+ "relateurl": "^0.2.7",
71
+ "srcset": "^5.0.0",
72
+ "svgo": "^2.8.0",
73
+ "terser": "^5.10.0",
74
+ "uncss": "^0.17.3"
75
+ },
76
+ "peerDependenciesMeta": {
77
+ "cssnano": {
78
+ "optional": true
79
+ },
80
+ "postcss": {
81
+ "optional": true
82
+ },
83
+ "purgecss": {
84
+ "optional": true
85
+ },
86
+ "relateurl": {
87
+ "optional": true
88
+ },
89
+ "srcset": {
90
+ "optional": true
91
+ },
92
+ "svgo": {
93
+ "optional": true
94
+ },
95
+ "terser": {
96
+ "optional": true
97
+ },
98
+ "uncss": {
99
+ "optional": true
100
+ }
65
101
  },
66
102
  "repository": {
67
103
  "type": "git",
package/uncss-fork.patch DELETED
@@ -1,13 +0,0 @@
1
- diff --git a/package.json b/package.json
2
- index a127c0f..66455bb 100644
3
- --- a/package.json
4
- +++ b/package.json
5
- @@ -49,7 +49,7 @@
6
- "svgo": "^2.4.0",
7
- "terser": "^5.7.0",
8
- "timsort": "^0.3.0",
9
- - "uncss": "^0.17.3"
10
- + "@novaatwarren/uncss": "^0.17.4"
11
- },
12
- "devDependencies": {
13
- "@babel/cli": "^7.14.3",