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.
- package/README.md +53 -12
- package/dist/_modules/collapseAttributeWhitespace.d.mts +57 -0
- package/dist/_modules/collapseAttributeWhitespace.d.ts +57 -0
- package/dist/_modules/collapseAttributeWhitespace.js +296 -0
- package/dist/_modules/collapseAttributeWhitespace.mjs +293 -0
- package/dist/_modules/collapseBooleanAttributes.d.mts +60 -0
- package/dist/_modules/collapseBooleanAttributes.d.ts +60 -0
- package/dist/_modules/collapseBooleanAttributes.js +159 -0
- package/{lib/modules → dist/_modules}/collapseBooleanAttributes.mjs +39 -57
- package/dist/_modules/collapseWhitespace.d.mts +57 -0
- package/dist/_modules/collapseWhitespace.d.ts +57 -0
- package/dist/_modules/collapseWhitespace.js +172 -0
- package/dist/_modules/collapseWhitespace.mjs +170 -0
- package/dist/_modules/custom.d.mts +57 -0
- package/dist/_modules/custom.d.ts +57 -0
- package/dist/_modules/custom.js +22 -0
- package/dist/_modules/custom.mjs +20 -0
- package/dist/_modules/deduplicateAttributeValues.d.mts +56 -0
- package/dist/_modules/deduplicateAttributeValues.d.ts +56 -0
- package/dist/_modules/deduplicateAttributeValues.js +38 -0
- package/dist/_modules/deduplicateAttributeValues.mjs +36 -0
- package/dist/_modules/example.d.mts +59 -0
- package/dist/_modules/example.d.ts +59 -0
- package/dist/_modules/example.js +67 -0
- package/dist/_modules/example.mjs +65 -0
- package/dist/_modules/mergeScripts.d.mts +56 -0
- package/dist/_modules/mergeScripts.d.ts +56 -0
- package/dist/_modules/mergeScripts.js +53 -0
- package/dist/_modules/mergeScripts.mjs +51 -0
- package/dist/_modules/mergeStyles.d.mts +56 -0
- package/dist/_modules/mergeStyles.d.ts +56 -0
- package/dist/_modules/mergeStyles.js +42 -0
- package/dist/_modules/mergeStyles.mjs +40 -0
- package/dist/_modules/minifyConditionalComments.d.mts +56 -0
- package/dist/_modules/minifyConditionalComments.d.ts +56 -0
- package/dist/_modules/minifyConditionalComments.js +54 -0
- package/{lib/modules → dist/_modules}/minifyConditionalComments.mjs +21 -22
- package/dist/_modules/minifyCss.d.mts +56 -0
- package/dist/_modules/minifyCss.d.ts +56 -0
- package/dist/_modules/minifyCss.js +84 -0
- package/dist/_modules/minifyCss.mjs +82 -0
- package/dist/_modules/minifyJs.d.mts +56 -0
- package/dist/_modules/minifyJs.d.ts +56 -0
- package/dist/_modules/minifyJs.js +108 -0
- package/dist/_modules/minifyJs.mjs +106 -0
- package/dist/_modules/minifyJson.d.mts +56 -0
- package/dist/_modules/minifyJson.d.ts +56 -0
- package/dist/_modules/minifyJson.js +35 -0
- package/dist/_modules/minifyJson.mjs +33 -0
- package/dist/_modules/minifySvg.d.mts +56 -0
- package/dist/_modules/minifySvg.d.ts +56 -0
- package/dist/_modules/minifySvg.js +40 -0
- package/dist/_modules/minifySvg.mjs +38 -0
- package/dist/_modules/minifyUrls.d.mts +56 -0
- package/dist/_modules/minifyUrls.d.ts +56 -0
- package/dist/_modules/minifyUrls.js +180 -0
- package/dist/_modules/minifyUrls.mjs +178 -0
- package/dist/_modules/normalizeAttributeValues.d.mts +56 -0
- package/dist/_modules/normalizeAttributeValues.d.ts +56 -0
- package/dist/_modules/normalizeAttributeValues.js +234 -0
- package/dist/_modules/normalizeAttributeValues.mjs +232 -0
- package/dist/_modules/removeAttributeQuotes.d.mts +56 -0
- package/dist/_modules/removeAttributeQuotes.d.ts +56 -0
- package/dist/_modules/removeAttributeQuotes.js +15 -0
- package/dist/_modules/removeAttributeQuotes.mjs +13 -0
- package/dist/_modules/removeComments.d.mts +58 -0
- package/dist/_modules/removeComments.d.ts +58 -0
- package/dist/_modules/removeComments.js +83 -0
- package/{lib/modules → dist/_modules}/removeComments.mjs +24 -35
- package/dist/_modules/removeEmptyAttributes.d.mts +56 -0
- package/dist/_modules/removeEmptyAttributes.d.ts +56 -0
- package/dist/_modules/removeEmptyAttributes.js +197 -0
- package/dist/_modules/removeEmptyAttributes.mjs +195 -0
- package/dist/_modules/removeOptionalTags.d.mts +56 -0
- package/dist/_modules/removeOptionalTags.d.ts +56 -0
- package/dist/_modules/removeOptionalTags.js +190 -0
- package/{lib/modules → dist/_modules}/removeOptionalTags.mjs +54 -91
- package/dist/_modules/removeRedundantAttributes.d.mts +57 -0
- package/dist/_modules/removeRedundantAttributes.d.ts +57 -0
- package/dist/_modules/removeRedundantAttributes.js +128 -0
- package/{lib/modules → dist/_modules}/removeRedundantAttributes.mjs +43 -59
- package/dist/_modules/removeUnusedCss.d.mts +60 -0
- package/dist/_modules/removeUnusedCss.d.ts +60 -0
- package/dist/_modules/removeUnusedCss.js +134 -0
- package/dist/_modules/removeUnusedCss.mjs +132 -0
- package/dist/_modules/sortAttributes.d.mts +57 -0
- package/dist/_modules/sortAttributes.d.ts +57 -0
- package/dist/_modules/sortAttributes.js +102 -0
- package/{lib/modules → dist/_modules}/sortAttributes.mjs +39 -58
- package/dist/_modules/sortAttributesWithLists.d.mts +56 -0
- package/dist/_modules/sortAttributesWithLists.d.ts +56 -0
- package/dist/_modules/sortAttributesWithLists.js +118 -0
- package/{lib/modules → dist/_modules}/sortAttributesWithLists.mjs +41 -59
- package/dist/helpers.js +72 -0
- package/dist/helpers.mjs +63 -0
- package/dist/index.js +223 -0
- package/dist/index.mjs +209 -0
- package/dist/presets/ampSafe.js +19 -0
- package/{lib → dist}/presets/ampSafe.mjs +6 -4
- package/dist/presets/max.js +28 -0
- package/{lib → dist}/presets/max.mjs +6 -4
- package/dist/presets/safe.js +60 -0
- package/{lib → dist}/presets/safe.mjs +13 -20
- package/package.json +48 -56
- package/.eslintignore +0 -3
- package/CHANGELOG.md +0 -409
- package/docs/README.md +0 -33
- package/docs/babel.config.js +0 -3
- package/docs/docs/010-introduction.md +0 -22
- package/docs/docs/020-usage.md +0 -117
- package/docs/docs/030-config.md +0 -21
- package/docs/docs/040-presets.md +0 -75
- package/docs/docs/050-modules.md +0 -855
- package/docs/docs/060-contribute.md +0 -16
- package/docs/docusaurus.config.js +0 -65
- package/docs/netlify.toml +0 -4
- package/docs/package-lock.json +0 -21630
- package/docs/package.json +0 -40
- package/docs/sidebars.js +0 -26
- package/docs/versioned_docs/version-1.1.1/010-introduction.md +0 -22
- package/docs/versioned_docs/version-1.1.1/020-usage.md +0 -77
- package/docs/versioned_docs/version-1.1.1/030-config.md +0 -21
- package/docs/versioned_docs/version-1.1.1/040-presets.md +0 -75
- package/docs/versioned_docs/version-1.1.1/050-modules.md +0 -785
- package/docs/versioned_docs/version-1.1.1/060-contribute.md +0 -16
- package/docs/versioned_docs/version-2.0.0/010-introduction.md +0 -22
- package/docs/versioned_docs/version-2.0.0/020-usage.md +0 -77
- package/docs/versioned_docs/version-2.0.0/030-config.md +0 -21
- package/docs/versioned_docs/version-2.0.0/040-presets.md +0 -75
- package/docs/versioned_docs/version-2.0.0/050-modules.md +0 -838
- package/docs/versioned_docs/version-2.0.0/060-contribute.md +0 -16
- package/docs/versioned_sidebars/version-1.1.1-sidebars.json +0 -8
- package/docs/versioned_sidebars/version-2.0.0-sidebars.json +0 -8
- package/docs/versions.json +0 -4
- package/index.cjs +0 -11
- package/index.d.cts +0 -3
- package/index.d.mts +0 -3
- package/index.d.ts +0 -94
- package/index.mjs +0 -2
- package/lib/helpers.cjs +0 -79
- package/lib/helpers.mjs +0 -53
- package/lib/htmlnano.cjs +0 -202
- package/lib/htmlnano.mjs +0 -198
- package/lib/modules/collapseAttributeWhitespace.cjs +0 -86
- package/lib/modules/collapseAttributeWhitespace.mjs +0 -104
- package/lib/modules/collapseBooleanAttributes.cjs +0 -62
- package/lib/modules/collapseWhitespace.cjs +0 -100
- package/lib/modules/collapseWhitespace.mjs +0 -132
- package/lib/modules/custom.cjs +0 -19
- package/lib/modules/custom.mjs +0 -16
- package/lib/modules/deduplicateAttributeValues.cjs +0 -38
- package/lib/modules/deduplicateAttributeValues.mjs +0 -40
- package/lib/modules/example.cjs +0 -85
- package/lib/modules/example.mjs +0 -75
- package/lib/modules/mergeScripts.cjs +0 -54
- package/lib/modules/mergeScripts.mjs +0 -56
- package/lib/modules/mergeStyles.cjs +0 -38
- package/lib/modules/mergeStyles.mjs +0 -36
- package/lib/modules/minifyConditionalComments.cjs +0 -47
- package/lib/modules/minifyCss.cjs +0 -73
- package/lib/modules/minifyCss.mjs +0 -88
- package/lib/modules/minifyJs.cjs +0 -103
- package/lib/modules/minifyJs.mjs +0 -121
- package/lib/modules/minifyJson.cjs +0 -24
- package/lib/modules/minifyJson.mjs +0 -21
- package/lib/modules/minifySvg.cjs +0 -37
- package/lib/modules/minifySvg.mjs +0 -30
- package/lib/modules/minifyUrls.cjs +0 -141
- package/lib/modules/minifyUrls.mjs +0 -229
- package/lib/modules/normalizeAttributeValues.cjs +0 -120
- package/lib/modules/normalizeAttributeValues.mjs +0 -140
- package/lib/modules/removeAttributeQuotes.cjs +0 -17
- package/lib/modules/removeAttributeQuotes.mjs +0 -12
- package/lib/modules/removeComments.cjs +0 -86
- package/lib/modules/removeEmptyAttributes.cjs +0 -72
- package/lib/modules/removeEmptyAttributes.mjs +0 -121
- package/lib/modules/removeOptionalTags.cjs +0 -183
- package/lib/modules/removeRedundantAttributes.cjs +0 -112
- package/lib/modules/removeUnusedCss.cjs +0 -113
- package/lib/modules/removeUnusedCss.mjs +0 -122
- package/lib/modules/sortAttributes.cjs +0 -98
- package/lib/modules/sortAttributesWithLists.cjs +0 -114
- package/lib/presets/ampSafe.cjs +0 -18
- package/lib/presets/max.cjs +0 -27
- package/lib/presets/safe.cjs +0 -65
- package/test.js +0 -23
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
2
|
+
|
|
3
|
+
var helpers_js = require('../helpers.js');
|
|
4
|
+
|
|
5
|
+
const postcssOptions = {
|
|
6
|
+
// Prevent the following warning from being shown:
|
|
7
|
+
// > Without `from` option PostCSS could generate wrong source map and will not find Browserslist config.
|
|
8
|
+
// > Set it to CSS file path or to `undefined` to prevent this warning.
|
|
9
|
+
from: undefined
|
|
10
|
+
};
|
|
11
|
+
/** Minify CSS with cssnano */ const mod = {
|
|
12
|
+
async default (tree, _, cssnanoOptions) {
|
|
13
|
+
const cssnano = await helpers_js.optionalImport('cssnano');
|
|
14
|
+
const postcss = await helpers_js.optionalImport('postcss');
|
|
15
|
+
if (!cssnano || !postcss) {
|
|
16
|
+
return tree;
|
|
17
|
+
}
|
|
18
|
+
const promises = [];
|
|
19
|
+
tree.walk((node)=>{
|
|
20
|
+
// Skip SRI, reasons are documented in "minifyJs" module
|
|
21
|
+
if (node.attrs && 'integrity' in node.attrs) {
|
|
22
|
+
return node;
|
|
23
|
+
}
|
|
24
|
+
if (helpers_js.isStyleNode(node)) {
|
|
25
|
+
promises.push(processStyleNode(node, cssnanoOptions, cssnano, postcss));
|
|
26
|
+
} else if (node.attrs && node.attrs.style) {
|
|
27
|
+
promises.push(processStyleAttr(node, cssnanoOptions, cssnano, postcss));
|
|
28
|
+
}
|
|
29
|
+
return node;
|
|
30
|
+
});
|
|
31
|
+
return Promise.all(promises).then(()=>tree);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
function processStyleNode(styleNode, cssnanoOptions, cssnano, postcss) {
|
|
35
|
+
let css = helpers_js.extractCssFromStyleNode(styleNode);
|
|
36
|
+
if (!css) return;
|
|
37
|
+
// Improve performance by avoiding calling stripCdata again and again
|
|
38
|
+
let isCdataWrapped = false;
|
|
39
|
+
if (css.includes('CDATA')) {
|
|
40
|
+
const strippedCss = stripCdata(css);
|
|
41
|
+
isCdataWrapped = css !== strippedCss;
|
|
42
|
+
css = strippedCss;
|
|
43
|
+
}
|
|
44
|
+
return postcss([
|
|
45
|
+
cssnano(cssnanoOptions)
|
|
46
|
+
]).process(css, postcssOptions).then((result)=>{
|
|
47
|
+
if (isCdataWrapped) {
|
|
48
|
+
styleNode.content = [
|
|
49
|
+
'<![CDATA[' + result.toString() + ']]>'
|
|
50
|
+
];
|
|
51
|
+
} else {
|
|
52
|
+
styleNode.content = [
|
|
53
|
+
result.css
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
function processStyleAttr(node, cssnanoOptions, cssnano, postcss) {
|
|
59
|
+
// CSS "color: red;" is invalid. Therefore it should be wrapped inside some selector:
|
|
60
|
+
// a{color: red;}
|
|
61
|
+
const wrapperStart = 'a{';
|
|
62
|
+
const wrapperEnd = '}';
|
|
63
|
+
if (!node.attrs || !node.attrs.style) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const wrappedStyle = wrapperStart + (node.attrs.style || '') + wrapperEnd;
|
|
67
|
+
return postcss([
|
|
68
|
+
cssnano(cssnanoOptions)
|
|
69
|
+
]).process(wrappedStyle, postcssOptions).then((result)=>{
|
|
70
|
+
const minifiedCss = result.css;
|
|
71
|
+
// Remove wrapperStart at the start and wrapperEnd at the end of minifiedCss
|
|
72
|
+
node.attrs.style = minifiedCss.substring(wrapperStart.length, minifiedCss.length - wrapperEnd.length);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function stripCdata(css) {
|
|
76
|
+
const leftStrippedCss = css.replace('<![CDATA[', '');
|
|
77
|
+
if (leftStrippedCss === css) {
|
|
78
|
+
return css;
|
|
79
|
+
}
|
|
80
|
+
const strippedCss = leftStrippedCss.replace(']]>', '');
|
|
81
|
+
return leftStrippedCss === strippedCss ? css : strippedCss;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
exports.default = mod;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { optionalImport, isStyleNode, extractCssFromStyleNode } 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
|
+
/** Minify CSS with cssnano */ const mod = {
|
|
10
|
+
async default (tree, _, cssnanoOptions) {
|
|
11
|
+
const cssnano = await optionalImport('cssnano');
|
|
12
|
+
const postcss = await optionalImport('postcss');
|
|
13
|
+
if (!cssnano || !postcss) {
|
|
14
|
+
return tree;
|
|
15
|
+
}
|
|
16
|
+
const promises = [];
|
|
17
|
+
tree.walk((node)=>{
|
|
18
|
+
// Skip SRI, reasons are documented in "minifyJs" module
|
|
19
|
+
if (node.attrs && 'integrity' in node.attrs) {
|
|
20
|
+
return node;
|
|
21
|
+
}
|
|
22
|
+
if (isStyleNode(node)) {
|
|
23
|
+
promises.push(processStyleNode(node, cssnanoOptions, cssnano, postcss));
|
|
24
|
+
} else if (node.attrs && node.attrs.style) {
|
|
25
|
+
promises.push(processStyleAttr(node, cssnanoOptions, cssnano, postcss));
|
|
26
|
+
}
|
|
27
|
+
return node;
|
|
28
|
+
});
|
|
29
|
+
return Promise.all(promises).then(()=>tree);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
function processStyleNode(styleNode, cssnanoOptions, cssnano, postcss) {
|
|
33
|
+
let css = extractCssFromStyleNode(styleNode);
|
|
34
|
+
if (!css) return;
|
|
35
|
+
// Improve performance by avoiding calling stripCdata again and again
|
|
36
|
+
let isCdataWrapped = false;
|
|
37
|
+
if (css.includes('CDATA')) {
|
|
38
|
+
const strippedCss = stripCdata(css);
|
|
39
|
+
isCdataWrapped = css !== strippedCss;
|
|
40
|
+
css = strippedCss;
|
|
41
|
+
}
|
|
42
|
+
return postcss([
|
|
43
|
+
cssnano(cssnanoOptions)
|
|
44
|
+
]).process(css, postcssOptions).then((result)=>{
|
|
45
|
+
if (isCdataWrapped) {
|
|
46
|
+
styleNode.content = [
|
|
47
|
+
'<![CDATA[' + result.toString() + ']]>'
|
|
48
|
+
];
|
|
49
|
+
} else {
|
|
50
|
+
styleNode.content = [
|
|
51
|
+
result.css
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function processStyleAttr(node, cssnanoOptions, cssnano, postcss) {
|
|
57
|
+
// CSS "color: red;" is invalid. Therefore it should be wrapped inside some selector:
|
|
58
|
+
// a{color: red;}
|
|
59
|
+
const wrapperStart = 'a{';
|
|
60
|
+
const wrapperEnd = '}';
|
|
61
|
+
if (!node.attrs || !node.attrs.style) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const wrappedStyle = wrapperStart + (node.attrs.style || '') + wrapperEnd;
|
|
65
|
+
return postcss([
|
|
66
|
+
cssnano(cssnanoOptions)
|
|
67
|
+
]).process(wrappedStyle, postcssOptions).then((result)=>{
|
|
68
|
+
const minifiedCss = result.css;
|
|
69
|
+
// Remove wrapperStart at the start and wrapperEnd at the end of minifiedCss
|
|
70
|
+
node.attrs.style = minifiedCss.substring(wrapperStart.length, minifiedCss.length - wrapperEnd.length);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function stripCdata(css) {
|
|
74
|
+
const leftStrippedCss = css.replace('<![CDATA[', '');
|
|
75
|
+
if (leftStrippedCss === css) {
|
|
76
|
+
return css;
|
|
77
|
+
}
|
|
78
|
+
const strippedCss = leftStrippedCss.replace(']]>', '');
|
|
79
|
+
return leftStrippedCss === strippedCss ? css : strippedCss;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
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<MinifyOptions>;
|
|
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<MinifyOptions>;
|
|
55
|
+
|
|
56
|
+
export { mod as default };
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
2
|
+
|
|
3
|
+
var helpers_js = require('../helpers.js');
|
|
4
|
+
var removeRedundantAttributes_js = require('./removeRedundantAttributes.js');
|
|
5
|
+
|
|
6
|
+
/** Minify JS with Terser */ const mod = {
|
|
7
|
+
async default (tree, _, terserOptions) {
|
|
8
|
+
const terser = await helpers_js.optionalImport('terser');
|
|
9
|
+
if (!terser) return tree;
|
|
10
|
+
const promises = [];
|
|
11
|
+
tree.walk((node)=>{
|
|
12
|
+
const nodeAttrs = node.attrs || {};
|
|
13
|
+
/**
|
|
14
|
+
* Skip SRI
|
|
15
|
+
*
|
|
16
|
+
* If the input <script /> has an SRI attribute, it means that the original <script /> could be trusted,
|
|
17
|
+
* and should not be altered anymore.
|
|
18
|
+
*
|
|
19
|
+
* htmlnano is exactly an MITM that SRI is designed to protect from. If htmlnano or its dependencies get
|
|
20
|
+
* compromised and introduces malicious code, then it is up to the original SRI to protect the end user.
|
|
21
|
+
*
|
|
22
|
+
* So htmlnano will simply skip <script /> that has SRI.
|
|
23
|
+
* If developers do trust htmlnano, they should generate SRI after htmlnano modify the <script />.
|
|
24
|
+
*/ if ('integrity' in nodeAttrs) {
|
|
25
|
+
return node;
|
|
26
|
+
}
|
|
27
|
+
if (node.tag && node.tag === 'script') {
|
|
28
|
+
const mimeType = nodeAttrs.type || 'text/javascript';
|
|
29
|
+
if (removeRedundantAttributes_js.redundantScriptTypes.has(mimeType) || mimeType === 'module') {
|
|
30
|
+
promises.push(processScriptNode(node, terserOptions, terser));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (node.attrs) {
|
|
34
|
+
promises.push.apply(processNodeWithOnAttrs(node, terserOptions, terser));
|
|
35
|
+
}
|
|
36
|
+
return node;
|
|
37
|
+
});
|
|
38
|
+
return Promise.all(promises).then(()=>tree);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
function stripCdata(js) {
|
|
42
|
+
const leftStrippedJs = js.replace(/\/\/\s*<!\[CDATA\[/, '').replace(/\/\*\s*<!\[CDATA\[\s*\*\//, '');
|
|
43
|
+
if (leftStrippedJs === js) {
|
|
44
|
+
return js;
|
|
45
|
+
}
|
|
46
|
+
const strippedJs = leftStrippedJs.replace(/\/\/\s*\]\]>/, '').replace(/\/\*\s*\]\]>\s*\*\//, '');
|
|
47
|
+
return leftStrippedJs === strippedJs ? js : strippedJs;
|
|
48
|
+
}
|
|
49
|
+
function processScriptNode(scriptNode, terserOptions, terser) {
|
|
50
|
+
let js = helpers_js.extractTextContentFromNode(scriptNode).trim();
|
|
51
|
+
if (!js.length) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// Improve performance by avoiding calling stripCdata again and again
|
|
55
|
+
let isCdataWrapped = false;
|
|
56
|
+
if (js.includes('CDATA')) {
|
|
57
|
+
const strippedJs = stripCdata(js);
|
|
58
|
+
isCdataWrapped = js !== strippedJs;
|
|
59
|
+
js = strippedJs;
|
|
60
|
+
}
|
|
61
|
+
return terser.minify(js, terserOptions).then((result)=>{
|
|
62
|
+
if ('error' in result) {
|
|
63
|
+
throw new Error(result.error);
|
|
64
|
+
}
|
|
65
|
+
if (result.code === undefined) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
let content = result.code;
|
|
69
|
+
if (isCdataWrapped) {
|
|
70
|
+
content = '/*<![CDATA[*/' + content + '/*]]>*/';
|
|
71
|
+
}
|
|
72
|
+
scriptNode.content = [
|
|
73
|
+
content
|
|
74
|
+
];
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
function processNodeWithOnAttrs(node, terserOptions, terser) {
|
|
78
|
+
const jsWrapperStart = 'a=function(){';
|
|
79
|
+
const jsWrapperEnd = '};a();';
|
|
80
|
+
const promises = [];
|
|
81
|
+
if (!node.attrs) {
|
|
82
|
+
return promises;
|
|
83
|
+
}
|
|
84
|
+
for(const attrName in node.attrs){
|
|
85
|
+
if (!helpers_js.isEventHandler(attrName)) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const attrValue = node.attrs[attrName];
|
|
89
|
+
if (typeof attrValue !== 'string') {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
// For example onclick="return false" is valid,
|
|
93
|
+
// but "return false;" is invalid (error: 'return' outside of function)
|
|
94
|
+
// Therefore the attribute's code should be wrapped inside function:
|
|
95
|
+
// "function _(){return false;}"
|
|
96
|
+
const wrappedJs = jsWrapperStart + node.attrs[attrName] + jsWrapperEnd;
|
|
97
|
+
const promise = terser.minify(wrappedJs, terserOptions).then(({ code })=>{
|
|
98
|
+
if (code) {
|
|
99
|
+
const minifiedJs = code.substring(jsWrapperStart.length, code.length - jsWrapperEnd.length);
|
|
100
|
+
node.attrs[attrName] = minifiedJs;
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
promises.push(promise);
|
|
104
|
+
}
|
|
105
|
+
return promises;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
exports.default = mod;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { optionalImport, extractTextContentFromNode, isEventHandler } from '../helpers.mjs';
|
|
2
|
+
import { redundantScriptTypes } from './removeRedundantAttributes.mjs';
|
|
3
|
+
|
|
4
|
+
/** Minify JS with Terser */ const mod = {
|
|
5
|
+
async default (tree, _, terserOptions) {
|
|
6
|
+
const terser = await optionalImport('terser');
|
|
7
|
+
if (!terser) return tree;
|
|
8
|
+
const promises = [];
|
|
9
|
+
tree.walk((node)=>{
|
|
10
|
+
const nodeAttrs = node.attrs || {};
|
|
11
|
+
/**
|
|
12
|
+
* Skip SRI
|
|
13
|
+
*
|
|
14
|
+
* If the input <script /> has an SRI attribute, it means that the original <script /> could be trusted,
|
|
15
|
+
* and should not be altered anymore.
|
|
16
|
+
*
|
|
17
|
+
* htmlnano is exactly an MITM that SRI is designed to protect from. If htmlnano or its dependencies get
|
|
18
|
+
* compromised and introduces malicious code, then it is up to the original SRI to protect the end user.
|
|
19
|
+
*
|
|
20
|
+
* So htmlnano will simply skip <script /> that has SRI.
|
|
21
|
+
* If developers do trust htmlnano, they should generate SRI after htmlnano modify the <script />.
|
|
22
|
+
*/ if ('integrity' in nodeAttrs) {
|
|
23
|
+
return node;
|
|
24
|
+
}
|
|
25
|
+
if (node.tag && node.tag === 'script') {
|
|
26
|
+
const mimeType = nodeAttrs.type || 'text/javascript';
|
|
27
|
+
if (redundantScriptTypes.has(mimeType) || mimeType === 'module') {
|
|
28
|
+
promises.push(processScriptNode(node, terserOptions, terser));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (node.attrs) {
|
|
32
|
+
promises.push.apply(processNodeWithOnAttrs(node, terserOptions, terser));
|
|
33
|
+
}
|
|
34
|
+
return node;
|
|
35
|
+
});
|
|
36
|
+
return Promise.all(promises).then(()=>tree);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
function stripCdata(js) {
|
|
40
|
+
const leftStrippedJs = js.replace(/\/\/\s*<!\[CDATA\[/, '').replace(/\/\*\s*<!\[CDATA\[\s*\*\//, '');
|
|
41
|
+
if (leftStrippedJs === js) {
|
|
42
|
+
return js;
|
|
43
|
+
}
|
|
44
|
+
const strippedJs = leftStrippedJs.replace(/\/\/\s*\]\]>/, '').replace(/\/\*\s*\]\]>\s*\*\//, '');
|
|
45
|
+
return leftStrippedJs === strippedJs ? js : strippedJs;
|
|
46
|
+
}
|
|
47
|
+
function processScriptNode(scriptNode, terserOptions, terser) {
|
|
48
|
+
let js = extractTextContentFromNode(scriptNode).trim();
|
|
49
|
+
if (!js.length) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
// Improve performance by avoiding calling stripCdata again and again
|
|
53
|
+
let isCdataWrapped = false;
|
|
54
|
+
if (js.includes('CDATA')) {
|
|
55
|
+
const strippedJs = stripCdata(js);
|
|
56
|
+
isCdataWrapped = js !== strippedJs;
|
|
57
|
+
js = strippedJs;
|
|
58
|
+
}
|
|
59
|
+
return terser.minify(js, terserOptions).then((result)=>{
|
|
60
|
+
if ('error' in result) {
|
|
61
|
+
throw new Error(result.error);
|
|
62
|
+
}
|
|
63
|
+
if (result.code === undefined) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
let content = result.code;
|
|
67
|
+
if (isCdataWrapped) {
|
|
68
|
+
content = '/*<![CDATA[*/' + content + '/*]]>*/';
|
|
69
|
+
}
|
|
70
|
+
scriptNode.content = [
|
|
71
|
+
content
|
|
72
|
+
];
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function processNodeWithOnAttrs(node, terserOptions, terser) {
|
|
76
|
+
const jsWrapperStart = 'a=function(){';
|
|
77
|
+
const jsWrapperEnd = '};a();';
|
|
78
|
+
const promises = [];
|
|
79
|
+
if (!node.attrs) {
|
|
80
|
+
return promises;
|
|
81
|
+
}
|
|
82
|
+
for(const attrName in node.attrs){
|
|
83
|
+
if (!isEventHandler(attrName)) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const attrValue = node.attrs[attrName];
|
|
87
|
+
if (typeof attrValue !== 'string') {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
// For example onclick="return false" is valid,
|
|
91
|
+
// but "return false;" is invalid (error: 'return' outside of function)
|
|
92
|
+
// Therefore the attribute's code should be wrapped inside function:
|
|
93
|
+
// "function _(){return false;}"
|
|
94
|
+
const wrappedJs = jsWrapperStart + node.attrs[attrName] + jsWrapperEnd;
|
|
95
|
+
const promise = terser.minify(wrappedJs, terserOptions).then(({ code })=>{
|
|
96
|
+
if (code) {
|
|
97
|
+
const minifiedJs = code.substring(jsWrapperStart.length, code.length - jsWrapperEnd.length);
|
|
98
|
+
node.attrs[attrName] = minifiedJs;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
promises.push(promise);
|
|
102
|
+
}
|
|
103
|
+
return promises;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
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,35 @@
|
|
|
1
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
2
|
+
|
|
3
|
+
const rNodeAttrsTypeJson = /(\/|\+)json/;
|
|
4
|
+
const mod = {
|
|
5
|
+
onContent () {
|
|
6
|
+
return (content, node)=>{
|
|
7
|
+
// Skip SRI, reasons are documented in "minifyJs" module
|
|
8
|
+
if (node.attrs && 'integrity' in node.attrs) {
|
|
9
|
+
return content;
|
|
10
|
+
}
|
|
11
|
+
if (node.attrs && node.attrs.type && rNodeAttrsTypeJson.test(node.attrs.type)) {
|
|
12
|
+
try {
|
|
13
|
+
// cast minified JSON to an array
|
|
14
|
+
let jsonContent = '';
|
|
15
|
+
for(let i = 0, len = content.length; i < len; i++){
|
|
16
|
+
const item = content[i];
|
|
17
|
+
if (typeof item === 'string') {
|
|
18
|
+
jsonContent += item;
|
|
19
|
+
} else {
|
|
20
|
+
return content; // If any item is not a string, return original contents
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return [
|
|
24
|
+
JSON.stringify(JSON.parse(jsonContent))
|
|
25
|
+
];
|
|
26
|
+
} catch (e) {
|
|
27
|
+
// Invalid JSON
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return content;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
exports.default = mod;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const rNodeAttrsTypeJson = /(\/|\+)json/;
|
|
2
|
+
const mod = {
|
|
3
|
+
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
|
+
if (node.attrs && node.attrs.type && rNodeAttrsTypeJson.test(node.attrs.type)) {
|
|
10
|
+
try {
|
|
11
|
+
// cast minified JSON to an array
|
|
12
|
+
let jsonContent = '';
|
|
13
|
+
for(let i = 0, len = content.length; i < len; i++){
|
|
14
|
+
const item = content[i];
|
|
15
|
+
if (typeof item === 'string') {
|
|
16
|
+
jsonContent += item;
|
|
17
|
+
} else {
|
|
18
|
+
return content; // If any item is not a string, return original contents
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return [
|
|
22
|
+
JSON.stringify(JSON.parse(jsonContent))
|
|
23
|
+
];
|
|
24
|
+
} catch (e) {
|
|
25
|
+
// Invalid JSON
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return content;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export { mod as default };
|