htmlnano 2.1.2 → 2.1.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.
Files changed (191) hide show
  1. package/README.md +54 -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.d.ts +16 -0
  95. package/dist/helpers.js +72 -0
  96. package/dist/helpers.mjs +63 -0
  97. package/dist/index.d.ts +79 -0
  98. package/dist/index.js +223 -0
  99. package/dist/index.mjs +209 -0
  100. package/dist/presets/ampSafe.d.ts +47 -0
  101. package/dist/presets/ampSafe.js +19 -0
  102. package/{lib → dist}/presets/ampSafe.mjs +6 -4
  103. package/dist/presets/max.d.ts +47 -0
  104. package/dist/presets/max.js +28 -0
  105. package/{lib → dist}/presets/max.mjs +6 -4
  106. package/dist/presets/safe.d.ts +47 -0
  107. package/dist/presets/safe.js +60 -0
  108. package/{lib → dist}/presets/safe.mjs +13 -20
  109. package/package.json +53 -56
  110. package/.eslintignore +0 -3
  111. package/CHANGELOG.md +0 -409
  112. package/docs/README.md +0 -33
  113. package/docs/babel.config.js +0 -3
  114. package/docs/docs/010-introduction.md +0 -22
  115. package/docs/docs/020-usage.md +0 -117
  116. package/docs/docs/030-config.md +0 -21
  117. package/docs/docs/040-presets.md +0 -75
  118. package/docs/docs/050-modules.md +0 -855
  119. package/docs/docs/060-contribute.md +0 -16
  120. package/docs/docusaurus.config.js +0 -65
  121. package/docs/netlify.toml +0 -4
  122. package/docs/package-lock.json +0 -21630
  123. package/docs/package.json +0 -40
  124. package/docs/sidebars.js +0 -26
  125. package/docs/versioned_docs/version-1.1.1/010-introduction.md +0 -22
  126. package/docs/versioned_docs/version-1.1.1/020-usage.md +0 -77
  127. package/docs/versioned_docs/version-1.1.1/030-config.md +0 -21
  128. package/docs/versioned_docs/version-1.1.1/040-presets.md +0 -75
  129. package/docs/versioned_docs/version-1.1.1/050-modules.md +0 -785
  130. package/docs/versioned_docs/version-1.1.1/060-contribute.md +0 -16
  131. package/docs/versioned_docs/version-2.0.0/010-introduction.md +0 -22
  132. package/docs/versioned_docs/version-2.0.0/020-usage.md +0 -77
  133. package/docs/versioned_docs/version-2.0.0/030-config.md +0 -21
  134. package/docs/versioned_docs/version-2.0.0/040-presets.md +0 -75
  135. package/docs/versioned_docs/version-2.0.0/050-modules.md +0 -838
  136. package/docs/versioned_docs/version-2.0.0/060-contribute.md +0 -16
  137. package/docs/versioned_sidebars/version-1.1.1-sidebars.json +0 -8
  138. package/docs/versioned_sidebars/version-2.0.0-sidebars.json +0 -8
  139. package/docs/versions.json +0 -4
  140. package/index.cjs +0 -11
  141. package/index.d.cts +0 -3
  142. package/index.d.mts +0 -3
  143. package/index.d.ts +0 -94
  144. package/index.mjs +0 -2
  145. package/lib/helpers.cjs +0 -79
  146. package/lib/helpers.mjs +0 -53
  147. package/lib/htmlnano.cjs +0 -202
  148. package/lib/htmlnano.mjs +0 -198
  149. package/lib/modules/collapseAttributeWhitespace.cjs +0 -86
  150. package/lib/modules/collapseAttributeWhitespace.mjs +0 -104
  151. package/lib/modules/collapseBooleanAttributes.cjs +0 -62
  152. package/lib/modules/collapseWhitespace.cjs +0 -100
  153. package/lib/modules/collapseWhitespace.mjs +0 -132
  154. package/lib/modules/custom.cjs +0 -19
  155. package/lib/modules/custom.mjs +0 -16
  156. package/lib/modules/deduplicateAttributeValues.cjs +0 -38
  157. package/lib/modules/deduplicateAttributeValues.mjs +0 -40
  158. package/lib/modules/example.cjs +0 -85
  159. package/lib/modules/example.mjs +0 -75
  160. package/lib/modules/mergeScripts.cjs +0 -54
  161. package/lib/modules/mergeScripts.mjs +0 -56
  162. package/lib/modules/mergeStyles.cjs +0 -38
  163. package/lib/modules/mergeStyles.mjs +0 -36
  164. package/lib/modules/minifyConditionalComments.cjs +0 -47
  165. package/lib/modules/minifyCss.cjs +0 -73
  166. package/lib/modules/minifyCss.mjs +0 -88
  167. package/lib/modules/minifyJs.cjs +0 -103
  168. package/lib/modules/minifyJs.mjs +0 -121
  169. package/lib/modules/minifyJson.cjs +0 -24
  170. package/lib/modules/minifyJson.mjs +0 -21
  171. package/lib/modules/minifySvg.cjs +0 -37
  172. package/lib/modules/minifySvg.mjs +0 -30
  173. package/lib/modules/minifyUrls.cjs +0 -141
  174. package/lib/modules/minifyUrls.mjs +0 -229
  175. package/lib/modules/normalizeAttributeValues.cjs +0 -120
  176. package/lib/modules/normalizeAttributeValues.mjs +0 -140
  177. package/lib/modules/removeAttributeQuotes.cjs +0 -17
  178. package/lib/modules/removeAttributeQuotes.mjs +0 -12
  179. package/lib/modules/removeComments.cjs +0 -86
  180. package/lib/modules/removeEmptyAttributes.cjs +0 -72
  181. package/lib/modules/removeEmptyAttributes.mjs +0 -121
  182. package/lib/modules/removeOptionalTags.cjs +0 -183
  183. package/lib/modules/removeRedundantAttributes.cjs +0 -112
  184. package/lib/modules/removeUnusedCss.cjs +0 -113
  185. package/lib/modules/removeUnusedCss.mjs +0 -122
  186. package/lib/modules/sortAttributes.cjs +0 -98
  187. package/lib/modules/sortAttributesWithLists.cjs +0 -114
  188. package/lib/presets/ampSafe.cjs +0 -18
  189. package/lib/presets/max.cjs +0 -27
  190. package/lib/presets/safe.cjs +0 -65
  191. package/test.js +0 -23
