htmlnano 2.1.2 → 2.1.3

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 (186) hide show
  1. package/README.md +53 -12
  2. package/dist/_modules/collapseAttributeWhitespace.d.mts +57 -0
  3. package/dist/_modules/collapseAttributeWhitespace.d.ts +57 -0
  4. package/dist/_modules/collapseAttributeWhitespace.js +296 -0
  5. package/dist/_modules/collapseAttributeWhitespace.mjs +293 -0
  6. package/dist/_modules/collapseBooleanAttributes.d.mts +60 -0
  7. package/dist/_modules/collapseBooleanAttributes.d.ts +60 -0
  8. package/dist/_modules/collapseBooleanAttributes.js +159 -0
  9. package/{lib/modules → dist/_modules}/collapseBooleanAttributes.mjs +39 -57
  10. package/dist/_modules/collapseWhitespace.d.mts +57 -0
  11. package/dist/_modules/collapseWhitespace.d.ts +57 -0
  12. package/dist/_modules/collapseWhitespace.js +172 -0
  13. package/dist/_modules/collapseWhitespace.mjs +170 -0
  14. package/dist/_modules/custom.d.mts +57 -0
  15. package/dist/_modules/custom.d.ts +57 -0
  16. package/dist/_modules/custom.js +22 -0
  17. package/dist/_modules/custom.mjs +20 -0
  18. package/dist/_modules/deduplicateAttributeValues.d.mts +56 -0
  19. package/dist/_modules/deduplicateAttributeValues.d.ts +56 -0
  20. package/dist/_modules/deduplicateAttributeValues.js +38 -0
  21. package/dist/_modules/deduplicateAttributeValues.mjs +36 -0
  22. package/dist/_modules/example.d.mts +59 -0
  23. package/dist/_modules/example.d.ts +59 -0
  24. package/dist/_modules/example.js +67 -0
  25. package/dist/_modules/example.mjs +65 -0
  26. package/dist/_modules/mergeScripts.d.mts +56 -0
  27. package/dist/_modules/mergeScripts.d.ts +56 -0
  28. package/dist/_modules/mergeScripts.js +53 -0
  29. package/dist/_modules/mergeScripts.mjs +51 -0
  30. package/dist/_modules/mergeStyles.d.mts +56 -0
  31. package/dist/_modules/mergeStyles.d.ts +56 -0
  32. package/dist/_modules/mergeStyles.js +42 -0
  33. package/dist/_modules/mergeStyles.mjs +40 -0
  34. package/dist/_modules/minifyConditionalComments.d.mts +56 -0
  35. package/dist/_modules/minifyConditionalComments.d.ts +56 -0
  36. package/dist/_modules/minifyConditionalComments.js +54 -0
  37. package/{lib/modules → dist/_modules}/minifyConditionalComments.mjs +21 -22
  38. package/dist/_modules/minifyCss.d.mts +56 -0
  39. package/dist/_modules/minifyCss.d.ts +56 -0
  40. package/dist/_modules/minifyCss.js +84 -0
  41. package/dist/_modules/minifyCss.mjs +82 -0
  42. package/dist/_modules/minifyJs.d.mts +56 -0
  43. package/dist/_modules/minifyJs.d.ts +56 -0
  44. package/dist/_modules/minifyJs.js +108 -0
  45. package/dist/_modules/minifyJs.mjs +106 -0
  46. package/dist/_modules/minifyJson.d.mts +56 -0
  47. package/dist/_modules/minifyJson.d.ts +56 -0
  48. package/dist/_modules/minifyJson.js +35 -0
  49. package/dist/_modules/minifyJson.mjs +33 -0
  50. package/dist/_modules/minifySvg.d.mts +56 -0
  51. package/dist/_modules/minifySvg.d.ts +56 -0
  52. package/dist/_modules/minifySvg.js +40 -0
  53. package/dist/_modules/minifySvg.mjs +38 -0
  54. package/dist/_modules/minifyUrls.d.mts +56 -0
  55. package/dist/_modules/minifyUrls.d.ts +56 -0
  56. package/dist/_modules/minifyUrls.js +180 -0
  57. package/dist/_modules/minifyUrls.mjs +178 -0
  58. package/dist/_modules/normalizeAttributeValues.d.mts +56 -0
  59. package/dist/_modules/normalizeAttributeValues.d.ts +56 -0
  60. package/dist/_modules/normalizeAttributeValues.js +234 -0
  61. package/dist/_modules/normalizeAttributeValues.mjs +232 -0
  62. package/dist/_modules/removeAttributeQuotes.d.mts +56 -0
  63. package/dist/_modules/removeAttributeQuotes.d.ts +56 -0
  64. package/dist/_modules/removeAttributeQuotes.js +15 -0
  65. package/dist/_modules/removeAttributeQuotes.mjs +13 -0
  66. package/dist/_modules/removeComments.d.mts +58 -0
  67. package/dist/_modules/removeComments.d.ts +58 -0
  68. package/dist/_modules/removeComments.js +83 -0
  69. package/{lib/modules → dist/_modules}/removeComments.mjs +24 -35
  70. package/dist/_modules/removeEmptyAttributes.d.mts +56 -0
  71. package/dist/_modules/removeEmptyAttributes.d.ts +56 -0
  72. package/dist/_modules/removeEmptyAttributes.js +197 -0
  73. package/dist/_modules/removeEmptyAttributes.mjs +195 -0
  74. package/dist/_modules/removeOptionalTags.d.mts +56 -0
  75. package/dist/_modules/removeOptionalTags.d.ts +56 -0
  76. package/dist/_modules/removeOptionalTags.js +190 -0
  77. package/{lib/modules → dist/_modules}/removeOptionalTags.mjs +54 -91
  78. package/dist/_modules/removeRedundantAttributes.d.mts +57 -0
  79. package/dist/_modules/removeRedundantAttributes.d.ts +57 -0
  80. package/dist/_modules/removeRedundantAttributes.js +128 -0
  81. package/{lib/modules → dist/_modules}/removeRedundantAttributes.mjs +43 -59
  82. package/dist/_modules/removeUnusedCss.d.mts +60 -0
  83. package/dist/_modules/removeUnusedCss.d.ts +60 -0
  84. package/dist/_modules/removeUnusedCss.js +134 -0
  85. package/dist/_modules/removeUnusedCss.mjs +132 -0
  86. package/dist/_modules/sortAttributes.d.mts +57 -0
  87. package/dist/_modules/sortAttributes.d.ts +57 -0
  88. package/dist/_modules/sortAttributes.js +102 -0
  89. package/{lib/modules → dist/_modules}/sortAttributes.mjs +39 -58
  90. package/dist/_modules/sortAttributesWithLists.d.mts +56 -0
  91. package/dist/_modules/sortAttributesWithLists.d.ts +56 -0
  92. package/dist/_modules/sortAttributesWithLists.js +118 -0
  93. package/{lib/modules → dist/_modules}/sortAttributesWithLists.mjs +41 -59
  94. package/dist/helpers.js +72 -0
  95. package/dist/helpers.mjs +63 -0
  96. package/dist/index.js +223 -0
  97. package/dist/index.mjs +209 -0
  98. package/dist/presets/ampSafe.js +19 -0
  99. package/{lib → dist}/presets/ampSafe.mjs +6 -4
  100. package/dist/presets/max.js +28 -0
  101. package/{lib → dist}/presets/max.mjs +6 -4
  102. package/dist/presets/safe.js +60 -0
  103. package/{lib → dist}/presets/safe.mjs +13 -20
  104. package/package.json +48 -56
  105. package/.eslintignore +0 -3
  106. package/CHANGELOG.md +0 -409
  107. package/docs/README.md +0 -33
  108. package/docs/babel.config.js +0 -3
  109. package/docs/docs/010-introduction.md +0 -22
  110. package/docs/docs/020-usage.md +0 -117
  111. package/docs/docs/030-config.md +0 -21
  112. package/docs/docs/040-presets.md +0 -75
  113. package/docs/docs/050-modules.md +0 -855
  114. package/docs/docs/060-contribute.md +0 -16
  115. package/docs/docusaurus.config.js +0 -65
  116. package/docs/netlify.toml +0 -4
  117. package/docs/package-lock.json +0 -21630
  118. package/docs/package.json +0 -40
  119. package/docs/sidebars.js +0 -26
  120. package/docs/versioned_docs/version-1.1.1/010-introduction.md +0 -22
  121. package/docs/versioned_docs/version-1.1.1/020-usage.md +0 -77
  122. package/docs/versioned_docs/version-1.1.1/030-config.md +0 -21
  123. package/docs/versioned_docs/version-1.1.1/040-presets.md +0 -75
  124. package/docs/versioned_docs/version-1.1.1/050-modules.md +0 -785
  125. package/docs/versioned_docs/version-1.1.1/060-contribute.md +0 -16
  126. package/docs/versioned_docs/version-2.0.0/010-introduction.md +0 -22
  127. package/docs/versioned_docs/version-2.0.0/020-usage.md +0 -77
  128. package/docs/versioned_docs/version-2.0.0/030-config.md +0 -21
  129. package/docs/versioned_docs/version-2.0.0/040-presets.md +0 -75
  130. package/docs/versioned_docs/version-2.0.0/050-modules.md +0 -838
  131. package/docs/versioned_docs/version-2.0.0/060-contribute.md +0 -16
  132. package/docs/versioned_sidebars/version-1.1.1-sidebars.json +0 -8
  133. package/docs/versioned_sidebars/version-2.0.0-sidebars.json +0 -8
  134. package/docs/versions.json +0 -4
  135. package/index.cjs +0 -11
  136. package/index.d.cts +0 -3
  137. package/index.d.mts +0 -3
  138. package/index.d.ts +0 -94
  139. package/index.mjs +0 -2
  140. package/lib/helpers.cjs +0 -79
  141. package/lib/helpers.mjs +0 -53
  142. package/lib/htmlnano.cjs +0 -202
  143. package/lib/htmlnano.mjs +0 -198
  144. package/lib/modules/collapseAttributeWhitespace.cjs +0 -86
  145. package/lib/modules/collapseAttributeWhitespace.mjs +0 -104
  146. package/lib/modules/collapseBooleanAttributes.cjs +0 -62
  147. package/lib/modules/collapseWhitespace.cjs +0 -100
  148. package/lib/modules/collapseWhitespace.mjs +0 -132
  149. package/lib/modules/custom.cjs +0 -19
  150. package/lib/modules/custom.mjs +0 -16
  151. package/lib/modules/deduplicateAttributeValues.cjs +0 -38
  152. package/lib/modules/deduplicateAttributeValues.mjs +0 -40
  153. package/lib/modules/example.cjs +0 -85
  154. package/lib/modules/example.mjs +0 -75
  155. package/lib/modules/mergeScripts.cjs +0 -54
  156. package/lib/modules/mergeScripts.mjs +0 -56
  157. package/lib/modules/mergeStyles.cjs +0 -38
  158. package/lib/modules/mergeStyles.mjs +0 -36
  159. package/lib/modules/minifyConditionalComments.cjs +0 -47
  160. package/lib/modules/minifyCss.cjs +0 -73
  161. package/lib/modules/minifyCss.mjs +0 -88
  162. package/lib/modules/minifyJs.cjs +0 -103
  163. package/lib/modules/minifyJs.mjs +0 -121
  164. package/lib/modules/minifyJson.cjs +0 -24
  165. package/lib/modules/minifyJson.mjs +0 -21
  166. package/lib/modules/minifySvg.cjs +0 -37
  167. package/lib/modules/minifySvg.mjs +0 -30
  168. package/lib/modules/minifyUrls.cjs +0 -141
  169. package/lib/modules/minifyUrls.mjs +0 -229
  170. package/lib/modules/normalizeAttributeValues.cjs +0 -120
  171. package/lib/modules/normalizeAttributeValues.mjs +0 -140
  172. package/lib/modules/removeAttributeQuotes.cjs +0 -17
  173. package/lib/modules/removeAttributeQuotes.mjs +0 -12
  174. package/lib/modules/removeComments.cjs +0 -86
  175. package/lib/modules/removeEmptyAttributes.cjs +0 -72
  176. package/lib/modules/removeEmptyAttributes.mjs +0 -121
  177. package/lib/modules/removeOptionalTags.cjs +0 -183
  178. package/lib/modules/removeRedundantAttributes.cjs +0 -112
  179. package/lib/modules/removeUnusedCss.cjs +0 -113
  180. package/lib/modules/removeUnusedCss.mjs +0 -122
  181. package/lib/modules/sortAttributes.cjs +0 -98
  182. package/lib/modules/sortAttributesWithLists.cjs +0 -114
  183. package/lib/presets/ampSafe.cjs +0 -18
  184. package/lib/presets/max.cjs +0 -27
  185. package/lib/presets/safe.cjs +0 -65
  186. package/test.js +0 -23
