htmlnano 2.0.4 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +3 -2
- package/CHANGELOG.md +11 -0
- package/docs/docs/040-presets.md +4 -4
- package/docs/docs/050-modules.md +1 -1
- package/docs/docs/060-contribute.md +1 -1
- package/docs/docusaurus.config.js +5 -0
- package/docs/package-lock.json +277 -224
- package/docs/package.json +1 -0
- package/docs/versioned_docs/version-1.1.1/040-presets.md +4 -4
- package/docs/versioned_docs/version-1.1.1/050-modules.md +1 -2
- package/docs/versioned_docs/version-1.1.1/060-contribute.md +1 -1
- package/docs/versioned_docs/version-2.0.0/040-presets.md +4 -4
- package/docs/versioned_docs/version-2.0.0/050-modules.md +1 -1
- package/docs/versioned_docs/version-2.0.0/060-contribute.md +1 -1
- package/index.cjs +11 -0
- package/index.d.cts +3 -0
- package/index.d.mts +3 -0
- package/index.d.ts +3 -3
- package/index.mjs +2 -0
- package/lib/helpers.cjs +78 -0
- package/lib/helpers.mjs +52 -0
- package/lib/htmlnano.cjs +200 -0
- package/lib/htmlnano.mjs +196 -0
- package/lib/modules/{collapseAttributeWhitespace.js → collapseAttributeWhitespace.cjs} +2 -3
- package/lib/modules/collapseAttributeWhitespace.mjs +104 -0
- package/lib/modules/collapseBooleanAttributes.mjs +175 -0
- package/lib/modules/{collapseWhitespace.js → collapseWhitespace.cjs} +3 -2
- package/lib/modules/collapseWhitespace.mjs +132 -0
- package/lib/modules/custom.mjs +16 -0
- package/lib/modules/{deduplicateAttributeValues.js → deduplicateAttributeValues.cjs} +1 -1
- package/lib/modules/deduplicateAttributeValues.mjs +40 -0
- package/lib/modules/example.cjs +85 -0
- package/lib/modules/example.mjs +75 -0
- package/lib/modules/mergeScripts.mjs +56 -0
- package/lib/modules/{mergeStyles.js → mergeStyles.cjs} +1 -1
- package/lib/modules/mergeStyles.mjs +36 -0
- package/lib/modules/{minifyConditionalComments.js → minifyConditionalComments.cjs} +2 -2
- package/lib/modules/minifyConditionalComments.mjs +49 -0
- package/lib/modules/{minifyCss.js → minifyCss.cjs} +8 -8
- package/lib/modules/minifyCss.mjs +88 -0
- package/lib/modules/{minifyJs.js → minifyJs.cjs} +8 -9
- package/lib/modules/minifyJs.mjs +121 -0
- package/lib/modules/minifyJson.mjs +21 -0
- package/lib/modules/{minifySvg.js → minifySvg.cjs} +3 -4
- package/lib/modules/minifySvg.mjs +30 -0
- package/lib/modules/{minifyUrls.js → minifyUrls.cjs} +7 -8
- package/lib/modules/minifyUrls.mjs +229 -0
- package/lib/modules/normalizeAttributeValues.mjs +140 -0
- package/lib/modules/removeAttributeQuotes.mjs +12 -0
- package/lib/modules/{removeComments.js → removeComments.cjs} +1 -1
- package/lib/modules/removeComments.mjs +92 -0
- package/lib/modules/{removeEmptyAttributes.js → removeEmptyAttributes.cjs} +1 -1
- package/lib/modules/removeEmptyAttributes.mjs +121 -0
- package/lib/modules/{removeOptionalTags.js → removeOptionalTags.cjs} +1 -1
- package/lib/modules/removeOptionalTags.mjs +225 -0
- package/lib/modules/{removeRedundantAttributes.js → removeRedundantAttributes.cjs} +1 -2
- package/lib/modules/removeRedundantAttributes.mjs +141 -0
- package/lib/modules/{removeUnusedCss.js → removeUnusedCss.cjs} +12 -13
- package/lib/modules/removeUnusedCss.mjs +122 -0
- package/lib/modules/sortAttributes.mjs +121 -0
- package/lib/modules/{sortAttributesWithLists.js → sortAttributesWithLists.cjs} +1 -1
- package/lib/modules/sortAttributesWithLists.mjs +135 -0
- package/lib/presets/{ampSafe.js → ampSafe.cjs} +4 -9
- package/lib/presets/ampSafe.mjs +11 -0
- package/lib/presets/{max.js → max.cjs} +4 -9
- package/lib/presets/max.mjs +20 -0
- package/lib/presets/{safe.js → safe.cjs} +2 -3
- package/lib/presets/safe.mjs +65 -0
- package/package.json +33 -5
- package/index.js +0 -1
- package/lib/helpers.js +0 -53
- package/lib/htmlnano.js +0 -150
- /package/lib/modules/{collapseBooleanAttributes.js → collapseBooleanAttributes.cjs} +0 -0
- /package/lib/modules/{custom.js → custom.cjs} +0 -0
- /package/lib/modules/{mergeScripts.js → mergeScripts.cjs} +0 -0
- /package/lib/modules/{minifyJson.js → minifyJson.cjs} +0 -0
- /package/lib/modules/{normalizeAttributeValues.js → normalizeAttributeValues.cjs} +0 -0
- /package/lib/modules/{removeAttributeQuotes.js → removeAttributeQuotes.cjs} +0 -0
- /package/lib/modules/{sortAttributes.js → sortAttributes.cjs} +0 -0
package/docs/package.json
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
A preset is just an object with modules config.
|
|
4
4
|
|
|
5
5
|
Currently the following presets are available:
|
|
6
|
-
- [safe](https://github.com/posthtml/htmlnano/blob/master/lib/presets/safe.
|
|
7
|
-
- [ampSafe](https://github.com/posthtml/htmlnano/blob/master/lib/presets/ampSafe.
|
|
8
|
-
- [max](https://github.com/posthtml/htmlnano/blob/master/lib/presets/max.
|
|
6
|
+
- [safe](https://github.com/posthtml/htmlnano/blob/master/lib/presets/safe.mjs) — a default preset for minifying a regular HTML in a safe way (without breaking anything)
|
|
7
|
+
- [ampSafe](https://github.com/posthtml/htmlnano/blob/master/lib/presets/ampSafe.mjs) - same as `safe` preset but for [AMP pages](https://www.ampproject.org/)
|
|
8
|
+
- [max](https://github.com/posthtml/htmlnano/blob/master/lib/presets/max.mjs) - maximal minification (might break some pages)
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
You can use them the following way:
|
|
@@ -26,7 +26,7 @@ htmlnano
|
|
|
26
26
|
});
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
If you skip `preset` argument [`safe`](https://github.com/posthtml/htmlnano/blob/master/lib/presets/safe.
|
|
29
|
+
If you skip `preset` argument [`safe`](https://github.com/posthtml/htmlnano/blob/master/lib/presets/safe.mjs) preset would be used by default.
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
If you'd like to define your very own config without any presets pass an empty object as a preset:
|
|
@@ -144,7 +144,7 @@ Minified:
|
|
|
144
144
|
|
|
145
145
|
|
|
146
146
|
### removeEmptyAttributes
|
|
147
|
-
Removes empty [safe-to-remove](https://github.com/posthtml/htmlnano/blob/master/lib/modules/removeEmptyAttributes.
|
|
147
|
+
Removes empty [safe-to-remove](https://github.com/posthtml/htmlnano/blob/master/lib/modules/removeEmptyAttributes.mjs) attributes.
|
|
148
148
|
|
|
149
149
|
#### Side effects
|
|
150
150
|
This module could break your styles or JS if you use selectors with attributes:
|
|
@@ -783,4 +783,3 @@ Minified:
|
|
|
783
783
|
```html
|
|
784
784
|
<form method="get"></form>
|
|
785
785
|
```
|
|
786
|
-
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Since the minifier is modular, it's very easy to add new modules:
|
|
4
4
|
|
|
5
|
-
1. Create a ES6-file inside `lib/modules/` with a function that does some minification. For example you can check [`lib/modules/example.
|
|
5
|
+
1. Create a ES6-file inside `lib/modules/` with a function that does some minification. For example you can check [`lib/modules/example.mjs`](https://github.com/posthtml/htmlnano/blob/master/lib/modules/example.mjs).
|
|
6
6
|
|
|
7
7
|
2. Add the module's name into one of those [presets](https://github.com/posthtml/htmlnano/tree/master/lib/presets). You can choose either `ampSafe`, `max`, or `safe`.
|
|
8
8
|
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
A preset is just an object with modules config.
|
|
4
4
|
|
|
5
5
|
Currently the following presets are available:
|
|
6
|
-
- [safe](https://github.com/posthtml/htmlnano/blob/master/lib/presets/safe.
|
|
7
|
-
- [ampSafe](https://github.com/posthtml/htmlnano/blob/master/lib/presets/ampSafe.
|
|
8
|
-
- [max](https://github.com/posthtml/htmlnano/blob/master/lib/presets/max.
|
|
6
|
+
- [safe](https://github.com/posthtml/htmlnano/blob/master/lib/presets/safe.mjs) — a default preset for minifying a regular HTML in a safe way (without breaking anything)
|
|
7
|
+
- [ampSafe](https://github.com/posthtml/htmlnano/blob/master/lib/presets/ampSafe.mjs) - same as `safe` preset but for [AMP pages](https://www.ampproject.org/)
|
|
8
|
+
- [max](https://github.com/posthtml/htmlnano/blob/master/lib/presets/max.mjs) - maximal minification (might break some pages)
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
You can use them the following way:
|
|
@@ -26,7 +26,7 @@ htmlnano
|
|
|
26
26
|
});
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
If you skip `preset` argument [`safe`](https://github.com/posthtml/htmlnano/blob/master/lib/presets/safe.
|
|
29
|
+
If you skip `preset` argument [`safe`](https://github.com/posthtml/htmlnano/blob/master/lib/presets/safe.mjs) preset would be used by default.
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
If you'd like to define your very own config without any presets pass an empty object as a preset:
|
|
@@ -144,7 +144,7 @@ Minified:
|
|
|
144
144
|
|
|
145
145
|
|
|
146
146
|
### removeEmptyAttributes
|
|
147
|
-
Removes empty [safe-to-remove](https://github.com/posthtml/htmlnano/blob/master/lib/modules/removeEmptyAttributes.
|
|
147
|
+
Removes empty [safe-to-remove](https://github.com/posthtml/htmlnano/blob/master/lib/modules/removeEmptyAttributes.mjs) attributes.
|
|
148
148
|
|
|
149
149
|
#### Side effects
|
|
150
150
|
This module could break your styles or JS if you use selectors with attributes:
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Since the minifier is modular, it's very easy to add new modules:
|
|
4
4
|
|
|
5
|
-
1. Create a ES6-file inside `lib/modules/` with a function that does some minification. For example you can check [`lib/modules/example.
|
|
5
|
+
1. Create a ES6-file inside `lib/modules/` with a function that does some minification. For example you can check [`lib/modules/example.mjs`](https://github.com/posthtml/htmlnano/blob/master/lib/modules/example.mjs).
|
|
6
6
|
|
|
7
7
|
2. Add the module's name into one of those [presets](https://github.com/posthtml/htmlnano/tree/master/lib/presets). You can choose either `ampSafe`, `max`, or `safe`.
|
|
8
8
|
|
package/index.cjs
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
const htmlnano = require("./lib/htmlnano.cjs").default;
|
|
8
|
+
exports.default = htmlnano;
|
|
9
|
+
|
|
10
|
+
// for backward compatibility with require('htmlnano')
|
|
11
|
+
module.exports = htmlnano;
|
package/index.d.cts
ADDED
package/index.d.mts
ADDED
package/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type PostHTML from "posthtml";
|
|
2
|
-
import type
|
|
2
|
+
import type { MinifyOptions } from "terser";
|
|
3
3
|
import type { Options as CssNanoOptions } from "cssnano";
|
|
4
|
-
import type {
|
|
4
|
+
import type { Config as SvgoOptimizeOptions } from "svgo";
|
|
5
5
|
|
|
6
6
|
export interface HtmlnanoOptions {
|
|
7
7
|
skipConfigLoading?: boolean;
|
|
@@ -17,7 +17,7 @@ export interface HtmlnanoOptions {
|
|
|
17
17
|
mergeScripts?: boolean;
|
|
18
18
|
minifyCss?: CssNanoOptions | boolean;
|
|
19
19
|
minifyConditionalComments?: boolean;
|
|
20
|
-
minifyJs?:
|
|
20
|
+
minifyJs?: MinifyOptions | boolean;
|
|
21
21
|
minifyJson?: boolean;
|
|
22
22
|
minifySvg?: SvgoOptimizeOptions | boolean;
|
|
23
23
|
normalizeAttributeValues?: boolean;
|
package/index.mjs
ADDED
package/lib/helpers.cjs
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.extractCssFromStyleNode = extractCssFromStyleNode;
|
|
7
|
+
exports.isAmpBoilerplate = isAmpBoilerplate;
|
|
8
|
+
exports.isComment = isComment;
|
|
9
|
+
exports.isConditionalComment = isConditionalComment;
|
|
10
|
+
exports.isEventHandler = isEventHandler;
|
|
11
|
+
exports.isStyleNode = isStyleNode;
|
|
12
|
+
exports.optionalImport = optionalImport;
|
|
13
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
14
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
15
|
+
function __transformExtension(filepath, extMapping) {
|
|
16
|
+
if (!filepath.startsWith('./') && !filepath.startsWith('../')) {
|
|
17
|
+
// Package import
|
|
18
|
+
return filepath;
|
|
19
|
+
}
|
|
20
|
+
const idx = filepath.lastIndexOf('.');
|
|
21
|
+
if (idx === -1 || filepath.includes('/', idx)) {
|
|
22
|
+
// No extension
|
|
23
|
+
const newExt = extMapping[''];
|
|
24
|
+
if (newExt) {
|
|
25
|
+
return filepath + newExt;
|
|
26
|
+
}
|
|
27
|
+
return filepath;
|
|
28
|
+
}
|
|
29
|
+
for (let [origExt, newExt] of Object.entries(extMapping).sort((a, b) => b[0].length - a[0].length)) {
|
|
30
|
+
if (filepath.endsWith(origExt)) {
|
|
31
|
+
return filepath.slice(0, -origExt.length) + newExt;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return filepath;
|
|
35
|
+
}
|
|
36
|
+
const ampBoilerplateAttributes = ['amp-boilerplate', 'amp4ads-boilerplate', 'amp4email-boilerplate'];
|
|
37
|
+
function isAmpBoilerplate(node) {
|
|
38
|
+
if (!node.attrs) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
for (const attr of ampBoilerplateAttributes) {
|
|
42
|
+
if (attr in node.attrs) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
function isComment(content) {
|
|
49
|
+
if (typeof content === 'string') {
|
|
50
|
+
return content.trim().startsWith('<!--');
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
function isConditionalComment(content) {
|
|
55
|
+
return (content || '').trim().startsWith('<!--[if');
|
|
56
|
+
}
|
|
57
|
+
function isStyleNode(node) {
|
|
58
|
+
return node.tag === 'style' && !isAmpBoilerplate(node) && 'content' in node && node.content.length > 0;
|
|
59
|
+
}
|
|
60
|
+
function extractCssFromStyleNode(node) {
|
|
61
|
+
return Array.isArray(node.content) ? node.content.join(' ') : node.content;
|
|
62
|
+
}
|
|
63
|
+
function isEventHandler(attributeName) {
|
|
64
|
+
return attributeName && attributeName.slice && attributeName.slice(0, 2).toLowerCase() === 'on' && attributeName.length >= 5;
|
|
65
|
+
}
|
|
66
|
+
async function optionalImport(moduleName) {
|
|
67
|
+
try {
|
|
68
|
+
const module = await (specifier => new Promise(r => r(`${specifier}`)).then(s => _interopRequireWildcard(require(s))))(__transformExtension(moduleName, {
|
|
69
|
+
".mjs": ".cjs"
|
|
70
|
+
}));
|
|
71
|
+
return module.default || module;
|
|
72
|
+
} catch (e) {
|
|
73
|
+
if (e.code === 'MODULE_NOT_FOUND' || e.code === 'ERR_MODULE_NOT_FOUND') {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
throw e;
|
|
77
|
+
}
|
|
78
|
+
}
|
package/lib/helpers.mjs
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const ampBoilerplateAttributes = [
|
|
2
|
+
'amp-boilerplate',
|
|
3
|
+
'amp4ads-boilerplate',
|
|
4
|
+
'amp4email-boilerplate'
|
|
5
|
+
];
|
|
6
|
+
|
|
7
|
+
export function isAmpBoilerplate(node) {
|
|
8
|
+
if (!node.attrs) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
for (const attr of ampBoilerplateAttributes) {
|
|
12
|
+
if (attr in node.attrs) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function isComment(content) {
|
|
20
|
+
if (typeof content === 'string') {
|
|
21
|
+
return content.trim().startsWith('<!--');
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function isConditionalComment(content) {
|
|
27
|
+
return (content || '').trim().startsWith('<!--[if');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function isStyleNode(node) {
|
|
31
|
+
return node.tag === 'style' && !isAmpBoilerplate(node) && 'content' in node && node.content.length > 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function extractCssFromStyleNode(node) {
|
|
35
|
+
return Array.isArray(node.content) ? node.content.join(' ') : node.content;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function isEventHandler(attributeName) {
|
|
39
|
+
return attributeName && attributeName.slice && attributeName.slice(0, 2).toLowerCase() === 'on' && attributeName.length >= 5;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function optionalImport(moduleName) {
|
|
43
|
+
try {
|
|
44
|
+
const module = await import(moduleName);
|
|
45
|
+
return module.default || module;
|
|
46
|
+
} catch (e) {
|
|
47
|
+
if (e.code === 'MODULE_NOT_FOUND' || e.code === 'ERR_MODULE_NOT_FOUND') {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
throw e;
|
|
51
|
+
}
|
|
52
|
+
}
|
package/lib/htmlnano.cjs
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
exports.loadConfig = loadConfig;
|
|
8
|
+
var _posthtml = _interopRequireDefault(require("posthtml"));
|
|
9
|
+
var _cosmiconfig = require("cosmiconfig");
|
|
10
|
+
var _safe = _interopRequireDefault(require("./presets/safe.cjs"));
|
|
11
|
+
var _ampSafe = _interopRequireDefault(require("./presets/ampSafe.cjs"));
|
|
12
|
+
var _max = _interopRequireDefault(require("./presets/max.cjs"));
|
|
13
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
14
|
+
function __transformExtension(filepath, extMapping) {
|
|
15
|
+
if (!filepath.startsWith('./') && !filepath.startsWith('../')) {
|
|
16
|
+
// Package import
|
|
17
|
+
return filepath;
|
|
18
|
+
}
|
|
19
|
+
const idx = filepath.lastIndexOf('.');
|
|
20
|
+
if (idx === -1 || filepath.includes('/', idx)) {
|
|
21
|
+
// No extension
|
|
22
|
+
const newExt = extMapping[''];
|
|
23
|
+
if (newExt) {
|
|
24
|
+
return filepath + newExt;
|
|
25
|
+
}
|
|
26
|
+
return filepath;
|
|
27
|
+
}
|
|
28
|
+
for (let [origExt, newExt] of Object.entries(extMapping).sort((a, b) => b[0].length - a[0].length)) {
|
|
29
|
+
if (filepath.endsWith(origExt)) {
|
|
30
|
+
return filepath.slice(0, -origExt.length) + newExt;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return filepath;
|
|
34
|
+
}
|
|
35
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
36
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
37
|
+
const presets = {
|
|
38
|
+
safe: _safe.default,
|
|
39
|
+
ampSafe: _ampSafe.default,
|
|
40
|
+
max: _max.default
|
|
41
|
+
};
|
|
42
|
+
function loadConfig(options, preset, configPath) {
|
|
43
|
+
let {
|
|
44
|
+
skipConfigLoading = false,
|
|
45
|
+
...rest
|
|
46
|
+
} = options || {};
|
|
47
|
+
if (!skipConfigLoading) {
|
|
48
|
+
const explorer = (0, _cosmiconfig.cosmiconfigSync)('htmlnano');
|
|
49
|
+
const rc = configPath ? explorer.load(configPath) : explorer.search();
|
|
50
|
+
if (rc) {
|
|
51
|
+
const {
|
|
52
|
+
preset: presetName
|
|
53
|
+
} = rc.config;
|
|
54
|
+
if (presetName) {
|
|
55
|
+
if (!preset && presets[presetName]) {
|
|
56
|
+
preset = presets[presetName];
|
|
57
|
+
}
|
|
58
|
+
delete rc.config.preset;
|
|
59
|
+
}
|
|
60
|
+
if (!options) {
|
|
61
|
+
rest = rc.config;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return [rest || {}, preset || _safe.default];
|
|
66
|
+
}
|
|
67
|
+
const optionalDependencies = {
|
|
68
|
+
minifyCss: ['cssnano', 'postcss'],
|
|
69
|
+
minifyJs: ['terser'],
|
|
70
|
+
minifyUrl: ['relateurl', 'srcset', 'terser'],
|
|
71
|
+
minifySvg: ['svgo']
|
|
72
|
+
};
|
|
73
|
+
const modules = {
|
|
74
|
+
collapseAttributeWhitespace: () => Promise.resolve().then(() => _interopRequireWildcard(require("./modules/collapseAttributeWhitespace.cjs"))),
|
|
75
|
+
collapseBooleanAttributes: () => Promise.resolve().then(() => _interopRequireWildcard(require("./modules/collapseBooleanAttributes.cjs"))),
|
|
76
|
+
collapseWhitespace: () => Promise.resolve().then(() => _interopRequireWildcard(require("./modules/collapseWhitespace.cjs"))),
|
|
77
|
+
custom: () => Promise.resolve().then(() => _interopRequireWildcard(require("./modules/custom.cjs"))),
|
|
78
|
+
deduplicateAttributeValues: () => Promise.resolve().then(() => _interopRequireWildcard(require("./modules/deduplicateAttributeValues.cjs"))),
|
|
79
|
+
example: () => Promise.resolve().then(() => _interopRequireWildcard(require("./modules/example.cjs"))),
|
|
80
|
+
mergeScripts: () => Promise.resolve().then(() => _interopRequireWildcard(require("./modules/mergeScripts.cjs"))),
|
|
81
|
+
mergeStyles: () => Promise.resolve().then(() => _interopRequireWildcard(require("./modules/mergeStyles.cjs"))),
|
|
82
|
+
minifyConditionalComments: () => Promise.resolve().then(() => _interopRequireWildcard(require("./modules/minifyConditionalComments.cjs"))),
|
|
83
|
+
minifyCss: () => Promise.resolve().then(() => _interopRequireWildcard(require("./modules/minifyCss.cjs"))),
|
|
84
|
+
minifyJs: () => Promise.resolve().then(() => _interopRequireWildcard(require("./modules/minifyJs.cjs"))),
|
|
85
|
+
minifyJson: () => Promise.resolve().then(() => _interopRequireWildcard(require("./modules/minifyJson.cjs"))),
|
|
86
|
+
minifySvg: () => Promise.resolve().then(() => _interopRequireWildcard(require("./modules/minifySvg.cjs"))),
|
|
87
|
+
minifyUrls: () => Promise.resolve().then(() => _interopRequireWildcard(require("./modules/minifyUrls.cjs"))),
|
|
88
|
+
normalizeAttributeValues: () => Promise.resolve().then(() => _interopRequireWildcard(require("./modules/normalizeAttributeValues.cjs"))),
|
|
89
|
+
removeAttributeQuotes: () => Promise.resolve().then(() => _interopRequireWildcard(require("./modules/removeAttributeQuotes.cjs"))),
|
|
90
|
+
removeComments: () => Promise.resolve().then(() => _interopRequireWildcard(require("./modules/removeComments.cjs"))),
|
|
91
|
+
removeEmptyAttributes: () => Promise.resolve().then(() => _interopRequireWildcard(require("./modules/removeEmptyAttributes.cjs"))),
|
|
92
|
+
removeOptionalTags: () => Promise.resolve().then(() => _interopRequireWildcard(require("./modules/removeOptionalTags.cjs"))),
|
|
93
|
+
removeRedundantAttributes: () => Promise.resolve().then(() => _interopRequireWildcard(require("./modules/removeRedundantAttributes.cjs"))),
|
|
94
|
+
removeUnusedCss: () => Promise.resolve().then(() => _interopRequireWildcard(require("./modules/removeUnusedCss.cjs"))),
|
|
95
|
+
sortAttributes: () => Promise.resolve().then(() => _interopRequireWildcard(require("./modules/sortAttributes.cjs"))),
|
|
96
|
+
sortAttributesWithLists: () => Promise.resolve().then(() => _interopRequireWildcard(require("./modules/sortAttributesWithLists.cjs")))
|
|
97
|
+
};
|
|
98
|
+
function htmlnano(optionsRun, presetRun) {
|
|
99
|
+
let [options, preset] = loadConfig(optionsRun, presetRun);
|
|
100
|
+
return async function minifier(tree) {
|
|
101
|
+
const nodeHandlers = [];
|
|
102
|
+
const attrsHandlers = [];
|
|
103
|
+
const contentsHandlers = [];
|
|
104
|
+
options = {
|
|
105
|
+
...preset,
|
|
106
|
+
...options
|
|
107
|
+
};
|
|
108
|
+
let promise = Promise.resolve(tree);
|
|
109
|
+
for (const [moduleName, moduleOptions] of Object.entries(options)) {
|
|
110
|
+
if (!moduleOptions) {
|
|
111
|
+
// The module is disabled
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (_safe.default[moduleName] === undefined) {
|
|
115
|
+
throw new Error('Module "' + moduleName + '" is not defined');
|
|
116
|
+
}
|
|
117
|
+
(optionalDependencies[moduleName] || []).forEach(async dependency => {
|
|
118
|
+
try {
|
|
119
|
+
await (specifier => new Promise(r => r(`${specifier}`)).then(s => _interopRequireWildcard(require(s))))(__transformExtension(dependency, {
|
|
120
|
+
".mjs": ".cjs"
|
|
121
|
+
}));
|
|
122
|
+
} catch (e) {
|
|
123
|
+
if (e.code === 'MODULE_NOT_FOUND' || e.code === 'ERR_MODULE_NOT_FOUND') {
|
|
124
|
+
console.warn(`You have to install "${dependency}" in order to use htmlnano's "${moduleName}" module`);
|
|
125
|
+
} else {
|
|
126
|
+
throw e;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
const module = moduleName in modules ? await modules[moduleName]() : await (specifier => new Promise(r => r(`${specifier}`)).then(s => _interopRequireWildcard(require(s))))(__transformExtension(`./modules/${moduleName}.mjs`, {
|
|
131
|
+
".mjs": ".cjs"
|
|
132
|
+
}));
|
|
133
|
+
if (typeof module.onAttrs === 'function') {
|
|
134
|
+
attrsHandlers.push(module.onAttrs(options, moduleOptions));
|
|
135
|
+
}
|
|
136
|
+
if (typeof module.onContent === 'function') {
|
|
137
|
+
contentsHandlers.push(module.onContent(options, moduleOptions));
|
|
138
|
+
}
|
|
139
|
+
if (typeof module.onNode === 'function') {
|
|
140
|
+
nodeHandlers.push(module.onNode(options, moduleOptions));
|
|
141
|
+
}
|
|
142
|
+
if (typeof module.default === 'function') {
|
|
143
|
+
promise = promise.then(async tree => await module.default(tree, options, moduleOptions));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (attrsHandlers.length + contentsHandlers.length + nodeHandlers.length === 0) {
|
|
147
|
+
return promise;
|
|
148
|
+
}
|
|
149
|
+
return promise.then(tree => {
|
|
150
|
+
tree.walk(node => {
|
|
151
|
+
if (node) {
|
|
152
|
+
if (node.attrs && typeof node.attrs === 'object') {
|
|
153
|
+
// Convert all attrs' key to lower case
|
|
154
|
+
let newAttrsObj = {};
|
|
155
|
+
Object.entries(node.attrs).forEach(([attrName, attrValue]) => {
|
|
156
|
+
newAttrsObj[attrName.toLowerCase()] = attrValue;
|
|
157
|
+
});
|
|
158
|
+
for (const handler of attrsHandlers) {
|
|
159
|
+
newAttrsObj = handler(newAttrsObj, node);
|
|
160
|
+
}
|
|
161
|
+
node.attrs = newAttrsObj;
|
|
162
|
+
}
|
|
163
|
+
if (node.content) {
|
|
164
|
+
node.content = typeof node.content === 'string' ? [node.content] : node.content;
|
|
165
|
+
if (Array.isArray(node.content) && node.content.length > 0) {
|
|
166
|
+
for (const handler of contentsHandlers) {
|
|
167
|
+
const result = handler(node.content, node);
|
|
168
|
+
node.content = typeof result === 'string' ? [result] : result;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
for (const handler of nodeHandlers) {
|
|
173
|
+
node = handler(node);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return node;
|
|
177
|
+
});
|
|
178
|
+
return tree;
|
|
179
|
+
});
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
htmlnano.getRequiredOptionalDependencies = function (optionsRun, presetRun) {
|
|
183
|
+
const [options] = loadConfig(optionsRun, presetRun);
|
|
184
|
+
return [...new Set(Object.keys(options).filter(moduleName => options[moduleName]).map(moduleName => optionalDependencies[moduleName]).flat())];
|
|
185
|
+
};
|
|
186
|
+
htmlnano.process = function (html, options, preset, postHtmlOptions) {
|
|
187
|
+
return (0, _posthtml.default)([htmlnano(options, preset)]).process(html, postHtmlOptions);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// https://github.com/webpack-contrib/html-minimizer-webpack-plugin/blob/faca00f2219514bc671c5942685721f0b5dbaa70/src/utils.js#L74
|
|
191
|
+
htmlnano.htmlMinimizerWebpackPluginMinify = function htmlNano(input, minimizerOptions = {}) {
|
|
192
|
+
const [[, code]] = Object.entries(input);
|
|
193
|
+
return htmlnano.process(code, minimizerOptions, presets.safe).then(result => {
|
|
194
|
+
return {
|
|
195
|
+
code: result.html
|
|
196
|
+
};
|
|
197
|
+
});
|
|
198
|
+
};
|
|
199
|
+
htmlnano.presets = presets;
|
|
200
|
+
var _default = exports.default = htmlnano;
|