htmlnano 2.0.3 → 2.1.0

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 (81) hide show
  1. package/.eslintignore +3 -2
  2. package/CHANGELOG.md +43 -3
  3. package/docs/docs/040-presets.md +4 -4
  4. package/docs/docs/050-modules.md +1 -1
  5. package/docs/docs/060-contribute.md +1 -1
  6. package/docs/docusaurus.config.js +5 -0
  7. package/docs/package-lock.json +7592 -11615
  8. package/docs/package.json +4 -3
  9. package/docs/versioned_docs/version-1.1.1/040-presets.md +4 -4
  10. package/docs/versioned_docs/version-1.1.1/050-modules.md +1 -2
  11. package/docs/versioned_docs/version-1.1.1/060-contribute.md +1 -1
  12. package/docs/versioned_docs/version-2.0.0/040-presets.md +4 -4
  13. package/docs/versioned_docs/version-2.0.0/050-modules.md +1 -1
  14. package/docs/versioned_docs/version-2.0.0/060-contribute.md +1 -1
  15. package/index.cjs +11 -0
  16. package/index.d.cts +3 -0
  17. package/index.d.mts +3 -0
  18. package/index.d.ts +3 -3
  19. package/index.mjs +2 -0
  20. package/lib/helpers.cjs +78 -0
  21. package/lib/helpers.mjs +52 -0
  22. package/lib/htmlnano.cjs +200 -0
  23. package/lib/htmlnano.mjs +196 -0
  24. package/lib/modules/{collapseAttributeWhitespace.js → collapseAttributeWhitespace.cjs} +2 -3
  25. package/lib/modules/collapseAttributeWhitespace.mjs +104 -0
  26. package/lib/modules/collapseBooleanAttributes.mjs +175 -0
  27. package/lib/modules/{collapseWhitespace.js → collapseWhitespace.cjs} +3 -2
  28. package/lib/modules/collapseWhitespace.mjs +132 -0
  29. package/lib/modules/custom.mjs +16 -0
  30. package/lib/modules/{deduplicateAttributeValues.js → deduplicateAttributeValues.cjs} +1 -1
  31. package/lib/modules/deduplicateAttributeValues.mjs +40 -0
  32. package/lib/modules/example.cjs +85 -0
  33. package/lib/modules/example.mjs +75 -0
  34. package/lib/modules/{mergeScripts.js → mergeScripts.cjs} +3 -1
  35. package/lib/modules/mergeScripts.mjs +56 -0
  36. package/lib/modules/{mergeStyles.js → mergeStyles.cjs} +4 -2
  37. package/lib/modules/mergeStyles.mjs +36 -0
  38. package/lib/modules/{minifyConditionalComments.js → minifyConditionalComments.cjs} +2 -2
  39. package/lib/modules/minifyConditionalComments.mjs +49 -0
  40. package/lib/modules/{minifyCss.js → minifyCss.cjs} +12 -8
  41. package/lib/modules/minifyCss.mjs +88 -0
  42. package/lib/modules/{minifyJs.js → minifyJs.cjs} +25 -10
  43. package/lib/modules/minifyJs.mjs +121 -0
  44. package/lib/modules/{minifyJson.js → minifyJson.cjs} +4 -0
  45. package/lib/modules/minifyJson.mjs +21 -0
  46. package/lib/modules/minifySvg.cjs +37 -0
  47. package/lib/modules/minifySvg.mjs +30 -0
  48. package/lib/modules/{minifyUrls.js → minifyUrls.cjs} +7 -8
  49. package/lib/modules/minifyUrls.mjs +229 -0
  50. package/lib/modules/{normalizeAttributeValues.js → normalizeAttributeValues.cjs} +0 -5
  51. package/lib/modules/normalizeAttributeValues.mjs +140 -0
  52. package/lib/modules/removeAttributeQuotes.mjs +12 -0
  53. package/lib/modules/{removeComments.js → removeComments.cjs} +1 -1
  54. package/lib/modules/removeComments.mjs +92 -0
  55. package/lib/modules/{removeEmptyAttributes.js → removeEmptyAttributes.cjs} +1 -1
  56. package/lib/modules/removeEmptyAttributes.mjs +121 -0
  57. package/lib/modules/{removeOptionalTags.js → removeOptionalTags.cjs} +1 -1
  58. package/lib/modules/removeOptionalTags.mjs +225 -0
  59. package/lib/modules/{removeRedundantAttributes.js → removeRedundantAttributes.cjs} +1 -2
  60. package/lib/modules/removeRedundantAttributes.mjs +141 -0
  61. package/lib/modules/{removeUnusedCss.js → removeUnusedCss.cjs} +12 -13
  62. package/lib/modules/removeUnusedCss.mjs +122 -0
  63. package/lib/modules/sortAttributes.mjs +121 -0
  64. package/lib/modules/{sortAttributesWithLists.js → sortAttributesWithLists.cjs} +1 -1
  65. package/lib/modules/sortAttributesWithLists.mjs +135 -0
  66. package/lib/presets/{ampSafe.js → ampSafe.cjs} +4 -9
  67. package/lib/presets/ampSafe.mjs +11 -0
  68. package/lib/presets/{max.js → max.cjs} +4 -9
  69. package/lib/presets/max.mjs +20 -0
  70. package/lib/presets/{safe.js → safe.cjs} +2 -3
  71. package/lib/presets/safe.mjs +65 -0
  72. package/package.json +40 -12
  73. package/test.js +48 -0
  74. package/index.js +0 -1
  75. package/lib/helpers.js +0 -52
  76. package/lib/htmlnano.js +0 -147
  77. package/lib/modules/minifySvg.js +0 -38
  78. /package/lib/modules/{collapseBooleanAttributes.js → collapseBooleanAttributes.cjs} +0 -0
  79. /package/lib/modules/{custom.js → custom.cjs} +0 -0
  80. /package/lib/modules/{removeAttributeQuotes.js → removeAttributeQuotes.cjs} +0 -0
  81. /package/lib/modules/{sortAttributes.js → sortAttributes.cjs} +0 -0
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
+ });
package/index.js DELETED
@@ -1 +0,0 @@
1
- module.exports = require('./lib/htmlnano').default;
package/lib/helpers.js DELETED
@@ -1,52 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.extractCssFromStyleNode = extractCssFromStyleNode;
7
- exports.isAmpBoilerplate = isAmpBoilerplate;
8
- exports.isComment = isComment;
9
- exports.isConditionalComment = isConditionalComment;
10
- exports.isEventHandler = isEventHandler;
11
- exports.isStyleNode = isStyleNode;
12
- exports.optionalRequire = optionalRequire;
13
- const ampBoilerplateAttributes = ['amp-boilerplate', 'amp4ads-boilerplate', 'amp4email-boilerplate'];
14
- function isAmpBoilerplate(node) {
15
- if (!node.attrs) {
16
- return false;
17
- }
18
- for (const attr of ampBoilerplateAttributes) {
19
- if (attr in node.attrs) {
20
- return true;
21
- }
22
- }
23
- return false;
24
- }
25
- function isComment(content) {
26
- if (typeof content === 'string') {
27
- return content.trim().startsWith('<!--');
28
- }
29
- return false;
30
- }
31
- function isConditionalComment(content) {
32
- return (content || '').trim().startsWith('<!--[if');
33
- }
34
- function isStyleNode(node) {
35
- return node.tag === 'style' && !isAmpBoilerplate(node) && 'content' in node && node.content.length > 0;
36
- }
37
- function extractCssFromStyleNode(node) {
38
- return Array.isArray(node.content) ? node.content.join(' ') : node.content;
39
- }
40
- function isEventHandler(attributeName) {
41
- return attributeName && attributeName.slice && attributeName.slice(0, 2).toLowerCase() === 'on' && attributeName.length >= 5;
42
- }
43
- function optionalRequire(moduleName) {
44
- try {
45
- return require(moduleName);
46
- } catch (e) {
47
- if (e.code === 'MODULE_NOT_FOUND') {
48
- return null;
49
- }
50
- throw e;
51
- }
52
- }
package/lib/htmlnano.js DELETED
@@ -1,147 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = void 0;
7
- exports.loadConfig = loadConfig;
8
- var _posthtml = _interopRequireDefault(require("posthtml"));
9
- var _cosmiconfig = require("cosmiconfig");
10
- var _safe = _interopRequireDefault(require("./presets/safe"));
11
- var _ampSafe = _interopRequireDefault(require("./presets/ampSafe"));
12
- var _max = _interopRequireDefault(require("./presets/max"));
13
- var _package = _interopRequireDefault(require("../package.json"));
14
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
- const presets = {
16
- safe: _safe.default,
17
- ampSafe: _ampSafe.default,
18
- max: _max.default
19
- };
20
- function loadConfig(options, preset, configPath) {
21
- var _options;
22
- if (!((_options = options) !== null && _options !== void 0 && _options.skipConfigLoading)) {
23
- const explorer = (0, _cosmiconfig.cosmiconfigSync)(_package.default.name);
24
- const rc = configPath ? explorer.load(configPath) : explorer.search();
25
- if (rc) {
26
- const {
27
- preset: presetName
28
- } = rc.config;
29
- if (presetName) {
30
- if (!preset && presets[presetName]) {
31
- preset = presets[presetName];
32
- }
33
- delete rc.config.preset;
34
- }
35
- if (!options) {
36
- options = rc.config;
37
- }
38
- }
39
- }
40
- return [options || {}, preset || _safe.default];
41
- }
42
- const optionalDependencies = {
43
- minifyCss: ['cssnano', 'postcss'],
44
- minifyJs: ['terser'],
45
- minifyUrl: ['relateurl', 'srcset', 'terser'],
46
- minifySvg: ['svgo']
47
- };
48
- function htmlnano(optionsRun, presetRun) {
49
- let [options, preset] = loadConfig(optionsRun, presetRun);
50
- return function minifier(tree) {
51
- const nodeHandlers = [];
52
- const attrsHandlers = [];
53
- const contentsHandlers = [];
54
- options = {
55
- ...preset,
56
- ...options
57
- };
58
- let promise = Promise.resolve(tree);
59
- for (const [moduleName, moduleOptions] of Object.entries(options)) {
60
- if (!moduleOptions) {
61
- // The module is disabled
62
- continue;
63
- }
64
- if (_safe.default[moduleName] === undefined) {
65
- throw new Error('Module "' + moduleName + '" is not defined');
66
- }
67
- (optionalDependencies[moduleName] || []).forEach(dependency => {
68
- try {
69
- require(dependency);
70
- } catch (e) {
71
- if (e.code === 'MODULE_NOT_FOUND') {
72
- console.warn(`You have to install "${dependency}" in order to use htmlnano's "${moduleName}" module`);
73
- } else {
74
- throw e;
75
- }
76
- }
77
- });
78
- const module = require('./modules/' + moduleName);
79
- if (typeof module.onAttrs === 'function') {
80
- attrsHandlers.push(module.onAttrs(options, moduleOptions));
81
- }
82
- if (typeof module.onContent === 'function') {
83
- contentsHandlers.push(module.onContent(options, moduleOptions));
84
- }
85
- if (typeof module.onNode === 'function') {
86
- nodeHandlers.push(module.onNode(options, moduleOptions));
87
- }
88
- if (typeof module.default === 'function') {
89
- promise = promise.then(tree => module.default(tree, options, moduleOptions));
90
- }
91
- }
92
- if (attrsHandlers.length + contentsHandlers.length + nodeHandlers.length === 0) {
93
- return promise;
94
- }
95
- return promise.then(tree => {
96
- tree.walk(node => {
97
- if (node) {
98
- if (node.attrs && typeof node.attrs === 'object') {
99
- // Convert all attrs' key to lower case
100
- let newAttrsObj = {};
101
- Object.entries(node.attrs).forEach(([attrName, attrValue]) => {
102
- newAttrsObj[attrName.toLowerCase()] = attrValue;
103
- });
104
- for (const handler of attrsHandlers) {
105
- newAttrsObj = handler(newAttrsObj, node);
106
- }
107
- node.attrs = newAttrsObj;
108
- }
109
- if (node.content) {
110
- node.content = typeof node.content === 'string' ? [node.content] : node.content;
111
- if (Array.isArray(node.content) && node.content.length > 0) {
112
- for (const handler of contentsHandlers) {
113
- const result = handler(node.content, node);
114
- node.content = typeof result === 'string' ? [result] : result;
115
- }
116
- }
117
- }
118
- for (const handler of nodeHandlers) {
119
- node = handler(node);
120
- }
121
- }
122
- return node;
123
- });
124
- return tree;
125
- });
126
- };
127
- }
128
- htmlnano.getRequiredOptionalDependencies = function (optionsRun, presetRun) {
129
- const [options] = loadConfig(optionsRun, presetRun);
130
- return [...new Set(Object.keys(options).filter(moduleName => options[moduleName]).map(moduleName => optionalDependencies[moduleName]).flat())];
131
- };
132
- htmlnano.process = function (html, options, preset, postHtmlOptions) {
133
- return (0, _posthtml.default)([htmlnano(options, preset)]).process(html, postHtmlOptions);
134
- };
135
-
136
- // https://github.com/webpack-contrib/html-minimizer-webpack-plugin/blob/faca00f2219514bc671c5942685721f0b5dbaa70/src/utils.js#L74
137
- htmlnano.htmlMinimizerWebpackPluginMinify = function htmlNano(input, minimizerOptions = {}) {
138
- const [[, code]] = Object.entries(input);
139
- return htmlnano.process(code, minimizerOptions, presets.safe).then(result => {
140
- return {
141
- code: result.html
142
- };
143
- });
144
- };
145
- htmlnano.presets = presets;
146
- var _default = htmlnano;
147
- exports.default = _default;
@@ -1,38 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = minifySvg;
7
- var _helpers = require("../helpers");
8
- const svgo = (0, _helpers.optionalRequire)('svgo');
9
-
10
- /** Minify SVG with SVGO */
11
- function minifySvg(tree, options, svgoOptions = {}) {
12
- if (!svgo) return tree;
13
- tree.match({
14
- tag: 'svg'
15
- }, node => {
16
- let svgStr = tree.render(node, {
17
- closingSingleTag: 'slash',
18
- quoteAllAttributes: true
19
- });
20
- const result = svgo.optimize(svgStr, svgoOptions);
21
- if (result.error) {
22
- console.error('htmlnano fails to minify the svg:');
23
- console.error(result.error);
24
- if (result.modernError) {
25
- console.error(result.modernError);
26
- }
27
-
28
- // We return the node as-is
29
- return node;
30
- }
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
- });
37
- return tree;
38
- }
File without changes