@@ -0,0 +1,56 @@
1
+ import PostHTML from 'posthtml';
2
+ import { MinifyOptions } from 'terser';
3
+ import { Options } from 'cssnano';
4
+ import { Config } from 'svgo';
5
+
6
+ type PostHTMLTreeLike = [PostHTML.Node] & PostHTML.NodeAPI & {
7
+ options?: {
8
+ quoteAllAttributes?: boolean | undefined;
9
+ } | undefined;
10
+ render(): string;
11
+ render(node: PostHTML.Node | PostHTMLTreeLike, renderOptions?: any): string;
12
+ };
13
+ type MaybeArray<T> = T | Array<T>;
14
+ type PostHTMLNodeLike = PostHTML.Node | string;
15
+ interface HtmlnanoOptions {
16
+ skipConfigLoading?: boolean;
17
+ skipInternalWarnings?: boolean;
18
+ collapseAttributeWhitespace?: boolean;
19
+ collapseBooleanAttributes?: {
20
+ amphtml?: boolean;
21
+ };
22
+ collapseWhitespace?: 'conservative' | 'all' | 'aggressive';
23
+ custom?: MaybeArray<(tree: PostHTMLTreeLike, options?: any) => (PostHTML.Node | PostHTMLTreeLike)>;
24
+ deduplicateAttributeValues?: boolean;
25
+ minifyUrls?: URL | string | false;
26
+ mergeStyles?: boolean;
27
+ mergeScripts?: boolean;
28
+ minifyCss?: Options | boolean;
29
+ minifyConditionalComments?: boolean;
30
+ minifyJs?: MinifyOptions | boolean;
31
+ minifyJson?: boolean;
32
+ minifySvg?: Config | boolean;
33
+ normalizeAttributeValues?: boolean;
34
+ removeAttributeQuotes?: boolean;
35
+ removeComments?: boolean | 'safe' | 'all' | RegExp | ((comment: string) => boolean);
36
+ removeEmptyAttributes?: boolean;
37
+ removeRedundantAttributes?: boolean;
38
+ removeOptionalTags?: boolean;
39
+ removeUnusedCss?: boolean;
40
+ sortAttributes?: boolean | 'alphabetical' | 'frequency';
41
+ sortAttributesWithLists?: boolean | 'alphabetical' | 'frequency';
42
+ }
43
+ type HtmlnanoModuleAttrsHandler = (attrs: Record<string, string | boolean | void>, node: PostHTML.Node) => Record<string, string | boolean | void>;
44
+ type HtmlnanoModuleContentHandler = (content: Array<PostHTMLNodeLike>, node: PostHTML.Node) => MaybeArray<PostHTMLNodeLike>;
45
+ type HtmlnanoModuleNodeHandler = (node: PostHTMLNodeLike) => PostHTML.Node | string;
46
+ type OptionalOptions<T> = T extends boolean | string | Function | number | null | undefined ? T : T extends object ? Partial<T> : T;
47
+ type HtmlnanoModule<Options = any> = {
48
+ onAttrs?: (options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => HtmlnanoModuleAttrsHandler;
49
+ onContent?: (options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => HtmlnanoModuleContentHandler;
50
+ onNode?: (options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => HtmlnanoModuleNodeHandler;
51
+ default?: (tree: PostHTMLTreeLike, options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => PostHTMLTreeLike | Promise<PostHTMLTreeLike>;
52
+ };
53
+
54
+ declare const mod: HtmlnanoModule<Config>;
55
+
56
+ export { mod as default };
@@ -0,0 +1,56 @@
1
+ import PostHTML from 'posthtml';
2
+ import { MinifyOptions } from 'terser';
3
+ import { Options } from 'cssnano';
4
+ import { Config } from 'svgo';
5
+
6
+ type PostHTMLTreeLike = [PostHTML.Node] & PostHTML.NodeAPI & {
7
+ options?: {
8
+ quoteAllAttributes?: boolean | undefined;
9
+ } | undefined;
10
+ render(): string;
11
+ render(node: PostHTML.Node | PostHTMLTreeLike, renderOptions?: any): string;
12
+ };
13
+ type MaybeArray<T> = T | Array<T>;
14
+ type PostHTMLNodeLike = PostHTML.Node | string;
15
+ interface HtmlnanoOptions {
16
+ skipConfigLoading?: boolean;
17
+ skipInternalWarnings?: boolean;
18
+ collapseAttributeWhitespace?: boolean;
19
+ collapseBooleanAttributes?: {
20
+ amphtml?: boolean;
21
+ };
22
+ collapseWhitespace?: 'conservative' | 'all' | 'aggressive';
23
+ custom?: MaybeArray<(tree: PostHTMLTreeLike, options?: any) => (PostHTML.Node | PostHTMLTreeLike)>;
24
+ deduplicateAttributeValues?: boolean;
25
+ minifyUrls?: URL | string | false;
26
+ mergeStyles?: boolean;
27
+ mergeScripts?: boolean;
28
+ minifyCss?: Options | boolean;
29
+ minifyConditionalComments?: boolean;
30
+ minifyJs?: MinifyOptions | boolean;
31
+ minifyJson?: boolean;
32
+ minifySvg?: Config | boolean;
33
+ normalizeAttributeValues?: boolean;
34
+ removeAttributeQuotes?: boolean;
35
+ removeComments?: boolean | 'safe' | 'all' | RegExp | ((comment: string) => boolean);
36
+ removeEmptyAttributes?: boolean;
37
+ removeRedundantAttributes?: boolean;
38
+ removeOptionalTags?: boolean;
39
+ removeUnusedCss?: boolean;
40
+ sortAttributes?: boolean | 'alphabetical' | 'frequency';
41
+ sortAttributesWithLists?: boolean | 'alphabetical' | 'frequency';
42
+ }
43
+ type HtmlnanoModuleAttrsHandler = (attrs: Record<string, string | boolean | void>, node: PostHTML.Node) => Record<string, string | boolean | void>;
44
+ type HtmlnanoModuleContentHandler = (content: Array<PostHTMLNodeLike>, node: PostHTML.Node) => MaybeArray<PostHTMLNodeLike>;
45
+ type HtmlnanoModuleNodeHandler = (node: PostHTMLNodeLike) => PostHTML.Node | string;
46
+ type OptionalOptions<T> = T extends boolean | string | Function | number | null | undefined ? T : T extends object ? Partial<T> : T;
47
+ type HtmlnanoModule<Options = any> = {
48
+ onAttrs?: (options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => HtmlnanoModuleAttrsHandler;
49
+ onContent?: (options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => HtmlnanoModuleContentHandler;
50
+ onNode?: (options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => HtmlnanoModuleNodeHandler;
51
+ default?: (tree: PostHTMLTreeLike, options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => PostHTMLTreeLike | Promise<PostHTMLTreeLike>;
52
+ };
53
+
54
+ declare const mod: HtmlnanoModule<Config>;
55
+
56
+ export { mod as default };
@@ -0,0 +1,40 @@
1
+ Object.defineProperty(exports, '__esModule', { value: true });
2
+
3
+ var helpers_js = require('../helpers.js');
4
+
5
+ /** Minify SVG with SVGO */ const mod = {
6
+ async default (tree, options, svgoOptions) {
7
+ const svgo = await helpers_js.optionalImport('svgo');
8
+ if (!svgo) return tree;
9
+ tree.match({
10
+ tag: 'svg'
11
+ }, (node)=>{
12
+ const svgStr = tree.render(node, {
13
+ closingSingleTag: 'slash',
14
+ quoteAllAttributes: true
15
+ });
16
+ try {
17
+ const result = svgo.optimize(svgStr, svgoOptions);
18
+ // @ts-expect-error -- remove this node
19
+ node.tag = false;
20
+ node.attrs = {};
21
+ // result.data is a string, we need to cast it to an array
22
+ node.content = [
23
+ result.data
24
+ ];
25
+ return node;
26
+ } catch (error) {
27
+ console.error('htmlnano fails to minify the svg:');
28
+ console.error(error);
29
+ if (error && typeof error === 'object' && 'name' in error && error.name === 'SvgoParserError') {
30
+ console.error(error);
31
+ }
32
+ // We return the node as-is
33
+ return node;
34
+ }
35
+ });
36
+ return tree;
37
+ }
38
+ };
39
+
40
+ exports.default = mod;
@@ -0,0 +1,38 @@
1
+ import { optionalImport } from '../helpers.mjs';
2
+
3
+ /** Minify SVG with SVGO */ const mod = {
4
+ async default (tree, options, svgoOptions) {
5
+ const svgo = await optionalImport('svgo');
6
+ if (!svgo) return tree;
7
+ tree.match({
8
+ tag: 'svg'
9
+ }, (node)=>{
10
+ const svgStr = tree.render(node, {
11
+ closingSingleTag: 'slash',
12
+ quoteAllAttributes: true
13
+ });
14
+ try {
15
+ const result = svgo.optimize(svgStr, svgoOptions);
16
+ // @ts-expect-error -- remove this node
17
+ node.tag = false;
18
+ node.attrs = {};
19
+ // result.data is a string, we need to cast it to an array
20
+ node.content = [
21
+ result.data
22
+ ];
23
+ return node;
24
+ } catch (error) {
25
+ console.error('htmlnano fails to minify the svg:');
26
+ console.error(error);
27
+ if (error && typeof error === 'object' && 'name' in error && error.name === 'SvgoParserError') {
28
+ console.error(error);
29
+ }
30
+ // We return the node as-is
31
+ return node;
32
+ }
33
+ });
34
+ return tree;
35
+ }
36
+ };
37
+
38
+ export { mod as default };
@@ -0,0 +1,56 @@
1
+ import PostHTML from 'posthtml';
2
+ import { MinifyOptions } from 'terser';
3
+ import { Options } from 'cssnano';
4
+ import { Config } from 'svgo';
5
+
6
+ type PostHTMLTreeLike = [PostHTML.Node] & PostHTML.NodeAPI & {
7
+ options?: {
8
+ quoteAllAttributes?: boolean | undefined;
9
+ } | undefined;
10
+ render(): string;
11
+ render(node: PostHTML.Node | PostHTMLTreeLike, renderOptions?: any): string;
12
+ };
13
+ type MaybeArray<T> = T | Array<T>;
14
+ type PostHTMLNodeLike = PostHTML.Node | string;
15
+ interface HtmlnanoOptions {
16
+ skipConfigLoading?: boolean;
17
+ skipInternalWarnings?: boolean;
18
+ collapseAttributeWhitespace?: boolean;
19
+ collapseBooleanAttributes?: {
20
+ amphtml?: boolean;
21
+ };
22
+ collapseWhitespace?: 'conservative' | 'all' | 'aggressive';
23
+ custom?: MaybeArray<(tree: PostHTMLTreeLike, options?: any) => (PostHTML.Node | PostHTMLTreeLike)>;
24
+ deduplicateAttributeValues?: boolean;
25
+ minifyUrls?: URL | string | false;
26
+ mergeStyles?: boolean;
27
+ mergeScripts?: boolean;
28
+ minifyCss?: Options | boolean;
29
+ minifyConditionalComments?: boolean;
30
+ minifyJs?: MinifyOptions | boolean;
31
+ minifyJson?: boolean;
32
+ minifySvg?: Config | boolean;
33
+ normalizeAttributeValues?: boolean;
34
+ removeAttributeQuotes?: boolean;
35
+ removeComments?: boolean | 'safe' | 'all' | RegExp | ((comment: string) => boolean);
36
+ removeEmptyAttributes?: boolean;
37
+ removeRedundantAttributes?: boolean;
38
+ removeOptionalTags?: boolean;
39
+ removeUnusedCss?: boolean;
40
+ sortAttributes?: boolean | 'alphabetical' | 'frequency';
41
+ sortAttributesWithLists?: boolean | 'alphabetical' | 'frequency';
42
+ }
43
+ type HtmlnanoModuleAttrsHandler = (attrs: Record<string, string | boolean | void>, node: PostHTML.Node) => Record<string, string | boolean | void>;
44
+ type HtmlnanoModuleContentHandler = (content: Array<PostHTMLNodeLike>, node: PostHTML.Node) => MaybeArray<PostHTMLNodeLike>;
45
+ type HtmlnanoModuleNodeHandler = (node: PostHTMLNodeLike) => PostHTML.Node | string;
46
+ type OptionalOptions<T> = T extends boolean | string | Function | number | null | undefined ? T : T extends object ? Partial<T> : T;
47
+ type HtmlnanoModule<Options = any> = {
48
+ onAttrs?: (options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => HtmlnanoModuleAttrsHandler;
49
+ onContent?: (options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => HtmlnanoModuleContentHandler;
50
+ onNode?: (options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => HtmlnanoModuleNodeHandler;
51
+ default?: (tree: PostHTMLTreeLike, options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => PostHTMLTreeLike | Promise<PostHTMLTreeLike>;
52
+ };
53
+
54
+ declare const mod: HtmlnanoModule;
55
+
56
+ export { mod as default };
@@ -0,0 +1,56 @@
1
+ import PostHTML from 'posthtml';
2
+ import { MinifyOptions } from 'terser';
3
+ import { Options } from 'cssnano';
4
+ import { Config } from 'svgo';
5
+
6
+ type PostHTMLTreeLike = [PostHTML.Node] & PostHTML.NodeAPI & {
7
+ options?: {
8
+ quoteAllAttributes?: boolean | undefined;
9
+ } | undefined;
10
+ render(): string;
11
+ render(node: PostHTML.Node | PostHTMLTreeLike, renderOptions?: any): string;
12
+ };
13
+ type MaybeArray<T> = T | Array<T>;
14
+ type PostHTMLNodeLike = PostHTML.Node | string;
15
+ interface HtmlnanoOptions {
16
+ skipConfigLoading?: boolean;
17
+ skipInternalWarnings?: boolean;
18
+ collapseAttributeWhitespace?: boolean;
19
+ collapseBooleanAttributes?: {
20
+ amphtml?: boolean;
21
+ };
22
+ collapseWhitespace?: 'conservative' | 'all' | 'aggressive';
23
+ custom?: MaybeArray<(tree: PostHTMLTreeLike, options?: any) => (PostHTML.Node | PostHTMLTreeLike)>;
24
+ deduplicateAttributeValues?: boolean;
25
+ minifyUrls?: URL | string | false;
26
+ mergeStyles?: boolean;
27
+ mergeScripts?: boolean;
28
+ minifyCss?: Options | boolean;
29
+ minifyConditionalComments?: boolean;
30
+ minifyJs?: MinifyOptions | boolean;
31
+ minifyJson?: boolean;
32
+ minifySvg?: Config | boolean;
33
+ normalizeAttributeValues?: boolean;
34
+ removeAttributeQuotes?: boolean;
35
+ removeComments?: boolean | 'safe' | 'all' | RegExp | ((comment: string) => boolean);
36
+ removeEmptyAttributes?: boolean;
37
+ removeRedundantAttributes?: boolean;
38
+ removeOptionalTags?: boolean;
39
+ removeUnusedCss?: boolean;
40
+ sortAttributes?: boolean | 'alphabetical' | 'frequency';
41
+ sortAttributesWithLists?: boolean | 'alphabetical' | 'frequency';
42
+ }
43
+ type HtmlnanoModuleAttrsHandler = (attrs: Record<string, string | boolean | void>, node: PostHTML.Node) => Record<string, string | boolean | void>;
44
+ type HtmlnanoModuleContentHandler = (content: Array<PostHTMLNodeLike>, node: PostHTML.Node) => MaybeArray<PostHTMLNodeLike>;
45
+ type HtmlnanoModuleNodeHandler = (node: PostHTMLNodeLike) => PostHTML.Node | string;
46
+ type OptionalOptions<T> = T extends boolean | string | Function | number | null | undefined ? T : T extends object ? Partial<T> : T;
47
+ type HtmlnanoModule<Options = any> = {
48
+ onAttrs?: (options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => HtmlnanoModuleAttrsHandler;
49
+ onContent?: (options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => HtmlnanoModuleContentHandler;
50
+ onNode?: (options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => HtmlnanoModuleNodeHandler;
51
+ default?: (tree: PostHTMLTreeLike, options: Partial<HtmlnanoOptions>, moduleOptions: OptionalOptions<Options>) => PostHTMLTreeLike | Promise<PostHTMLTreeLike>;
52
+ };
53
+
54
+ declare const mod: HtmlnanoModule;
55
+
56
+ export { mod as default };
@@ -0,0 +1,180 @@
1
+ Object.defineProperty(exports, '__esModule', { value: true });
2
+
3
+ var helpers_js = require('../helpers.js');
4
+
5
+ // Adopts from https://github.com/kangax/html-minifier/blob/51ce10f4daedb1de483ffbcccecc41be1c873da2/src/htmlminifier.js#L209-L221
6
+ const tagsHaveUriValuesForAttributes = new Set([
7
+ 'a',
8
+ 'area',
9
+ 'link',
10
+ 'base',
11
+ 'object',
12
+ 'blockquote',
13
+ 'q',
14
+ 'del',
15
+ 'ins',
16
+ 'form',
17
+ 'input',
18
+ 'head',
19
+ 'audio',
20
+ 'embed',
21
+ 'iframe',
22
+ 'img',
23
+ 'script',
24
+ 'track',
25
+ 'video'
26
+ ]);
27
+ const tagsHasHrefAttributes = new Set([
28
+ 'a',
29
+ 'area',
30
+ 'link',
31
+ 'base'
32
+ ]);
33
+ const attributesOfImgTagHasUriValues = new Set([
34
+ 'src',
35
+ 'longdesc',
36
+ 'usemap'
37
+ ]);
38
+ const attributesOfObjectTagHasUriValues = new Set([
39
+ 'classid',
40
+ 'codebase',
41
+ 'data',
42
+ 'usemap'
43
+ ]);
44
+ const tagsHasCiteAttributes = new Set([
45
+ 'blockquote',
46
+ 'q',
47
+ 'ins',
48
+ 'del'
49
+ ]);
50
+ const tagsHasSrcAttributes = new Set([
51
+ 'audio',
52
+ 'embed',
53
+ 'iframe',
54
+ 'img',
55
+ 'input',
56
+ 'script',
57
+ 'track',
58
+ 'video',
59
+ /**
60
+ * https://html.spec.whatwg.org/#attr-source-src
61
+ *
62
+ * Although most of browsers recommend not to use "src" in <source>,
63
+ * but technically it does comply with HTML Standard.
64
+ */ 'source'
65
+ ]);
66
+ const isUriTypeAttribute = (tag, attr)=>{
67
+ 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';
68
+ };
69
+ const isSrcsetAttribute = (tag, attr)=>{
70
+ return tag === 'source' && attr === 'srcset' || tag === 'img' && attr === 'srcset' || tag === 'link' && attr === 'imagesrcset';
71
+ };
72
+ const processModuleOptions = (options)=>{
73
+ // FIXME!
74
+ // relateurl@1.0.0-alpha only supports URL while stable version (0.2.7) only supports string
75
+ // should convert input into URL instance after relateurl@1 is stable
76
+ if (typeof options === 'string') return options;
77
+ if (options instanceof URL) return options.toString();
78
+ return false;
79
+ };
80
+ const isLinkRelCanonical = ({ tag, attrs })=>{
81
+ // Return false early for non-"link" tag
82
+ if (tag !== 'link' || !attrs) return false;
83
+ for (const [attrName, attrValue] of Object.entries(attrs)){
84
+ if (attrName.toLowerCase() === 'rel' && attrValue === 'canonical') return true;
85
+ }
86
+ return false;
87
+ };
88
+ const JAVASCRIPT_URL_PROTOCOL = 'javascript:';
89
+ let relateUrlInstance;
90
+ let STORED_URL_BASE;
91
+ /** Convert absolute url into relative url */ const mod = {
92
+ async default (tree, options, moduleOptions) {
93
+ const RelateUrl = await helpers_js.optionalImport('relateurl');
94
+ const srcset = await helpers_js.optionalImport('srcset');
95
+ const terser = await helpers_js.optionalImport('terser');
96
+ const promises = [];
97
+ const urlBase = processModuleOptions(moduleOptions);
98
+ // Invalid configuration, return tree directly
99
+ if (!urlBase) return tree;
100
+ /** Bring up a reusable RelateUrl instances (only once)
101
+ *
102
+ * STORED_URL_BASE is used to invalidate RelateUrl instances,
103
+ * avoiding require.cache acrossing multiple htmlnano instance with different configuration,
104
+ * e.g. unit tests cases.
105
+ */ if (!relateUrlInstance || STORED_URL_BASE !== urlBase) {
106
+ if (RelateUrl) {
107
+ relateUrlInstance = new RelateUrl(urlBase);
108
+ }
109
+ STORED_URL_BASE = urlBase;
110
+ }
111
+ tree.walk((node)=>{
112
+ if (!node.attrs) return node;
113
+ if (!node.tag) return node;
114
+ if (!tagsHaveUriValuesForAttributes.has(node.tag)) return node;
115
+ // Prevent link[rel=canonical] being processed
116
+ // Can't be excluded by isUriTypeAttribute()
117
+ if (isLinkRelCanonical(node)) return node;
118
+ for (const [attrName, attrValue] of Object.entries(node.attrs)){
119
+ const attrNameLower = attrName.toLowerCase();
120
+ if (isUriTypeAttribute(node.tag, attrNameLower)) {
121
+ if (isJavaScriptUrl(attrValue)) {
122
+ promises.push(minifyJavaScriptUrl(node, attrName, terser));
123
+ } else {
124
+ if (relateUrlInstance && attrValue) {
125
+ // FIXME!
126
+ // relateurl@1.0.0-alpha only supports URL while stable version (0.2.7) only supports string
127
+ // the WHATWG URL API is very strict while attrValue might not be a valid URL
128
+ // new URL should be used, and relateUrl#relate should be wrapped in try...catch after relateurl@1 is stable
129
+ node.attrs[attrName] = relateUrlInstance.relate(attrValue);
130
+ }
131
+ }
132
+ continue;
133
+ }
134
+ if (isSrcsetAttribute(node.tag, attrNameLower)) {
135
+ if (srcset && attrValue) {
136
+ try {
137
+ const parsedSrcset = srcset.parseSrcset(attrValue, {
138
+ strict: true
139
+ });
140
+ node.attrs[attrName] = srcset.stringifySrcset(parsedSrcset.map((item)=>{
141
+ if (relateUrlInstance) {
142
+ // @ts-expect-error -- not actually readonly
143
+ item.url = relateUrlInstance.relate(item.url);
144
+ }
145
+ return item;
146
+ }));
147
+ } catch (e) {
148
+ // srcset will throw an Error for invalid srcset.
149
+ }
150
+ }
151
+ continue;
152
+ }
153
+ }
154
+ return node;
155
+ });
156
+ if (promises.length > 0) return Promise.all(promises).then(()=>tree);
157
+ return Promise.resolve(tree);
158
+ }
159
+ };
160
+ function isJavaScriptUrl(url) {
161
+ return typeof url === 'string' && url.toLowerCase().startsWith(JAVASCRIPT_URL_PROTOCOL);
162
+ }
163
+ const jsWrapperStart = 'function a(){';
164
+ const jsWrapperEnd = '}a();';
165
+ function minifyJavaScriptUrl(node, attrName, terser) {
166
+ var _node_attrs;
167
+ if (!terser) return Promise.resolve();
168
+ let result = (_node_attrs = node.attrs) == null ? void 0 : _node_attrs[attrName];
169
+ if (result) {
170
+ result = jsWrapperStart + result.slice(JAVASCRIPT_URL_PROTOCOL.length) + jsWrapperEnd;
171
+ return terser.minify(result, {}) // Default Option is good enough
172
+ .then(({ code })=>{
173
+ const minifiedJs = code.substring(jsWrapperStart.length, code.length - jsWrapperEnd.length);
174
+ node.attrs[attrName] = JAVASCRIPT_URL_PROTOCOL + minifiedJs;
175
+ });
176
+ }
177
+ return Promise.resolve();
178
+ }
179
+
180
+ exports.default = mod;
@@ -0,0 +1,178 @@
1
+ import { optionalImport } from '../helpers.mjs';
2
+
3
+ // Adopts from https://github.com/kangax/html-minifier/blob/51ce10f4daedb1de483ffbcccecc41be1c873da2/src/htmlminifier.js#L209-L221
4
+ const tagsHaveUriValuesForAttributes = new Set([
5
+ 'a',
6
+ 'area',
7
+ 'link',
8
+ 'base',
9
+ 'object',
10
+ 'blockquote',
11
+ 'q',
12
+ 'del',
13
+ 'ins',
14
+ 'form',
15
+ 'input',
16
+ 'head',
17
+ 'audio',
18
+ 'embed',
19
+ 'iframe',
20
+ 'img',
21
+ 'script',
22
+ 'track',
23
+ 'video'
24
+ ]);
25
+ const tagsHasHrefAttributes = new Set([
26
+ 'a',
27
+ 'area',
28
+ 'link',
29
+ 'base'
30
+ ]);
31
+ const attributesOfImgTagHasUriValues = new Set([
32
+ 'src',
33
+ 'longdesc',
34
+ 'usemap'
35
+ ]);
36
+ const attributesOfObjectTagHasUriValues = new Set([
37
+ 'classid',
38
+ 'codebase',
39
+ 'data',
40
+ 'usemap'
41
+ ]);
42
+ const tagsHasCiteAttributes = new Set([
43
+ 'blockquote',
44
+ 'q',
45
+ 'ins',
46
+ 'del'
47
+ ]);
48
+ const tagsHasSrcAttributes = new Set([
49
+ 'audio',
50
+ 'embed',
51
+ 'iframe',
52
+ 'img',
53
+ 'input',
54
+ 'script',
55
+ 'track',
56
+ 'video',
57
+ /**
58
+ * https://html.spec.whatwg.org/#attr-source-src
59
+ *
60
+ * Although most of browsers recommend not to use "src" in <source>,
61
+ * but technically it does comply with HTML Standard.
62
+ */ 'source'
63
+ ]);
64
+ const isUriTypeAttribute = (tag, attr)=>{
65
+ 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';
66
+ };
67
+ const isSrcsetAttribute = (tag, attr)=>{
68
+ return tag === 'source' && attr === 'srcset' || tag === 'img' && attr === 'srcset' || tag === 'link' && attr === 'imagesrcset';
69
+ };
70
+ const processModuleOptions = (options)=>{
71
+ // FIXME!
72
+ // relateurl@1.0.0-alpha only supports URL while stable version (0.2.7) only supports string
73
+ // should convert input into URL instance after relateurl@1 is stable
74
+ if (typeof options === 'string') return options;
75
+ if (options instanceof URL) return options.toString();
76
+ return false;
77
+ };
78
+ const isLinkRelCanonical = ({ tag, attrs })=>{
79
+ // Return false early for non-"link" tag
80
+ if (tag !== 'link' || !attrs) return false;
81
+ for (const [attrName, attrValue] of Object.entries(attrs)){
82
+ if (attrName.toLowerCase() === 'rel' && attrValue === 'canonical') return true;
83
+ }
84
+ return false;
85
+ };
86
+ const JAVASCRIPT_URL_PROTOCOL = 'javascript:';
87
+ let relateUrlInstance;
88
+ let STORED_URL_BASE;
89
+ /** Convert absolute url into relative url */ const mod = {
90
+ async default (tree, options, moduleOptions) {
91
+ const RelateUrl = await optionalImport('relateurl');
92
+ const srcset = await optionalImport('srcset');
93
+ const terser = await optionalImport('terser');
94
+ const promises = [];
95
+ const urlBase = processModuleOptions(moduleOptions);
96
+ // Invalid configuration, return tree directly
97
+ if (!urlBase) return tree;
98
+ /** Bring up a reusable RelateUrl instances (only once)
99
+ *
100
+ * STORED_URL_BASE is used to invalidate RelateUrl instances,
101
+ * avoiding require.cache acrossing multiple htmlnano instance with different configuration,
102
+ * e.g. unit tests cases.
103
+ */ if (!relateUrlInstance || STORED_URL_BASE !== urlBase) {
104
+ if (RelateUrl) {
105
+ relateUrlInstance = new RelateUrl(urlBase);
106
+ }
107
+ STORED_URL_BASE = urlBase;
108
+ }
109
+ tree.walk((node)=>{
110
+ if (!node.attrs) return node;
111
+ if (!node.tag) return node;
112
+ if (!tagsHaveUriValuesForAttributes.has(node.tag)) return node;
113
+ // Prevent link[rel=canonical] being processed
114
+ // Can't be excluded by isUriTypeAttribute()
115
+ if (isLinkRelCanonical(node)) return node;
116
+ for (const [attrName, attrValue] of Object.entries(node.attrs)){
117
+ const attrNameLower = attrName.toLowerCase();
118
+ if (isUriTypeAttribute(node.tag, attrNameLower)) {
119
+ if (isJavaScriptUrl(attrValue)) {
120
+ promises.push(minifyJavaScriptUrl(node, attrName, terser));
121
+ } else {
122
+ if (relateUrlInstance && attrValue) {
123
+ // FIXME!
124
+ // relateurl@1.0.0-alpha only supports URL while stable version (0.2.7) only supports string
125
+ // the WHATWG URL API is very strict while attrValue might not be a valid URL
126
+ // new URL should be used, and relateUrl#relate should be wrapped in try...catch after relateurl@1 is stable
127
+ node.attrs[attrName] = relateUrlInstance.relate(attrValue);
128
+ }
129
+ }
130
+ continue;
131
+ }
132
+ if (isSrcsetAttribute(node.tag, attrNameLower)) {
133
+ if (srcset && attrValue) {
134
+ try {
135
+ const parsedSrcset = srcset.parseSrcset(attrValue, {
136
+ strict: true
137
+ });
138
+ node.attrs[attrName] = srcset.stringifySrcset(parsedSrcset.map((item)=>{
139
+ if (relateUrlInstance) {
140
+ // @ts-expect-error -- not actually readonly
141
+ item.url = relateUrlInstance.relate(item.url);
142
+ }
143
+ return item;
144
+ }));
145
+ } catch (e) {
146
+ // srcset will throw an Error for invalid srcset.
147
+ }
148
+ }
149
+ continue;
150
+ }
151
+ }
152
+ return node;
153
+ });
154
+ if (promises.length > 0) return Promise.all(promises).then(()=>tree);
155
+ return Promise.resolve(tree);
156
+ }
157
+ };
158
+ function isJavaScriptUrl(url) {
159
+ return typeof url === 'string' && url.toLowerCase().startsWith(JAVASCRIPT_URL_PROTOCOL);
160
+ }
161
+ const jsWrapperStart = 'function a(){';
162
+ const jsWrapperEnd = '}a();';
163
+ function minifyJavaScriptUrl(node, attrName, terser) {
164
+ var _node_attrs;
165
+ if (!terser) return Promise.resolve();
166
+ let result = (_node_attrs = node.attrs) == null ? void 0 : _node_attrs[attrName];
167
+ if (result) {
168
+ result = jsWrapperStart + result.slice(JAVASCRIPT_URL_PROTOCOL.length) + jsWrapperEnd;
169
+ return terser.minify(result, {}) // Default Option is good enough
170
+ .then(({ code })=>{
171
+ const minifiedJs = code.substring(jsWrapperStart.length, code.length - jsWrapperEnd.length);
172
+ node.attrs[attrName] = JAVASCRIPT_URL_PROTOCOL + minifiedJs;
173
+ });
174
+ }
175
+ return Promise.resolve();
176
+ }
177
+
178
+ export { mod as default };