htmlnano 0.2.5 → 0.2.9
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/CHANGELOG.md +81 -2
- package/README.md +268 -15
- package/lib/helpers.js +16 -36
- package/lib/htmlnano.js +33 -69
- package/lib/modules/collapseAttributeWhitespace.js +21 -19
- package/lib/modules/collapseBooleanAttributes.js +36 -48
- package/lib/modules/collapseWhitespace.js +58 -32
- package/lib/modules/custom.js +12 -12
- package/lib/modules/deduplicateAttributeValues.js +34 -36
- package/lib/modules/mergeScripts.js +46 -66
- package/lib/modules/mergeStyles.js +33 -32
- package/lib/modules/minifyConditionalComments.js +58 -0
- package/lib/modules/minifyCss.js +59 -41
- package/lib/modules/minifyJs.js +79 -64
- package/lib/modules/minifyJson.js +23 -17
- package/lib/modules/minifySvg.js +20 -28
- package/lib/modules/minifyUrls.js +116 -0
- package/lib/modules/removeAttributeQuotes.js +19 -0
- package/lib/modules/removeComments.js +52 -34
- package/lib/modules/removeEmptyAttributes.js +19 -21
- package/lib/modules/removeOptionalTags.js +220 -0
- package/lib/modules/removeRedundantAttributes.js +89 -66
- package/lib/modules/removeUnusedCss.js +94 -116
- package/lib/modules/sortAttributes.js +115 -0
- package/lib/modules/sortAttributesWithLists.js +138 -0
- package/lib/presets/ampSafe.js +16 -13
- package/lib/presets/max.js +23 -17
- package/lib/presets/safe.js +38 -25
- package/package.json +28 -19
- package/test.js +30 -7
- package/lib/presets/hard.js +0 -26
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,61 @@
|
|
|
2
2
|
All notable changes to this project will be documented in this file.
|
|
3
3
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
|
4
4
|
|
|
5
|
+
|
|
6
|
+
## [0.2.9] - 2021-04-11
|
|
7
|
+
### Added
|
|
8
|
+
- `minifyConditionalComment` support `<html>` [#125].
|
|
9
|
+
- Minify JS within `<script type="module">` [#135].
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- `collapseWhitespaces` around comment [#120].
|
|
13
|
+
- handle `CDATA` inside script correctly [#122].
|
|
14
|
+
- Minify SVG correctly [#129].
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Upgrade to terser@5 (JS minification).
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## [0.2.8] - 2020-11-15
|
|
22
|
+
### Added
|
|
23
|
+
- [`removeOptionalTags`](https://github.com/posthtml/htmlnano#removeoptionaltags) [#110].
|
|
24
|
+
- [`sortAttributes`](https://github.com/posthtml/htmlnano#removeoptionaltags) [#113].
|
|
25
|
+
- `source[src]` and `srcset` support to `minifyUrls` [#117].
|
|
26
|
+
- [`minifyConditionalComments`](https://github.com/posthtml/htmlnano#minifyconditionalcomments) [#119].
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
- Sort by frequency `sortAttributesWithLists` [#111].
|
|
30
|
+
- Strip more spaces in `collapseWhitespace` [#112].
|
|
31
|
+
- Remove `loading="eager"` from `<img>` and `<iframe>` [#114].
|
|
32
|
+
- Remove redundant `type` from `<script>` [#114].
|
|
33
|
+
- Strip whitespaces between textnode and element [#116].
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
## [0.2.7] - 2020-10-17
|
|
38
|
+
### Added
|
|
39
|
+
- More aggressive whitespace removal option [#90].
|
|
40
|
+
- Cloudflare SSE support to `removeComments` [#94].
|
|
41
|
+
- Improve compression ratio by sorting attribute values [#95].
|
|
42
|
+
- New `minifyUrls` module [#98].
|
|
43
|
+
- New `removeAttributeQuotes` module [#104].
|
|
44
|
+
- Remove `type=text/css` for `link[rel=stylesheet]` [#102].
|
|
45
|
+
- Collapse `crossorigin` attributes [#107].
|
|
46
|
+
- Exclude excerpt comment for common CMS [#108].
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
- Keep JS inside SVG wrapped in `//<![CDATA[ //]]` [#88].
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
## [0.2.6] - 2020-07-15
|
|
53
|
+
### Added
|
|
54
|
+
- Let PostHTML options to be passed.
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
- `<script>` tags merging without content.
|
|
58
|
+
|
|
59
|
+
|
|
5
60
|
## [0.2.5] - 2019-11-09
|
|
6
61
|
### Added
|
|
7
62
|
- Option to remove unused CSS using PurgeCSS [#84].
|
|
@@ -128,7 +183,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
|
128
183
|
- Remove attributes that contains only white spaces.
|
|
129
184
|
|
|
130
185
|
|
|
131
|
-
|
|
186
|
+
[0.2.9]: https://github.com/posthtml/htmlnano/compare/0.2.8...0.2.9
|
|
187
|
+
[0.2.8]: https://github.com/posthtml/htmlnano/compare/0.2.7...0.2.8
|
|
188
|
+
[0.2.7]: https://github.com/posthtml/htmlnano/compare/0.2.6...0.2.7
|
|
189
|
+
[0.2.6]: https://github.com/posthtml/htmlnano/compare/0.2.5...0.2.6
|
|
132
190
|
[0.2.5]: https://github.com/posthtml/htmlnano/compare/0.2.4...0.2.5
|
|
133
191
|
[0.2.4]: https://github.com/posthtml/htmlnano/compare/0.2.3...0.2.4
|
|
134
192
|
[0.2.3]: https://github.com/posthtml/htmlnano/compare/0.2.2...0.2.3
|
|
@@ -146,7 +204,28 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
|
146
204
|
[0.1.2]: https://github.com/posthtml/htmlnano/compare/0.1.1...0.1.2
|
|
147
205
|
[0.1.1]: https://github.com/posthtml/htmlnano/compare/0.1.0...0.1.1
|
|
148
206
|
|
|
149
|
-
|
|
207
|
+
[#135]: https://github.com/posthtml/htmlnano/issues/135
|
|
208
|
+
[#129]: https://github.com/posthtml/htmlnano/issues/129
|
|
209
|
+
[#125]: https://github.com/posthtml/htmlnano/issues/125
|
|
210
|
+
[#122]: https://github.com/posthtml/htmlnano/issues/122
|
|
211
|
+
[#120]: https://github.com/posthtml/htmlnano/issues/120
|
|
212
|
+
[#119]: https://github.com/posthtml/htmlnano/issues/119
|
|
213
|
+
[#117]: https://github.com/posthtml/htmlnano/issues/117
|
|
214
|
+
[#116]: https://github.com/posthtml/htmlnano/issues/116
|
|
215
|
+
[#114]: https://github.com/posthtml/htmlnano/issues/114
|
|
216
|
+
[#113]: https://github.com/posthtml/htmlnano/issues/113
|
|
217
|
+
[#112]: https://github.com/posthtml/htmlnano/issues/112
|
|
218
|
+
[#111]: https://github.com/posthtml/htmlnano/issues/111
|
|
219
|
+
[#110]: https://github.com/posthtml/htmlnano/issues/110
|
|
220
|
+
[#107]: https://github.com/posthtml/htmlnano/issues/107
|
|
221
|
+
[#108]: https://github.com/posthtml/htmlnano/issues/108
|
|
222
|
+
[#102]: https://github.com/posthtml/htmlnano/issues/102
|
|
223
|
+
[#104]: https://github.com/posthtml/htmlnano/issues/104
|
|
224
|
+
[#98]: https://github.com/posthtml/htmlnano/issues/98
|
|
225
|
+
[#95]: https://github.com/posthtml/htmlnano/issues/95
|
|
226
|
+
[#94]: https://github.com/posthtml/htmlnano/issues/94
|
|
227
|
+
[#90]: https://github.com/posthtml/htmlnano/issues/90
|
|
228
|
+
[#88]: https://github.com/posthtml/htmlnano/issues/88
|
|
150
229
|
[#84]: https://github.com/posthtml/htmlnano/issues/84
|
|
151
230
|
[#80]: https://github.com/posthtml/htmlnano/issues/80
|
|
152
231
|
[#79]: https://github.com/posthtml/htmlnano/issues/79
|
package/README.md
CHANGED
|
@@ -8,16 +8,16 @@ Modular HTML minifier, built on top of the [PostHTML](https://github.com/posthtm
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
## [Benchmark](https://github.com/maltsev/html-minifiers-benchmark/blob/master/README.md)
|
|
11
|
-
[html-minifier@
|
|
12
|
-
[htmlnano@0.2.
|
|
11
|
+
[html-minifier@4.0.0]: https://www.npmjs.com/package/html-minifier
|
|
12
|
+
[htmlnano@0.2.8]: https://www.npmjs.com/package/htmlnano
|
|
13
13
|
|
|
14
|
-
| Website | Source (KB) | [html-minifier@
|
|
14
|
+
| Website | Source (KB) | [html-minifier@4.0.0] | [htmlnano@0.2.8] |
|
|
15
15
|
|---------|------------:|----------------:|-----------:|
|
|
16
|
-
| [stackoverflow.
|
|
17
|
-
| [github.com](
|
|
18
|
-
| [en.wikipedia.org](https://en.wikipedia.org/wiki/Main_Page) |
|
|
19
|
-
| [npmjs.com](https://www.npmjs.com/features) |
|
|
20
|
-
| **Avg. minify rate** | 0% | **
|
|
16
|
+
| [stackoverflow.blog](https://stackoverflow.blog/) | 78 | 72 | 66 |
|
|
17
|
+
| [github.com](https://github.com/) | 215 | 187 | 177 |
|
|
18
|
+
| [en.wikipedia.org](https://en.wikipedia.org/wiki/Main_Page) | 78 | 73 | 72 |
|
|
19
|
+
| [npmjs.com](https://www.npmjs.com/features) | 29 | 25 | 25 |
|
|
20
|
+
| **Avg. minify rate** | 0% | **10%** | **13%** |
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
## Usage
|
|
@@ -49,10 +49,17 @@ const options = {
|
|
|
49
49
|
removeEmptyAttributes: false, // Disable the module "removeEmptyAttributes"
|
|
50
50
|
collapseWhitespace: 'conservative' // Pass options to the module "collapseWhitespace"
|
|
51
51
|
};
|
|
52
|
+
// posthtml, posthtml-render, and posthtml-parse options
|
|
53
|
+
const postHtmlOptions = {
|
|
54
|
+
sync: true, // https://github.com/posthtml/posthtml#usage
|
|
55
|
+
lowerCaseTags: true, // https://github.com/posthtml/posthtml-parser#options
|
|
56
|
+
quoteAllAttributes: false, // https://github.com/posthtml/posthtml-render#options
|
|
57
|
+
};
|
|
52
58
|
|
|
53
59
|
htmlnano
|
|
54
60
|
// "preset" arg might be skipped (see "Presets" section below for more info)
|
|
55
|
-
|
|
61
|
+
// "postHtmlOptions" arg might be skipped
|
|
62
|
+
.process(html, options, preset, postHtmlOptions)
|
|
56
63
|
.then(function (result) {
|
|
57
64
|
// result.html is minified
|
|
58
65
|
})
|
|
@@ -76,8 +83,12 @@ const posthtmlPlugins = [
|
|
|
76
83
|
require('htmlnano')(options)
|
|
77
84
|
];
|
|
78
85
|
|
|
86
|
+
const postHtmlOptions = {
|
|
87
|
+
// See PostHTML docs
|
|
88
|
+
};
|
|
89
|
+
|
|
79
90
|
posthtml(posthtmlPlugins)
|
|
80
|
-
.process(html)
|
|
91
|
+
.process(html, posthtmlOptions)
|
|
81
92
|
.then(function (result) {
|
|
82
93
|
// result.html is minified
|
|
83
94
|
})
|
|
@@ -191,29 +202,39 @@ Collapses redundant white spaces (including new lines). It doesn’t affect whit
|
|
|
191
202
|
|
|
192
203
|
##### Options
|
|
193
204
|
- `conservative` — collapses all redundant white spaces to 1 space (default)
|
|
205
|
+
- `aggressive` — collapses all whitespaces that are redundant and safe to remove
|
|
194
206
|
- `all` — collapses all redundant white spaces
|
|
195
207
|
|
|
196
208
|
##### Side effects
|
|
197
|
-
|
|
198
|
-
|
|
209
|
+
|
|
210
|
+
*all*
|
|
211
|
+
`<i>hello</i> <i>world</i>` or `<i>hello</i><br><i>world</i>` after minification will be rendered as `helloworld`.
|
|
212
|
+
To prevent that use either the default `conservative` option, or the `aggressive` option.
|
|
199
213
|
|
|
200
214
|
##### Example
|
|
201
215
|
Source:
|
|
202
216
|
```html
|
|
203
217
|
<div>
|
|
204
218
|
hello world!
|
|
219
|
+
<a href="#">answer</a>
|
|
205
220
|
<style>div { color: red; } </style>
|
|
221
|
+
<main></main>
|
|
206
222
|
</div>
|
|
207
223
|
```
|
|
208
224
|
|
|
209
225
|
Minified (with `all`):
|
|
210
226
|
```html
|
|
211
|
-
<div>hello world!<style>div { color: red; } </style></div>
|
|
227
|
+
<div>hello world!<a href="#">answer</a><style>div { color: red; } </style><main></main></div>
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Minified (with `aggressive`):
|
|
231
|
+
```html
|
|
232
|
+
<div> hello world! <a href="#">answer</a> <style>div { color: red; } </style><main></main></div>
|
|
212
233
|
```
|
|
213
234
|
|
|
214
235
|
Minified (with `conservative`):
|
|
215
236
|
```html
|
|
216
|
-
<div> hello world! <style>div { color: red; } </style> </div>
|
|
237
|
+
<div> hello world! <a href="#">answer</a> <style>div { color: red; } </style> <main></main> </div>
|
|
217
238
|
```
|
|
218
239
|
|
|
219
240
|
|
|
@@ -271,6 +292,37 @@ Minified:
|
|
|
271
292
|
<img src="foo.jpg" alt="">
|
|
272
293
|
```
|
|
273
294
|
|
|
295
|
+
### removeAttributeQuotes
|
|
296
|
+
Remove quotes around attributes when possible, see [HTML Standard - 12.1.2.3 Attributes - Unquoted attribute value syntax](https://html.spec.whatwg.org/multipage/syntax.html#attributes-2).
|
|
297
|
+
|
|
298
|
+
##### Example
|
|
299
|
+
Source:
|
|
300
|
+
```html
|
|
301
|
+
<div class="foo" title="hello world"></div>
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Minified:
|
|
305
|
+
```html
|
|
306
|
+
<div class=foo title="hello world"></div>
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
##### Notice
|
|
310
|
+
The feature is implemented by [posthtml-render's `quoteAllAttributes`](https://github.com/posthtml/posthtml-render#options), which is one of the PostHTML's option. So `removeAttributeQuotes` could be overriden by other PostHTML's plugins and PostHTML's configuration.
|
|
311
|
+
|
|
312
|
+
For example:
|
|
313
|
+
|
|
314
|
+
```js
|
|
315
|
+
posthtml([
|
|
316
|
+
htmlnano({
|
|
317
|
+
removeAttributeQuotes: true
|
|
318
|
+
})
|
|
319
|
+
]).process(html, {
|
|
320
|
+
quoteAllAttributes: true
|
|
321
|
+
})
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
`removeAttributeQuotes` will not work because PostHTML's `quoteAllAttributes` takes the priority.
|
|
325
|
+
|
|
274
326
|
### removeUnusedCss
|
|
275
327
|
|
|
276
328
|
Removes unused CSS inside `<style>` tags with either [uncss](https://github.com/uncss/uncss)
|
|
@@ -477,6 +529,29 @@ Minified:
|
|
|
477
529
|
<svg baseProfile="full" width="300" height="200" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="red"/><circle cx="150" cy="100" r="80" fill="green"/><text x="150" y="125" font-size="60" text-anchor="middle" fill="#fff">SVG</text></svg>
|
|
478
530
|
```
|
|
479
531
|
|
|
532
|
+
### minifyConditionalComments
|
|
533
|
+
|
|
534
|
+
Minify content inside conditional comments.
|
|
535
|
+
|
|
536
|
+
##### Example
|
|
537
|
+
|
|
538
|
+
Source:
|
|
539
|
+
|
|
540
|
+
```html
|
|
541
|
+
<!--[if lte IE 7]>
|
|
542
|
+
<style type="text/css">
|
|
543
|
+
.title {
|
|
544
|
+
color: red;
|
|
545
|
+
}
|
|
546
|
+
</style>
|
|
547
|
+
<![endif]-->
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
Minified:
|
|
551
|
+
|
|
552
|
+
```html
|
|
553
|
+
<!--[if lte IE 7]><style>.title{color:red}</style><![endif]-->
|
|
554
|
+
```
|
|
480
555
|
|
|
481
556
|
### removeRedundantAttributes
|
|
482
557
|
Removes redundant attributes from tags if they contain default values:
|
|
@@ -486,6 +561,7 @@ Removes redundant attributes from tags if they contain default values:
|
|
|
486
561
|
- `language="javascript"` and `type="text/javascript"` from `<script>`
|
|
487
562
|
- `charset` from `<script>` if it's an external script
|
|
488
563
|
- `media="all"` from `<style>` and `<link>`
|
|
564
|
+
- `type="text/css"` from `<link rel="stylesheet">`
|
|
489
565
|
|
|
490
566
|
##### Options
|
|
491
567
|
This module is disabled by default, change option to true to enable this module.
|
|
@@ -627,13 +703,190 @@ const options = {
|
|
|
627
703
|
|
|
628
704
|
`options` is an object with all options that were passed to the plugin.
|
|
629
705
|
|
|
706
|
+
### sortAttributesWithLists
|
|
707
|
+
Sort values in list-like attributes (`class`, `rel`, `ping`).
|
|
708
|
+
|
|
709
|
+
The module won't impact the plain-text size of the output. However it will improve the compression ratio of gzip/brotli used in HTTP compression.
|
|
710
|
+
|
|
711
|
+
##### Options
|
|
712
|
+
|
|
713
|
+
- `alphabetical`: Default option. Sort attribute values in alphabetical order.
|
|
714
|
+
- `frequency`: Sort attribute values by frequency.
|
|
715
|
+
|
|
716
|
+
##### Example
|
|
717
|
+
|
|
718
|
+
**alphabetical**
|
|
719
|
+
|
|
720
|
+
Source:
|
|
721
|
+
```html
|
|
722
|
+
<div class="foo baz bar">click</div>
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
Processed:
|
|
726
|
+
```html
|
|
727
|
+
<div class="bar baz foo">click</div>
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
**frequency**
|
|
731
|
+
|
|
732
|
+
Source:
|
|
733
|
+
```html
|
|
734
|
+
<div class="foo baz bar"></div><div class="bar foo"></div>
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
Processed:
|
|
738
|
+
```html
|
|
739
|
+
<div class="foo bar baz"></div><div class="foo bar"></div>
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
### sortAttributes
|
|
743
|
+
Sort attributes inside elements.
|
|
744
|
+
|
|
745
|
+
The module won't impact the plain-text size of the output. However it will improve the compression ratio of gzip/brotli used in HTTP compression.
|
|
746
|
+
|
|
747
|
+
##### Options
|
|
748
|
+
|
|
749
|
+
- `alphabetical`: Default option. Sort attributes in alphabetical order.
|
|
750
|
+
- `frequency`: Sort attributes by frequency.
|
|
751
|
+
|
|
752
|
+
##### Example
|
|
753
|
+
|
|
754
|
+
**alphabetical**
|
|
755
|
+
|
|
756
|
+
Source:
|
|
757
|
+
```html
|
|
758
|
+
<input type="text" class="form-control" name="testInput" autofocus="" autocomplete="off" id="testId">
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
Processed:
|
|
762
|
+
```html
|
|
763
|
+
<input autocomplete="off" autofocus="" class="form-control" id="testId" name="testInput" type="text">
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
**frequency**
|
|
767
|
+
|
|
768
|
+
Source:
|
|
769
|
+
```html
|
|
770
|
+
<input type="text" class="form-control" name="testInput" id="testId">
|
|
771
|
+
<a id="testId" href="#" class="testClass"></a>
|
|
772
|
+
<img width="20" src="../images/image.png" height="40" alt="image" class="cls" id="id2">
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
Processed:
|
|
776
|
+
```html
|
|
777
|
+
<input class="form-control" id="testId" type="text" name="testInput">
|
|
778
|
+
<a class="testClass" id="testId" href="#"></a>
|
|
779
|
+
<img class="cls" id="id2" width="20" src="../images/image.png" height="40" alt="image">
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
### minifyUrls
|
|
783
|
+
Convert absolute URL to relative URL using [relateurl](https://www.npmjs.com/package/relateurl).
|
|
784
|
+
|
|
785
|
+
##### Options
|
|
786
|
+
|
|
787
|
+
The base URL to resolve against. Support `String` & `URL`.
|
|
788
|
+
|
|
789
|
+
```js
|
|
790
|
+
htmlnano.process(html, {
|
|
791
|
+
minifyUrls: 'https://example.com' // Valid configuration
|
|
792
|
+
});
|
|
793
|
+
```
|
|
794
|
+
|
|
795
|
+
```js
|
|
796
|
+
htmlnano.process(html, {
|
|
797
|
+
minifyUrls: new URL('https://example.com') // Valid configuration
|
|
798
|
+
});
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
```js
|
|
802
|
+
htmlnano.process(html, {
|
|
803
|
+
minifyUrls: false // The module will be disabled
|
|
804
|
+
});
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
```js
|
|
808
|
+
htmlnano.process(html, {
|
|
809
|
+
minifyUrls: true // Invalid configuration, the module will be disabled
|
|
810
|
+
});
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
##### Example
|
|
814
|
+
|
|
815
|
+
**Basic Usage**
|
|
816
|
+
|
|
817
|
+
Configuration:
|
|
818
|
+
|
|
819
|
+
```js
|
|
820
|
+
htmlnano.process(html, {
|
|
821
|
+
minifyUrls: 'https://example.com'
|
|
822
|
+
});
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
Source:
|
|
826
|
+
|
|
827
|
+
```html
|
|
828
|
+
<a href="https://example.com/foo/bar/baz">bar</a>
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
Minified:
|
|
832
|
+
|
|
833
|
+
```html
|
|
834
|
+
<a href="foo/bar/baz">bar</a>
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
**With sub-directory**
|
|
838
|
+
|
|
839
|
+
Configuration:
|
|
840
|
+
|
|
841
|
+
```js
|
|
842
|
+
htmlnano.process(html, {
|
|
843
|
+
minifyUrls: 'https://example.com/foo/baz/'
|
|
844
|
+
});
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
Source:
|
|
848
|
+
|
|
849
|
+
```html
|
|
850
|
+
<a href="https://example.com/foo/bar">bar</a>
|
|
851
|
+
```
|
|
852
|
+
|
|
853
|
+
Minified:
|
|
854
|
+
|
|
855
|
+
```html
|
|
856
|
+
<a href="../bar">bar</a>
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
## removeOptionalTags
|
|
860
|
+
Remove certain tags that can be omitted, see [HTML Standard - 13.1.2.4 Optional tags](https://html.spec.whatwg.org/multipage/syntax.html#optional-tags).
|
|
861
|
+
|
|
862
|
+
##### Example
|
|
863
|
+
|
|
864
|
+
Source:
|
|
865
|
+
|
|
866
|
+
```html
|
|
867
|
+
<html><head><title>Title</title></head><body><p>Hi</p></body></html>
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
Minified:
|
|
871
|
+
|
|
872
|
+
```html
|
|
873
|
+
<title>Title</title><p>Hi</p>
|
|
874
|
+
```
|
|
875
|
+
##### Notice
|
|
876
|
+
Due to [the limitation of PostHTML](https://github.com/posthtml/htmlnano/issues/99), htmlnano can't remove only the start tag or the end tag of an element. Currently, htmlnano only supports removing the following optional tags, as htmlnano can remove their start tag and end tag at the same time:
|
|
877
|
+
|
|
878
|
+
- `html`
|
|
879
|
+
- `head`
|
|
880
|
+
- `body`
|
|
881
|
+
- `colgroup`
|
|
882
|
+
- `tbody`
|
|
630
883
|
|
|
631
884
|
## Contribute
|
|
632
885
|
Since the minifier is modular, it's very easy to add new modules:
|
|
633
886
|
|
|
634
887
|
1. Create a ES6-file inside `lib/modules/` with a function that does some minification. For example you can check [`lib/modules/example.es6`](https://github.com/posthtml/htmlnano/blob/master/lib/modules/example.es6).
|
|
635
888
|
|
|
636
|
-
2. Add the module
|
|
889
|
+
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`.
|
|
637
890
|
|
|
638
891
|
3. Create a JS-file inside `test/modules/` with some unit-tests.
|
|
639
892
|
|
package/lib/helpers.js
CHANGED
|
@@ -1,61 +1,41 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
|
|
4
|
+
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.isAmpBoilerplate = isAmpBoilerplate;
|
|
7
7
|
exports.isComment = isComment;
|
|
8
8
|
exports.isConditionalComment = isConditionalComment;
|
|
9
9
|
exports.isStyleNode = isStyleNode;
|
|
10
10
|
exports.extractCssFromStyleNode = extractCssFromStyleNode;
|
|
11
|
-
|
|
11
|
+
const ampBoilerplateAttributes = ['amp-boilerplate', 'amp4ads-boilerplate', 'amp4email-boilerplate'];
|
|
12
12
|
|
|
13
13
|
function isAmpBoilerplate(node) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
for (var _iterator = ampBoilerplateAttributes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
23
|
-
var attr = _step.value;
|
|
24
|
-
|
|
25
|
-
if (attr in node.attrs) {
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
} catch (err) {
|
|
30
|
-
_didIteratorError = true;
|
|
31
|
-
_iteratorError = err;
|
|
32
|
-
} finally {
|
|
33
|
-
try {
|
|
34
|
-
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
35
|
-
_iterator.return();
|
|
36
|
-
}
|
|
37
|
-
} finally {
|
|
38
|
-
if (_didIteratorError) {
|
|
39
|
-
throw _iteratorError;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
14
|
+
if (!node.attrs) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
for (const attr of ampBoilerplateAttributes) {
|
|
19
|
+
if (attr in node.attrs) {
|
|
20
|
+
return true;
|
|
42
21
|
}
|
|
22
|
+
}
|
|
43
23
|
|
|
44
|
-
|
|
24
|
+
return false;
|
|
45
25
|
}
|
|
46
26
|
|
|
47
27
|
function isComment(content) {
|
|
48
|
-
|
|
28
|
+
return (content || '').trim().startsWith('<!--');
|
|
49
29
|
}
|
|
50
30
|
|
|
51
31
|
function isConditionalComment(content) {
|
|
52
|
-
|
|
32
|
+
return (content || '').trim().startsWith('<!--[if');
|
|
53
33
|
}
|
|
54
34
|
|
|
55
35
|
function isStyleNode(node) {
|
|
56
|
-
|
|
36
|
+
return node.tag === 'style' && !isAmpBoilerplate(node) && 'content' in node && node.content.length > 0;
|
|
57
37
|
}
|
|
58
38
|
|
|
59
39
|
function extractCssFromStyleNode(node) {
|
|
60
|
-
|
|
40
|
+
return Array.isArray(node.content) ? node.content.join(' ') : node.content;
|
|
61
41
|
}
|
package/lib/htmlnano.js
CHANGED
|
@@ -1,90 +1,54 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
|
|
4
|
+
value: true
|
|
5
5
|
});
|
|
6
|
+
exports.default = void 0;
|
|
6
7
|
|
|
7
|
-
var _posthtml = require(
|
|
8
|
+
var _posthtml = _interopRequireDefault(require("posthtml"));
|
|
8
9
|
|
|
9
|
-
var
|
|
10
|
+
var _safe = _interopRequireDefault(require("./presets/safe"));
|
|
10
11
|
|
|
11
|
-
var
|
|
12
|
+
var _ampSafe = _interopRequireDefault(require("./presets/ampSafe"));
|
|
12
13
|
|
|
13
|
-
var
|
|
14
|
-
|
|
15
|
-
var _ampSafe = require('./presets/ampSafe');
|
|
16
|
-
|
|
17
|
-
var _ampSafe2 = _interopRequireDefault(_ampSafe);
|
|
18
|
-
|
|
19
|
-
var _max = require('./presets/max');
|
|
20
|
-
|
|
21
|
-
var _max2 = _interopRequireDefault(_max);
|
|
14
|
+
var _max = _interopRequireDefault(require("./presets/max"));
|
|
22
15
|
|
|
23
16
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
24
17
|
|
|
25
|
-
function htmlnano() {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
var promise = Promise.resolve(tree);
|
|
32
|
-
|
|
33
|
-
var _loop = function _loop(moduleName) {
|
|
34
|
-
if (!options[moduleName]) {
|
|
35
|
-
// The module is disabled
|
|
36
|
-
return 'continue';
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (_safe2.default[moduleName] === undefined) {
|
|
40
|
-
throw new Error('Module "' + moduleName + '" is not defined');
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
var module = require('./modules/' + moduleName);
|
|
44
|
-
promise = promise.then(function (tree) {
|
|
45
|
-
return module.default(tree, options, options[moduleName]);
|
|
46
|
-
});
|
|
47
|
-
};
|
|
18
|
+
function htmlnano(options = {}, preset = _safe.default) {
|
|
19
|
+
return function minifier(tree) {
|
|
20
|
+
options = { ...preset,
|
|
21
|
+
...options
|
|
22
|
+
};
|
|
23
|
+
let promise = Promise.resolve(tree);
|
|
48
24
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
25
|
+
for (const [moduleName, moduleOptions] of Object.entries(options)) {
|
|
26
|
+
if (!moduleOptions) {
|
|
27
|
+
// The module is disabled
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
52
30
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
31
|
+
if (_safe.default[moduleName] === undefined) {
|
|
32
|
+
throw new Error('Module "' + moduleName + '" is not defined');
|
|
33
|
+
}
|
|
56
34
|
|
|
57
|
-
|
|
35
|
+
let module = require('./modules/' + moduleName);
|
|
58
36
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
} catch (err) {
|
|
62
|
-
_didIteratorError = true;
|
|
63
|
-
_iteratorError = err;
|
|
64
|
-
} finally {
|
|
65
|
-
try {
|
|
66
|
-
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
67
|
-
_iterator.return();
|
|
68
|
-
}
|
|
69
|
-
} finally {
|
|
70
|
-
if (_didIteratorError) {
|
|
71
|
-
throw _iteratorError;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
37
|
+
promise = promise.then(tree => module.default(tree, options, moduleOptions));
|
|
38
|
+
}
|
|
75
39
|
|
|
76
|
-
|
|
77
|
-
|
|
40
|
+
return promise;
|
|
41
|
+
};
|
|
78
42
|
}
|
|
79
43
|
|
|
80
|
-
htmlnano.process = function (html, options, preset) {
|
|
81
|
-
|
|
44
|
+
htmlnano.process = function (html, options, preset, postHtmlOptions) {
|
|
45
|
+
return (0, _posthtml.default)([htmlnano(options, preset)]).process(html, postHtmlOptions);
|
|
82
46
|
};
|
|
83
47
|
|
|
84
48
|
htmlnano.presets = {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
49
|
+
safe: _safe.default,
|
|
50
|
+
ampSafe: _ampSafe.default,
|
|
51
|
+
max: _max.default
|
|
88
52
|
};
|
|
89
|
-
|
|
90
|
-
exports.default =
|
|
53
|
+
var _default = htmlnano;
|
|
54
|
+
exports.default = _default;
|