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/CHANGELOG.md +32 -3
- package/docs/package-lock.json +7454 -11530
- package/docs/package.json +3 -3
- package/lib/helpers.js +2 -1
- package/lib/htmlnano.js +7 -4
- package/lib/modules/mergeScripts.js +3 -1
- package/lib/modules/mergeStyles.js +3 -1
- package/lib/modules/minifyCss.js +4 -0
- package/lib/modules/minifyJs.js +17 -1
- package/lib/modules/minifyJson.js +4 -0
- package/lib/modules/minifySvg.js +11 -11
- package/lib/modules/normalizeAttributeValues.js +0 -5
- package/package.json +8 -8
- package/test.js +48 -0
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.
|
|
17
|
-
"@docusaurus/preset-classic": "2.
|
|
16
|
+
"@docusaurus/core": "2.2.0",
|
|
17
|
+
"@docusaurus/preset-classic": "2.2.0",
|
|
18
18
|
"@mdx-js/react": "^1.6.21",
|
|
19
|
-
"@svgr/webpack": "^
|
|
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
|
-
|
|
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
|
-
|
|
22
|
-
|
|
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
|
-
|
|
39
|
+
rest = rc.config;
|
|
37
40
|
}
|
|
38
41
|
}
|
|
39
42
|
}
|
|
40
|
-
return [
|
|
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 (
|
|
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
|
-
|
|
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)) {
|
package/lib/modules/minifyCss.js
CHANGED
|
@@ -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) {
|
package/lib/modules/minifyJs.js
CHANGED
|
@@ -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
|
package/lib/modules/minifySvg.js
CHANGED
|
@@ -17,22 +17,22 @@ function minifySvg(tree, options, svgoOptions = {}) {
|
|
|
17
17
|
closingSingleTag: 'slash',
|
|
18
18
|
quoteAllAttributes: true
|
|
19
19
|
});
|
|
20
|
-
|
|
21
|
-
|
|
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(
|
|
24
|
-
if (
|
|
25
|
-
console.error(
|
|
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
|
+
"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": "^
|
|
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": "^
|
|
47
|
+
"cssnano": "^6.0.0",
|
|
48
48
|
"eslint": "^8.12.0",
|
|
49
|
-
"expect": "^
|
|
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": "^
|
|
54
|
+
"rimraf": "^5.0.0",
|
|
55
55
|
"srcset": "4.0.0",
|
|
56
|
-
"svgo": "^
|
|
56
|
+
"svgo": "^3.0.2",
|
|
57
57
|
"terser": "^5.10.0",
|
|
58
58
|
"uncss": "^0.17.3"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
|
-
"cssnano": "^
|
|
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": "^
|
|
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
|
+
});
|