@@ -1,73 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = minifyCss;
7
- var _helpers = require("../helpers.cjs");
8
- const postcssOptions = {
9
- // Prevent the following warning from being shown:
10
- // > Without `from` option PostCSS could generate wrong source map and will not find Browserslist config.
11
- // > Set it to CSS file path or to `undefined` to prevent this warning.
12
- from: undefined
13
- };
14
-
15
- /** Minify CSS with cssnano */
16
- async function minifyCss(tree, options, cssnanoOptions) {
17
- const cssnano = await (0, _helpers.optionalImport)('cssnano');
18
- const postcss = await (0, _helpers.optionalImport)('postcss');
19
- if (!cssnano || !postcss) {
20
- return tree;
21
- }
22
- let promises = [];
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
- }
28
- if ((0, _helpers.isStyleNode)(node)) {
29
- promises.push(processStyleNode(node, cssnanoOptions, cssnano, postcss));
30
- } else if (node.attrs && node.attrs.style) {
31
- promises.push(processStyleAttr(node, cssnanoOptions, cssnano, postcss));
32
- }
33
- return node;
34
- });
35
- return Promise.all(promises).then(() => tree);
36
- }
37
- function processStyleNode(styleNode, cssnanoOptions, cssnano, postcss) {
38
- let css = (0, _helpers.extractCssFromStyleNode)(styleNode);
39
-
40
- // Improve performance by avoiding calling stripCdata again and again
41
- let isCdataWrapped = false;
42
- if (css.includes('CDATA')) {
43
- const strippedCss = stripCdata(css);
44
- isCdataWrapped = css !== strippedCss;
45
- css = strippedCss;
46
- }
47
- return postcss([cssnano(cssnanoOptions)]).process(css, postcssOptions).then(result => {
48
- if (isCdataWrapped) {
49
- return styleNode.content = ['<![CDATA[' + result + ']]>'];
50
- }
51
- return styleNode.content = [result.css];
52
- });
53
- }
54
- function processStyleAttr(node, cssnanoOptions, cssnano, postcss) {
55
- // CSS "color: red;" is invalid. Therefore it should be wrapped inside some selector:
56
- // a{color: red;}
57
- const wrapperStart = 'a{';
58
- const wrapperEnd = '}';
59
- const wrappedStyle = wrapperStart + (node.attrs.style || '') + wrapperEnd;
60
- return postcss([cssnano(cssnanoOptions)]).process(wrappedStyle, postcssOptions).then(result => {
61
- const minifiedCss = result.css;
62
- // Remove wrapperStart at the start and wrapperEnd at the end of minifiedCss
63
- node.attrs.style = minifiedCss.substring(wrapperStart.length, minifiedCss.length - wrapperEnd.length);
64
- });
65
- }
66
- function stripCdata(css) {
67
- const leftStrippedCss = css.replace('<![CDATA[', '');
68
- if (leftStrippedCss === css) {
69
- return css;
70
- }
71
- const strippedCss = leftStrippedCss.replace(']]>', '');
72
- return leftStrippedCss === strippedCss ? css : strippedCss;
73
- }
@@ -1,88 +0,0 @@
1
- import { isStyleNode, extractCssFromStyleNode, optionalImport } from '../helpers.mjs';
2
-
3
- const postcssOptions = {
4
- // Prevent the following warning from being shown:
5
- // > Without `from` option PostCSS could generate wrong source map and will not find Browserslist config.
6
- // > Set it to CSS file path or to `undefined` to prevent this warning.
7
- from: undefined,
8
- };
9
-
10
- /** Minify CSS with cssnano */
11
- export default async function minifyCss(tree, options, cssnanoOptions) {
12
- const cssnano = await optionalImport('cssnano');
13
- const postcss = await optionalImport('postcss');
14
-
15
- if (!cssnano || !postcss) {
16
- return tree;
17
- }
18
-
19
- let promises = [];
20
- tree.walk(node => {
21
- // Skip SRI, reasons are documented in "minifyJs" module
22
- if (node.attrs && 'integrity' in node.attrs) {
23
- return node;
24
- }
25
-
26
- if (isStyleNode(node)) {
27
- promises.push(processStyleNode(node, cssnanoOptions, cssnano, postcss));
28
- } else if (node.attrs && node.attrs.style) {
29
- promises.push(processStyleAttr(node, cssnanoOptions, cssnano, postcss));
30
- }
31
-
32
- return node;
33
- });
34
-
35
- return Promise.all(promises).then(() => tree);
36
- }
37
-
38
-
39
- function processStyleNode(styleNode, cssnanoOptions, cssnano, postcss) {
40
- let css = extractCssFromStyleNode(styleNode);
41
-
42
- // Improve performance by avoiding calling stripCdata again and again
43
- let isCdataWrapped = false;
44
- if (css.includes('CDATA')) {
45
- const strippedCss = stripCdata(css);
46
- isCdataWrapped = css !== strippedCss;
47
- css = strippedCss;
48
- }
49
-
50
- return postcss([cssnano(cssnanoOptions)])
51
- .process(css, postcssOptions)
52
- .then(result => {
53
- if (isCdataWrapped) {
54
- return styleNode.content = ['<![CDATA[' + result + ']]>'];
55
- }
56
- return styleNode.content = [result.css];
57
- });
58
- }
59
-
60
-
61
- function processStyleAttr(node, cssnanoOptions, cssnano, postcss) {
62
- // CSS "color: red;" is invalid. Therefore it should be wrapped inside some selector:
63
- // a{color: red;}
64
- const wrapperStart = 'a{';
65
- const wrapperEnd = '}';
66
- const wrappedStyle = wrapperStart + (node.attrs.style || '') + wrapperEnd;
67
-
68
- return postcss([cssnano(cssnanoOptions)])
69
- .process(wrappedStyle, postcssOptions)
70
- .then(result => {
71
- const minifiedCss = result.css;
72
- // Remove wrapperStart at the start and wrapperEnd at the end of minifiedCss
73
- node.attrs.style = minifiedCss.substring(
74
- wrapperStart.length,
75
- minifiedCss.length - wrapperEnd.length
76
- );
77
- });
78
- }
79
-
80
- function stripCdata(css) {
81
- const leftStrippedCss = css.replace('<![CDATA[', '');
82
- if (leftStrippedCss === css) {
83
- return css;
84
- }
85
-
86
- const strippedCss = leftStrippedCss.replace(']]>', '');
87
- return leftStrippedCss === strippedCss ? css : strippedCss;
88
- }
@@ -1,103 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = minifyJs;
7
- var _helpers = require("../helpers.cjs");
8
- var _removeRedundantAttributes = require("./removeRedundantAttributes.cjs");
9
- /** Minify JS with Terser */
10
- async function minifyJs(tree, options, terserOptions) {
11
- const terser = await (0, _helpers.optionalImport)('terser');
12
- if (!terser) return tree;
13
- let promises = [];
14
- tree.walk(node => {
15
- const nodeAttrs = node.attrs || {};
16
-
17
- /**
18
- * Skip SRI
19
- *
20
- * If the input <script /> has an SRI attribute, it means that the original <script /> could be trusted,
21
- * and should not be altered anymore.
22
- *
23
- * htmlnano is exactly an MITM that SRI is designed to protect from. If htmlnano or its dependencies get
24
- * compromised and introduces malicious code, then it is up to the original SRI to protect the end user.
25
- *
26
- * So htmlnano will simply skip <script /> that has SRI.
27
- * If developers do trust htmlnano, they should generate SRI after htmlnano modify the <script />.
28
- */
29
- if ('integrity' in nodeAttrs) {
30
- return node;
31
- }
32
- if (node.tag && node.tag === 'script') {
33
- const mimeType = nodeAttrs.type || 'text/javascript';
34
- if (_removeRedundantAttributes.redundantScriptTypes.has(mimeType) || mimeType === 'module') {
35
- promises.push(processScriptNode(node, terserOptions, terser));
36
- }
37
- }
38
- if (node.attrs) {
39
- promises = promises.concat(processNodeWithOnAttrs(node, terserOptions, terser));
40
- }
41
- return node;
42
- });
43
- return Promise.all(promises).then(() => tree);
44
- }
45
- function stripCdata(js) {
46
- const leftStrippedJs = js.replace(/\/\/\s*<!\[CDATA\[/, '').replace(/\/\*\s*<!\[CDATA\[\s*\*\//, '');
47
- if (leftStrippedJs === js) {
48
- return js;
49
- }
50
- const strippedJs = leftStrippedJs.replace(/\/\/\s*\]\]>/, '').replace(/\/\*\s*\]\]>\s*\*\//, '');
51
- return leftStrippedJs === strippedJs ? js : strippedJs;
52
- }
53
- function processScriptNode(scriptNode, terserOptions, terser) {
54
- let js = (scriptNode.content || []).join('').trim();
55
- if (!js) {
56
- return scriptNode;
57
- }
58
-
59
- // Improve performance by avoiding calling stripCdata again and again
60
- let isCdataWrapped = false;
61
- if (js.includes('CDATA')) {
62
- const strippedJs = stripCdata(js);
63
- isCdataWrapped = js !== strippedJs;
64
- js = strippedJs;
65
- }
66
- return terser.minify(js, terserOptions).then(result => {
67
- if (result.error) {
68
- throw new Error(result.error);
69
- }
70
- if (result.code === undefined) {
71
- return;
72
- }
73
- let content = result.code;
74
- if (isCdataWrapped) {
75
- content = '/*<![CDATA[*/' + content + '/*]]>*/';
76
- }
77
- scriptNode.content = [content];
78
- });
79
- }
80
- function processNodeWithOnAttrs(node, terserOptions, terser) {
81
- const jsWrapperStart = 'a=function(){';
82
- const jsWrapperEnd = '};a();';
83
- const promises = [];
84
- for (const attrName of Object.keys(node.attrs || {})) {
85
- if (!(0, _helpers.isEventHandler)(attrName)) {
86
- continue;
87
- }
88
-
89
- // For example onclick="return false" is valid,
90
- // but "return false;" is invalid (error: 'return' outside of function)
91
- // Therefore the attribute's code should be wrapped inside function:
92
- // "function _(){return false;}"
93
- let wrappedJs = jsWrapperStart + node.attrs[attrName] + jsWrapperEnd;
94
- let promise = terser.minify(wrappedJs, terserOptions).then(({
95
- code
96
- }) => {
97
- let minifiedJs = code.substring(jsWrapperStart.length, code.length - jsWrapperEnd.length);
98
- node.attrs[attrName] = minifiedJs;
99
- });
100
- promises.push(promise);
101
- }
102
- return promises;
103
- }
@@ -1,121 +0,0 @@
1
- import { isEventHandler, optionalImport } from '../helpers.mjs';
2
- import { redundantScriptTypes } from './removeRedundantAttributes.mjs';
3
-
4
- /** Minify JS with Terser */
5
- export default async function minifyJs (tree, options, terserOptions) {
6
- const terser = await optionalImport('terser');
7
-
8
- if (!terser) return tree;
9
-
10
- let promises = [];
11
- tree.walk(node => {
12
- const nodeAttrs = node.attrs || {};
13
-
14
- /**
15
- * Skip SRI
16
- *
17
- * If the input <script /> has an SRI attribute, it means that the original <script /> could be trusted,
18
- * and should not be altered anymore.
19
- *
20
- * htmlnano is exactly an MITM that SRI is designed to protect from. If htmlnano or its dependencies get
21
- * compromised and introduces malicious code, then it is up to the original SRI to protect the end user.
22
- *
23
- * So htmlnano will simply skip <script /> that has SRI.
24
- * If developers do trust htmlnano, they should generate SRI after htmlnano modify the <script />.
25
- */
26
- if ('integrity' in nodeAttrs) {
27
- return node;
28
- }
29
-
30
- if (node.tag && node.tag === 'script') {
31
- const mimeType = nodeAttrs.type || 'text/javascript';
32
- if (redundantScriptTypes.has(mimeType) || mimeType === 'module') {
33
- promises.push(processScriptNode(node, terserOptions, terser));
34
- }
35
- }
36
-
37
- if (node.attrs) {
38
- promises = promises.concat(processNodeWithOnAttrs(node, terserOptions, terser));
39
- }
40
-
41
- return node;
42
- });
43
-
44
- return Promise.all(promises).then(() => tree);
45
- }
46
-
47
-
48
- function stripCdata (js) {
49
- const leftStrippedJs = js.replace(/\/\/\s*<!\[CDATA\[/, '').replace(/\/\*\s*<!\[CDATA\[\s*\*\//, '');
50
- if (leftStrippedJs === js) {
51
- return js;
52
- }
53
-
54
- const strippedJs = leftStrippedJs.replace(/\/\/\s*\]\]>/, '').replace(/\/\*\s*\]\]>\s*\*\//, '');
55
- return leftStrippedJs === strippedJs ? js : strippedJs;
56
- }
57
-
58
-
59
- function processScriptNode (scriptNode, terserOptions, terser) {
60
- let js = (scriptNode.content || []).join('').trim();
61
- if (!js) {
62
- return scriptNode;
63
- }
64
-
65
- // Improve performance by avoiding calling stripCdata again and again
66
- let isCdataWrapped = false;
67
- if (js.includes('CDATA')) {
68
- const strippedJs = stripCdata(js);
69
- isCdataWrapped = js !== strippedJs;
70
- js = strippedJs;
71
- }
72
-
73
- return terser
74
- .minify(js, terserOptions)
75
- .then(result => {
76
- if (result.error) {
77
- throw new Error(result.error);
78
- }
79
- if (result.code === undefined) {
80
- return;
81
- }
82
-
83
- let content = result.code;
84
- if (isCdataWrapped) {
85
- content = '/*<![CDATA[*/' + content + '/*]]>*/';
86
- }
87
-
88
- scriptNode.content = [content];
89
- });
90
- }
91
-
92
-
93
- function processNodeWithOnAttrs (node, terserOptions, terser) {
94
- const jsWrapperStart = 'a=function(){';
95
- const jsWrapperEnd = '};a();';
96
-
97
- const promises = [];
98
- for (const attrName of Object.keys(node.attrs || {})) {
99
- if (!isEventHandler(attrName)) {
100
- continue;
101
- }
102
-
103
- // For example onclick="return false" is valid,
104
- // but "return false;" is invalid (error: 'return' outside of function)
105
- // Therefore the attribute's code should be wrapped inside function:
106
- // "function _(){return false;}"
107
- let wrappedJs = jsWrapperStart + node.attrs[attrName] + jsWrapperEnd;
108
- let promise = terser
109
- .minify(wrappedJs, terserOptions)
110
- .then(({ code }) => {
111
- let minifiedJs = code.substring(
112
- jsWrapperStart.length,
113
- code.length - jsWrapperEnd.length
114
- );
115
- node.attrs[attrName] = minifiedJs;
116
- });
117
- promises.push(promise);
118
- }
119
-
120
- return promises;
121
- }
@@ -1,24 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.onContent = onContent;
7
- const rNodeAttrsTypeJson = /(\/|\+)json/;
8
- function onContent() {
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
- }
14
- if (node.attrs && node.attrs.type && rNodeAttrsTypeJson.test(node.attrs.type)) {
15
- try {
16
- // cast minified JSON to an array
17
- return [JSON.stringify(JSON.parse((content || []).join('')))];
18
- } catch (error) {
19
- // Invalid JSON
20
- }
21
- }
22
- return content;
23
- };
24
- }
@@ -1,21 +0,0 @@
1
- const rNodeAttrsTypeJson = /(\/|\+)json/;
2
-
3
- export function onContent() {
4
- return (content, node) => {
5
- // Skip SRI, reasons are documented in "minifyJs" module
6
- if (node.attrs && 'integrity' in node.attrs) {
7
- return content;
8
- }
9
-
10
- if (node.attrs && node.attrs.type && rNodeAttrsTypeJson.test(node.attrs.type)) {
11
- try {
12
- // cast minified JSON to an array
13
- return [JSON.stringify(JSON.parse((content || []).join('')))];
14
- } catch (error) {
15
- // Invalid JSON
16
- }
17
- }
18
-
19
- return content;
20
- };
21
- }
@@ -1,37 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = minifySvg;
7
- var _helpers = require("../helpers.cjs");
8
- /** Minify SVG with SVGO */
9
- async function minifySvg(tree, options, svgoOptions = {}) {
10
- const svgo = await (0, _helpers.optionalImport)('svgo');
11
- if (!svgo) return tree;
12
- tree.match({
13
- tag: 'svg'
14
- }, node => {
15
- let svgStr = tree.render(node, {
16
- closingSingleTag: 'slash',
17
- quoteAllAttributes: true
18
- });
19
- try {
20
- const result = svgo.optimize(svgStr, svgoOptions);
21
- node.tag = false;
22
- node.attrs = {};
23
- // result.data is a string, we need to cast it to an array
24
- node.content = [result.data];
25
- return node;
26
- } catch (error) {
27
- console.error('htmlnano fails to minify the svg:');
28
- console.error(error);
29
- if (error.name === 'SvgoParserError') {
30
- console.error(error.toString());
31
- }
32
- // We return the node as-is
33
- return node;
34
- }
35
- });
36
- return tree;
37
- }
@@ -1,30 +0,0 @@
1
- import { optionalImport } from '../helpers.mjs';
2
-
3
- /** Minify SVG with SVGO */
4
- export default async function minifySvg(tree, options, svgoOptions = {}) {
5
- const svgo = await optionalImport('svgo');
6
-
7
- if (!svgo) return tree;
8
-
9
- tree.match({tag: 'svg'}, node => {
10
- let svgStr = tree.render(node, { closingSingleTag: 'slash', quoteAllAttributes: true });
11
- try {
12
- const result = svgo.optimize(svgStr, svgoOptions);
13
- node.tag = false;
14
- node.attrs = {};
15
- // result.data is a string, we need to cast it to an array
16
- node.content = [result.data];
17
- return node;
18
- } catch (error) {
19
- console.error('htmlnano fails to minify the svg:');
20
- console.error(error);
21
- if (error.name === 'SvgoParserError') {
22
- console.error(error.toString());
23
- }
24
- // We return the node as-is
25
- return node;
26
- }
27
- });
28
-
29
- return tree;
30
- }
@@ -1,141 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = minifyUrls;
7
- var _helpers = require("../helpers.cjs");
8
- // Adopts from https://github.com/kangax/html-minifier/blob/51ce10f4daedb1de483ffbcccecc41be1c873da2/src/htmlminifier.js#L209-L221
9
- const tagsHaveUriValuesForAttributes = new Set(['a', 'area', 'link', 'base', 'object', 'blockquote', 'q', 'del', 'ins', 'form', 'input', 'head', 'audio', 'embed', 'iframe', 'img', 'script', 'track', 'video']);
10
- const tagsHasHrefAttributes = new Set(['a', 'area', 'link', 'base']);
11
- const attributesOfImgTagHasUriValues = new Set(['src', 'longdesc', 'usemap']);
12
- const attributesOfObjectTagHasUriValues = new Set(['classid', 'codebase', 'data', 'usemap']);
13
- const tagsHasCiteAttributes = new Set(['blockquote', 'q', 'ins', 'del']);
14
- const tagsHasSrcAttributes = new Set(['audio', 'embed', 'iframe', 'img', 'input', 'script', 'track', 'video',
15
- /**
16
- * https://html.spec.whatwg.org/#attr-source-src
17
- *
18
- * Although most of browsers recommend not to use "src" in <source>,
19
- * but technically it does comply with HTML Standard.
20
- */
21
- 'source']);
22
- const isUriTypeAttribute = (tag, attr) => {
23
- return tagsHasHrefAttributes.has(tag) && attr === 'href' || tag === 'img' && attributesOfImgTagHasUriValues.has(attr) || tag === 'object' && attributesOfObjectTagHasUriValues.has(attr) || tagsHasCiteAttributes.has(tag) && attr === 'cite' || tag === 'form' && attr === 'action' || tag === 'input' && attr === 'usemap' || tag === 'head' && attr === 'profile' || tag === 'script' && attr === 'for' || tagsHasSrcAttributes.has(tag) && attr === 'src';
24
- };
25
- const isSrcsetAttribute = (tag, attr) => {
26
- return tag === 'source' && attr === 'srcset' || tag === 'img' && attr === 'srcset' || tag === 'link' && attr === 'imagesrcset';
27
- };
28
- const processModuleOptions = options => {
29
- // FIXME!
30
- // relateurl@1.0.0-alpha only supports URL while stable version (0.2.7) only supports string
31
- // should convert input into URL instance after relateurl@1 is stable
32
- if (typeof options === 'string') return options;
33
- if (options instanceof URL) return options.toString();
34
- return false;
35
- };
36
- const isLinkRelCanonical = ({
37
- tag,
38
- attrs
39
- }) => {
40
- // Return false early for non-"link" tag
41
- if (tag !== 'link') return false;
42
- for (const [attrName, attrValue] of Object.entries(attrs)) {
43
- if (attrName.toLowerCase() === 'rel' && attrValue === 'canonical') return true;
44
- }
45
- return false;
46
- };
47
- const JAVASCRIPT_URL_PROTOCOL = 'javascript:';
48
- let relateUrlInstance;
49
- let STORED_URL_BASE;
50
-
51
- /** Convert absolute url into relative url */
52
- async function minifyUrls(tree, options, moduleOptions) {
53
- const RelateUrl = await (0, _helpers.optionalImport)('relateurl');
54
- const srcset = await (0, _helpers.optionalImport)('srcset');
55
- const terser = await (0, _helpers.optionalImport)('terser');
56
- let promises = [];
57
- const urlBase = processModuleOptions(moduleOptions);
58
-
59
- // Invalid configuration, return tree directly
60
- if (!urlBase) return tree;
61
-
62
- /** Bring up a reusable RelateUrl instances (only once)
63
- *
64
- * STORED_URL_BASE is used to invalidate RelateUrl instances,
65
- * avoiding require.cache acrossing multiple htmlnano instance with different configuration,
66
- * e.g. unit tests cases.
67
- */
68
- if (!relateUrlInstance || STORED_URL_BASE !== urlBase) {
69
- if (RelateUrl) {
70
- relateUrlInstance = new RelateUrl(urlBase);
71
- }
72
- STORED_URL_BASE = urlBase;
73
- }
74
- tree.walk(node => {
75
- if (!node.attrs) return node;
76
- if (!node.tag) return node;
77
- if (!tagsHaveUriValuesForAttributes.has(node.tag)) return node;
78
-
79
- // Prevent link[rel=canonical] being processed
80
- // Can't be excluded by isUriTypeAttribute()
81
- if (isLinkRelCanonical(node)) return node;
82
- for (const [attrName, attrValue] of Object.entries(node.attrs)) {
83
- const attrNameLower = attrName.toLowerCase();
84
- if (isUriTypeAttribute(node.tag, attrNameLower)) {
85
- if (isJavaScriptUrl(attrValue)) {
86
- promises.push(minifyJavaScriptUrl(node, attrName, terser));
87
- } else {
88
- if (relateUrlInstance) {
89
- // FIXME!
90
- // relateurl@1.0.0-alpha only supports URL while stable version (0.2.7) only supports string
91
- // the WHATWG URL API is very strict while attrValue might not be a valid URL
92
- // new URL should be used, and relateUrl#relate should be wrapped in try...catch after relateurl@1 is stable
93
- node.attrs[attrName] = relateUrlInstance.relate(attrValue);
94
- }
95
- }
96
- continue;
97
- }
98
- if (isSrcsetAttribute(node.tag, attrNameLower)) {
99
- if (srcset) {
100
- try {
101
- const parsedSrcset = srcset.parseSrcset(attrValue, {
102
- strict: true
103
- });
104
- node.attrs[attrName] = srcset.stringifySrcset(parsedSrcset.map(item => {
105
- if (relateUrlInstance) {
106
- item.url = relateUrlInstance.relate(item.url);
107
- }
108
- return item;
109
- }));
110
- } catch (e) {
111
- // srcset will throw an Error for invalid srcset.
112
- }
113
- }
114
- continue;
115
- }
116
- }
117
- return node;
118
- });
119
- if (promises.length > 0) return Promise.all(promises).then(() => tree);
120
- return Promise.resolve(tree);
121
- }
122
- function isJavaScriptUrl(url) {
123
- return typeof url === 'string' && url.toLowerCase().startsWith(JAVASCRIPT_URL_PROTOCOL);
124
- }
125
- const jsWrapperStart = 'function a(){';
126
- const jsWrapperEnd = '}a();';
127
- function minifyJavaScriptUrl(node, attrName, terser) {
128
- if (!terser) return Promise.resolve();
129
- let result = node.attrs[attrName];
130
- if (result) {
131
- result = jsWrapperStart + result.slice(JAVASCRIPT_URL_PROTOCOL.length) + jsWrapperEnd;
132
- return terser.minify(result, {}) // Default Option is good enough
133
- .then(({
134
- code
135
- }) => {
136
- const minifiedJs = code.substring(jsWrapperStart.length, code.length - jsWrapperEnd.length);
137
- node.attrs[attrName] = JAVASCRIPT_URL_PROTOCOL + minifiedJs;
138
- });
139
- }
140
- return Promise.resolve();
141
- }