htmlnano 2.0.3 → 2.0.4

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/docs/package.json CHANGED
@@ -13,10 +13,10 @@
13
13
  "write-heading-ids": "docusaurus write-heading-ids"
14
14
  },
15
15
  "dependencies": {
16
- "@docusaurus/core": "2.0.0-beta.4",
17
- "@docusaurus/preset-classic": "2.0.0-beta.4",
16
+ "@docusaurus/core": "2.2.0",
17
+ "@docusaurus/preset-classic": "2.2.0",
18
18
  "@mdx-js/react": "^1.6.21",
19
- "@svgr/webpack": "^5.5.0",
19
+ "@svgr/webpack": "^6.5.1",
20
20
  "clsx": "^1.1.1",
21
21
  "file-loader": "^6.2.0",
22
22
  "prism-react-renderer": "^1.2.1",
package/lib/helpers.js CHANGED
@@ -42,7 +42,8 @@ function isEventHandler(attributeName) {
42
42
  }
43
43
  function optionalRequire(moduleName) {
44
44
  try {
45
- return require(moduleName);
45
+ const module = require(moduleName);
46
+ return module.default || module;
46
47
  } catch (e) {
47
48
  if (e.code === 'MODULE_NOT_FOUND') {
48
49
  return null;
package/lib/htmlnano.js CHANGED
@@ -18,8 +18,11 @@ const presets = {
18
18
  max: _max.default
19
19
  };
20
20
  function loadConfig(options, preset, configPath) {
21
- var _options;
22
- if (!((_options = options) !== null && _options !== void 0 && _options.skipConfigLoading)) {
21
+ let {
22
+ skipConfigLoading = false,
23
+ ...rest
24
+ } = options || {};
25
+ if (!skipConfigLoading) {
23
26
  const explorer = (0, _cosmiconfig.cosmiconfigSync)(_package.default.name);
24
27
  const rc = configPath ? explorer.load(configPath) : explorer.search();
25
28
  if (rc) {
@@ -33,11 +36,11 @@ function loadConfig(options, preset, configPath) {
33
36
  delete rc.config.preset;
34
37
  }
35
38
  if (!options) {
36
- options = rc.config;
39
+ rest = rc.config;
37
40
  }
38
41
  }
39
42
  }
40
- return [options || {}, preset || _safe.default];
43
+ return [rest || {}, preset || _safe.default];
41
44
  }
42
45
  const optionalDependencies = {
43
46
  minifyCss: ['cssnano', 'postcss'],
@@ -12,7 +12,9 @@ function mergeScripts(tree) {
12
12
  tag: 'script'
13
13
  }, node => {
14
14
  const nodeAttrs = node.attrs || {};
15
- if (nodeAttrs.src) {
15
+ if ('src' in nodeAttrs
16
+ // Skip SRI, reasons are documented in "minifyJs" module
17
+ || 'integrity' in nodeAttrs) {
16
18
  scriptSrcIndex++;
17
19
  return node;
18
20
  }
@@ -14,7 +14,9 @@ function mergeStyles(tree) {
14
14
  const nodeAttrs = node.attrs || {};
15
15
  // Skip <style scoped></style>
16
16
  // https://developer.mozilla.org/en/docs/Web/HTML/Element/style
17
- if (nodeAttrs.scoped !== undefined) {
17
+ //
18
+ // Also skip SRI, reasons are documented in "minifyJs" module
19
+ if ('scoped' in nodeAttrs || 'integrity' in nodeAttrs) {
18
20
  return node;
19
21
  }
20
22
  if ((0, _helpers.isAmpBoilerplate)(node)) {
@@ -21,6 +21,10 @@ function minifyCss(tree, options, cssnanoOptions) {
21
21
  }
22
22
  let promises = [];
23
23
  tree.walk(node => {
24
+ // Skip SRI, reasons are documented in "minifyJs" module
25
+ if (node.attrs && 'integrity' in node.attrs) {
26
+ return node;
27
+ }
24
28
  if ((0, _helpers.isStyleNode)(node)) {
25
29
  promises.push(processStyleNode(node, cssnanoOptions));
26
30
  } else if (node.attrs && node.attrs.style) {
@@ -13,8 +13,24 @@ function minifyJs(tree, options, terserOptions) {
13
13
  if (!terser) return tree;
14
14
  let promises = [];
15
15
  tree.walk(node => {
16
+ const nodeAttrs = node.attrs || {};
17
+
18
+ /**
19
+ * Skip SRI
20
+ *
21
+ * If the input <script /> has an SRI attribute, it means that the original <script /> could be trusted,
22
+ * and should not be altered anymore.
23
+ *
24
+ * htmlnano is exactly an MITM that SRI is designed to protect from. If htmlnano or its dependencies get
25
+ * compromised and introduces malicious code, then it is up to the original SRI to protect the end user.
26
+ *
27
+ * So htmlnano will simply skip <script /> that has SRI.
28
+ * If developers do trust htmlnano, they should generate SRI after htmlnano modify the <script />.
29
+ */
30
+ if ('integrity' in nodeAttrs) {
31
+ return node;
32
+ }
16
33
  if (node.tag && node.tag === 'script') {
17
- const nodeAttrs = node.attrs || {};
18
34
  const mimeType = nodeAttrs.type || 'text/javascript';
19
35
  if (_removeRedundantAttributes.redundantScriptTypes.has(mimeType) || mimeType === 'module') {
20
36
  promises.push(processScriptNode(node, terserOptions));
@@ -7,6 +7,10 @@ exports.onContent = onContent;
7
7
  const rNodeAttrsTypeJson = /(\/|\+)json/;
8
8
  function onContent() {
9
9
  return (content, node) => {
10
+ // Skip SRI, reasons are documented in "minifyJs" module
11
+ if (node.attrs && 'integrity' in node.attrs) {
12
+ return content;
13
+ }
10
14
  if (node.attrs && node.attrs.type && rNodeAttrsTypeJson.test(node.attrs.type)) {
11
15
  try {
12
16
  // cast minified JSON to an array
@@ -17,22 +17,22 @@ function minifySvg(tree, options, svgoOptions = {}) {
17
17
  closingSingleTag: 'slash',
18
18
  quoteAllAttributes: true
19
19
  });
20
- const result = svgo.optimize(svgStr, svgoOptions);
21
- if (result.error) {
20
+ try {
21
+ const result = svgo.optimize(svgStr, svgoOptions);
22
+ node.tag = false;
23
+ node.attrs = {};
24
+ // result.data is a string, we need to cast it to an array
25
+ node.content = [result.data];
26
+ return node;
27
+ } catch (error) {
22
28
  console.error('htmlnano fails to minify the svg:');
23
- console.error(result.error);
24
- if (result.modernError) {
25
- console.error(result.modernError);
29
+ console.error(error);
30
+ if (error.name === 'SvgoParserError') {
31
+ console.error(error.toString());
26
32
  }
27
-
28
33
  // We return the node as-is
29
34
  return node;
30
35
  }
31
- node.tag = false;
32
- node.attrs = {};
33
- // result.data is a string, we need to cast it to an array
34
- node.content = [result.data];
35
- return node;
36
36
  });
37
37
  return tree;
38
38
  }
@@ -63,11 +63,6 @@ const invalidValueDefault = {
63
63
  default: 'metadata',
64
64
  valid: ['subtitles', 'captions', 'descriptions', 'chapters', 'metadata']
65
65
  },
66
- autocomplete: {
67
- tag: null,
68
- default: 'on',
69
- valid: ['on', 'off']
70
- },
71
66
  type: {
72
67
  tag: ['button'],
73
68
  default: 'submit',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "htmlnano",
3
- "version": "2.0.3",
3
+ "version": "2.0.4",
4
4
  "description": "Modular HTML minifier, built on top of the PostHTML",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -34,7 +34,7 @@
34
34
  ]
35
35
  },
36
36
  "dependencies": {
37
- "cosmiconfig": "^7.0.1",
37
+ "cosmiconfig": "^8.0.0",
38
38
  "posthtml": "^0.16.5",
39
39
  "timsort": "^0.3.0"
40
40
  },
@@ -44,26 +44,26 @@
44
44
  "@babel/eslint-parser": "^7.17.0",
45
45
  "@babel/preset-env": "^7.15.6",
46
46
  "@babel/register": "^7.15.3",
47
- "cssnano": "^5.1.12",
47
+ "cssnano": "^6.0.0",
48
48
  "eslint": "^8.12.0",
49
- "expect": "^27.2.0",
49
+ "expect": "^29.0.0",
50
50
  "mocha": "^10.1.0",
51
51
  "postcss": "^8.3.11",
52
52
  "purgecss": "^5.0.0",
53
53
  "relateurl": "^0.2.7",
54
- "rimraf": "^3.0.2",
54
+ "rimraf": "^5.0.0",
55
55
  "srcset": "4.0.0",
56
- "svgo": "^2.8.0",
56
+ "svgo": "^3.0.2",
57
57
  "terser": "^5.10.0",
58
58
  "uncss": "^0.17.3"
59
59
  },
60
60
  "peerDependencies": {
61
- "cssnano": "^5.0.11",
61
+ "cssnano": "^6.0.0",
62
62
  "postcss": "^8.3.11",
63
63
  "purgecss": "^5.0.0",
64
64
  "relateurl": "^0.2.7",
65
65
  "srcset": "4.0.0",
66
- "svgo": "^2.8.0",
66
+ "svgo": "^3.0.2",
67
67
  "terser": "^5.10.0",
68
68
  "uncss": "^0.17.3"
69
69
  },
package/test.js ADDED
@@ -0,0 +1,48 @@
1
+ const htmlnano = require('.');
2
+ // const posthtml = require('posthtml');
3
+ const safePreset = require('./lib/presets/safe');
4
+ // const options = {
5
+ // minifySvg: false,
6
+ // minifyJs: false,
7
+ // };
8
+ // // posthtml, posthtml-render, and posthtml-parse options
9
+ // const postHtmlOptions = {
10
+ // sync: true, // https://github.com/posthtml/posthtml#usage
11
+ // lowerCaseTags: true, // https://github.com/posthtml/posthtml-parser#options
12
+ // quoteAllAttributes: false, // https://github.com/posthtml/posthtml-render#options
13
+ // };
14
+
15
+ // const html = `
16
+ // <!doctype html>
17
+ // <html lang="en">
18
+ // <head>
19
+ // <meta charset="utf-8">
20
+ // <title></title>
21
+ // <script class="fob">alert(1)</script>
22
+ // <script>alert(2)</script>
23
+ // </head>
24
+ // <body>
25
+ // <script>alert(3)</script>
26
+ // <script>alert(4)</script>
27
+ // </body>
28
+ // </html>
29
+ // `;
30
+
31
+ const options = {
32
+ minifySvg: safePreset.minifySvg,
33
+ };
34
+ const html = `
35
+ <input type="text" class="form-control" name="testInput" autofocus="" autocomplete="off" id="testId"><a id="testId" href="#" class="testClass"></a><img width="20" src="../images/image.png" height="40" alt="image" class="cls" id="id2">
36
+ `;
37
+
38
+ htmlnano
39
+ // "preset" arg might be skipped (see "Presets" section below for more info)
40
+ // "postHtmlOptions" arg might be skipped
41
+ .process(html)
42
+ .then(function (result) {
43
+ // result.html is minified
44
+ console.log(result.html);
45
+ })
46
+ .catch(function (err) {
47
+ console.error(err);
48
+ });