htmlnano 2.0.1 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +2 -2
  3. package/docs/docs/010-introduction.md +4 -4
  4. package/docs/docs/020-usage.md +63 -23
  5. package/docs/docs/030-config.md +1 -1
  6. package/docs/docs/050-modules.md +500 -483
  7. package/docs/package-lock.json +289 -95
  8. package/docs/versioned_docs/version-1.1.1/010-introduction.md +4 -4
  9. package/docs/versioned_docs/version-1.1.1/030-config.md +1 -1
  10. package/docs/versioned_docs/version-2.0.0/010-introduction.md +4 -4
  11. package/docs/versioned_docs/version-2.0.0/030-config.md +2 -2
  12. package/index.d.ts +93 -0
  13. package/lib/helpers.js +4 -11
  14. package/lib/htmlnano.js +37 -55
  15. package/lib/modules/collapseAttributeWhitespace.js +11 -12
  16. package/lib/modules/collapseBooleanAttributes.js +33 -9
  17. package/lib/modules/collapseWhitespace.js +17 -19
  18. package/lib/modules/custom.js +0 -3
  19. package/lib/modules/deduplicateAttributeValues.js +3 -5
  20. package/lib/modules/mergeScripts.js +0 -11
  21. package/lib/modules/mergeStyles.js +2 -8
  22. package/lib/modules/minifyConditionalComments.js +4 -15
  23. package/lib/modules/minifyCss.js +5 -16
  24. package/lib/modules/minifyJs.js +8 -28
  25. package/lib/modules/minifyJson.js +5 -7
  26. package/lib/modules/minifySvg.js +13 -4
  27. package/lib/modules/minifyUrls.js +18 -34
  28. package/lib/modules/normalizeAttributeValues.js +85 -2
  29. package/lib/modules/removeAttributeQuotes.js +0 -2
  30. package/lib/modules/removeComments.js +10 -28
  31. package/lib/modules/removeEmptyAttributes.js +6 -6
  32. package/lib/modules/removeOptionalTags.js +9 -46
  33. package/lib/modules/removeRedundantAttributes.js +20 -68
  34. package/lib/modules/removeUnusedCss.js +7 -18
  35. package/lib/modules/sortAttributes.js +10 -25
  36. package/lib/modules/sortAttributesWithLists.js +7 -29
  37. package/lib/presets/ampSafe.js +2 -5
  38. package/lib/presets/max.js +2 -5
  39. package/lib/presets/safe.js +32 -15
  40. package/package.json +9 -15
  41. package/test.js +0 -48
@@ -3,6 +3,49 @@
3
3
  By default the modules should only perform safe transforms, see the module documentation below for details.
4
4
  You can disable modules by passing `false` as option, and enable them by passing `true`.
5
5
 
6
+ The order in which the modules are documented is also the order in which they are applied.
7
+
8
+ ## Attributes
9
+
10
+ ### normalizeAttributeValues
11
+
12
+ - Normalize casing of specific attribute values that are case-insensitive (like `form[method]`, `img[img]` and `input[type]`).
13
+ - Apply [invalid value default](https://html.spec.whatwg.org/#invalid-value-default) attribute to invalid attribute values (like `<input type=foo>` to `<input type=text>`, which can then be minified to `<input>` by `removeRedundantAttributes` module).
14
+ #### Example
15
+
16
+ Source:
17
+
18
+ ```html
19
+ <form method="GET"></form>
20
+ ```
21
+
22
+ Minified:
23
+
24
+ ```html
25
+ <form method="get"></form>
26
+ ```
27
+
28
+ ### removeEmptyAttributes
29
+ Removes empty [safe-to-remove](https://github.com/posthtml/htmlnano/blob/master/lib/modules/removeEmptyAttributes.es6) attributes.
30
+
31
+ #### Side effects
32
+ This module could break your styles or JS if you use selectors with attributes:
33
+ ```CSS
34
+ img[style=""] {
35
+ margin: 10px;
36
+ }
37
+ ```
38
+
39
+ #### Example
40
+ Source:
41
+ ```html
42
+ <img src="foo.jpg" alt="" style="">
43
+ ```
44
+
45
+ Minified:
46
+ ```html
47
+ <img src="foo.jpg" alt="">
48
+ ```
6
49
 
7
50
  ### collapseAttributeWhitespace
8
51
  Collapse redundant white spaces in list-like attributes (`class`, `rel`, `ping`).
@@ -18,48 +61,81 @@ Minified:
18
61
  <a class="content page" style="display: block;" href="https://example.com"></a>
19
62
  ```
20
63
 
21
-
22
-
23
- ### collapseWhitespace
24
- Collapses redundant white spaces (including new lines). It doesn’t affect white spaces in the elements `<style>`, `<textarea>`, `<script>` and `<pre>`.
64
+ ### removeRedundantAttributes
65
+ Removes redundant attributes from tags if they contain default values:
66
+ - `method="get"` from `<form>`
67
+ - `type="text"` from `<input>`
68
+ - `type="submit"` from `<button>`
69
+ - `language="javascript"` and `type="text/javascript"` from `<script>`
70
+ - `charset` from `<script>` if it's an external script
71
+ - `media="all"` from `<style>` and `<link>`
72
+ - `type="text/css"` from `<link rel="stylesheet">`
25
73
 
26
74
  #### Options
27
- - `conservative` collapses all redundant white spaces to 1 space (default)
28
- - `aggressive` — collapses all whitespaces that are redundant and safe to remove
29
- - `all` — collapses all redundant white spaces
75
+ This module is disabled by default, change option to true to enable this module.
30
76
 
31
77
  #### Side effects
32
-
33
- *all*
34
- `<i>hello</i> <i>world</i>` or `<i>hello</i><br><i>world</i>` after minification will be rendered as `helloworld`.
35
- To prevent that use either the default `conservative` option, or the `aggressive` option.
78
+ This module could break your styles or JS if you use selectors with attributes:
79
+ ```CSS
80
+ form[method="get"] {
81
+ color: red;
82
+ }
83
+ ```
36
84
 
37
85
  #### Example
38
86
  Source:
39
87
  ```html
40
- <div>
41
- hello world!
42
- <a href="#">answer</a>
43
- <style>div { color: red; } </style>
44
- <main></main>
45
- </div>
88
+ <form method="get">
89
+ <input type="text">
90
+ </form>
46
91
  ```
47
92
 
48
- Minified (with `all`):
93
+ Minified:
49
94
  ```html
50
- <div>hello world!<a href="#">answer</a><style>div { color: red; } </style><main></main></div>
95
+ <form>
96
+ <input>
97
+ </form>
51
98
  ```
52
99
 
53
- Minified (with `aggressive`):
54
- ```html
55
- <div> hello world! <a href="#">answer</a> <style>div { color: red; } </style><main></main></div>
100
+ ### collapseBooleanAttributes
101
+
102
+ - Collapses boolean attributes (like `disabled`) to the minimized form.
103
+ - Collapses empty string value attributes (like `href=""`) to the minimized form.
104
+ - Collapses [missing value default](https://html.spec.whatwg.org/#missing-value-default) attributes that are empty strings (`audio[preload=auto]` and `video[preload=auto]`) to the minimized form.
105
+
106
+ #### Options
107
+ If your document uses [AMP](https://www.ampproject.org/), set the `amphtml` flag
108
+ to collapse additonal, AMP-specific boolean attributes:
109
+ ```Json
110
+ "collapseBooleanAttributes": {
111
+ "amphtml": true
112
+ }
56
113
  ```
57
114
 
58
- Minified (with `conservative`):
115
+ #### Side effects
116
+ This module could break your styles or JS if you use selectors with attributes:
117
+ ```CSS
118
+ button[disabled="disabled"] {
119
+ color: red;
120
+ }
121
+ ```
122
+
123
+ #### Example
124
+ Source:
59
125
  ```html
60
- <div> hello world! <a href="#">answer</a> <style>div { color: red; } </style> <main></main> </div>
126
+ <button disabled="disabled">click</button>
127
+ <script defer=""></script>
128
+ <a href=""></a>
129
+ <video preload="auto"></video>
61
130
  ```
62
131
 
132
+ Minified:
133
+ ```html
134
+ <button disabled>click</button>
135
+ <script defer></script>
136
+ <a href></a>
137
+ <video preload></video>
138
+ ```
63
139
 
64
140
  ### deduplicateAttributeValues
65
141
  Remove duplicate values from list-like attributes (`class`, `rel`, `ping`).
@@ -75,480 +151,371 @@ Minified:
75
151
  <div class="sidebar left"></div>
76
152
  ```
77
153
 
154
+ ### minifyUrls
155
+ Convert absolute URL to relative URL using [relateurl](https://www.npmjs.com/package/relateurl).
78
156
 
79
- ### removeComments
80
- #### Options
81
- - `safe` – removes all HTML comments except the conditional comments and [`<!--noindex--><!--/noindex-->`](https://yandex.com/support/webmaster/controlling-robot/html.xml) (default)
82
- - `all` — removes all HTML comments
83
- - A `RegExp` — only HTML comments matching the given regexp will be removed.
84
- - A `Function` that returns boolean — removes HTML comments that can make the given callback function returns truthy value.
157
+ You have to install `relateurl`, `terser` and `srcset` in order to use this feature:
85
158
 
86
- #### Example
159
+ ```bash
160
+ npm install --save-dev relateurl terser srcset
161
+ # if you prefer yarn
162
+ # yarn add --dev relateurl terser srcset
163
+ # if you prefer pnpm
164
+ # pnpm install --save-dev relateurl terser srcset
165
+ ```
87
166
 
88
- Source:
167
+ #### Options
168
+
169
+ The base URL to resolve against. Support `String` & `URL`.
89
170
 
90
171
  ```js
91
- {
92
- removeComments: 'all'
93
- }
172
+ htmlnano.process(html, {
173
+ minifyUrls: 'https://example.com' // Valid configuration
174
+ });
94
175
  ```
95
176
 
96
- ```html
97
- <div><!-- test --></div>
177
+ ```js
178
+ htmlnano.process(html, {
179
+ minifyUrls: new URL('https://example.com') // Valid configuration
180
+ });
98
181
  ```
99
182
 
100
- Minified:
183
+ ```js
184
+ htmlnano.process(html, {
185
+ minifyUrls: false // The module will be disabled
186
+ });
187
+ ```
101
188
 
102
- ```html
103
- <div></div>
189
+ ```js
190
+ htmlnano.process(html, {
191
+ minifyUrls: true // Invalid configuration, the module will be disabled
192
+ });
104
193
  ```
105
194
 
106
- Source:
195
+ #### Example
196
+
197
+ **Basic Usage**
198
+
199
+ Configuration:
107
200
 
108
201
  ```js
109
- {
110
- removeComments: /<!--(\/)?noindex-->/
111
- }
202
+ htmlnano.process(html, {
203
+ minifyUrls: 'https://example.com'
204
+ });
112
205
  ```
113
206
 
207
+ Source:
208
+
114
209
  ```html
115
- <div><!--noindex-->this text will not be indexed<!--/noindex-->Lorem ipsum dolor sit amet<!--more-->Lorem ipsum dolor sit amet</div>
210
+ <a href="https://example.com/foo/bar/baz">bar</a>
116
211
  ```
117
212
 
118
213
  Minified:
119
214
 
120
215
  ```html
121
- <div>this text will not be indexedLorem ipsum dolor sit amet<!--more-->Lorem ipsum dolor sit amet</div>
216
+ <a href="foo/bar/baz">bar</a>
122
217
  ```
123
218
 
124
- Source:
219
+ **With sub-directory**
220
+
221
+ Configuration:
125
222
 
126
223
  ```js
127
- {
128
- removeComments: (comments) => {
129
- if (comments.includes('noindex')) return true;
130
- return false;
131
- }
132
- }
224
+ htmlnano.process(html, {
225
+ minifyUrls: 'https://example.com/foo/baz/'
226
+ });
133
227
  ```
134
228
 
229
+ Source:
230
+
135
231
  ```html
136
- <div><!--noindex-->this text will not be indexed<!--/noindex-->Lorem ipsum dolor sit amet<!--more-->Lorem ipsum dolor sit amet</div>
232
+ <a href="https://example.com/foo/bar">bar</a>
137
233
  ```
138
234
 
139
235
  Minified:
140
236
 
141
237
  ```html
142
- <div>this text will not be indexedLorem ipsum dolor sit amet<!--more-->Lorem ipsum dolor sit amet</div>
238
+ <a href="../bar">bar</a>
143
239
  ```
144
240
 
145
241
 
146
- ### removeEmptyAttributes
147
- Removes empty [safe-to-remove](https://github.com/posthtml/htmlnano/blob/master/lib/modules/removeEmptyAttributes.es6) attributes.
242
+ ### sortAttributes
243
+ Sort attributes inside elements.
148
244
 
149
- #### Side effects
150
- This module could break your styles or JS if you use selectors with attributes:
151
- ```CSS
152
- img[style=""] {
153
- margin: 10px;
154
- }
155
- ```
245
+ 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.
246
+
247
+ #### Options
248
+
249
+ - `alphabetical`: Default option. Sort attributes in alphabetical order.
250
+ - `frequency`: Sort attributes by frequency.
156
251
 
157
252
  #### Example
253
+
254
+ **alphabetical**
255
+
158
256
  Source:
159
257
  ```html
160
- <img src="foo.jpg" alt="" style="">
258
+ <input type="text" class="form-control" name="testInput" autofocus="" autocomplete="off" id="testId">
161
259
  ```
162
260
 
163
- Minified:
261
+ Processed:
164
262
  ```html
165
- <img src="foo.jpg" alt="">
263
+ <input autocomplete="off" autofocus="" class="form-control" id="testId" name="testInput" type="text">
166
264
  ```
167
265
 
168
- ### removeAttributeQuotes
169
- 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).
266
+ **frequency**
170
267
 
171
- #### Example
172
268
  Source:
173
269
  ```html
174
- <div class="foo" title="hello world"></div>
270
+ <input type="text" class="form-control" name="testInput" id="testId">
271
+ <a id="testId" href="#" class="testClass"></a>
272
+ <img width="20" src="../images/image.png" height="40" alt="image" class="cls" id="id2">
175
273
  ```
176
274
 
177
- Minified:
275
+ Processed:
178
276
  ```html
179
- <div class=foo title="hello world"></div>
277
+ <input class="form-control" id="testId" type="text" name="testInput">
278
+ <a class="testClass" id="testId" href="#"></a>
279
+ <img class="cls" id="id2" width="20" src="../images/image.png" height="40" alt="image">
180
280
  ```
181
281
 
182
- #### Notice
183
- 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.
184
-
185
- For example:
186
282
 
187
- ```js
188
- posthtml([
189
- htmlnano({
190
- removeAttributeQuotes: true
191
- })
192
- ]).process(html, {
193
- quoteAllAttributes: true
194
- })
195
- ```
196
283
 
197
- `removeAttributeQuotes` will not work because PostHTML's `quoteAllAttributes` takes the priority.
284
+ ### sortAttributesWithLists
285
+ Sort values in list-like attributes (`class`, `rel`, `ping`).
198
286
 
199
- ### removeUnusedCss
287
+ 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.
200
288
 
201
- Removes unused CSS inside `<style>` tags with either [uncss](https://github.com/uncss/uncss)
202
- or [PurgeCSS](https://github.com/FullHuman/purgecss).
289
+ #### Options
203
290
 
204
- #### With uncss
205
-
206
- You have to install `uncss` in order to use this feature:
207
-
208
- ```bash
209
- npm install --save-dev uncss
210
- # if you prefer yarn
211
- # yarn add --dev uncss
212
- # if you prefer pnpm
213
- # pnpm install --save-dev uncss
214
- ```
215
-
216
- You can also use a mainted fork [@novaatwarren/uncss](https://www.npmjs.com/package/@novaatwarren/uncss) instead.
217
-
218
-
219
- ##### Options
220
- See [the documentation of uncss](https://github.com/uncss/uncss) for all supported options.
221
-
222
- uncss options can be passed directly to the `removeUnusedCss` module:
223
- ```js
224
- htmlnano.process(html, {
225
- removeUnusedCss: {
226
- ignore: ['.do-not-remove']
227
- }
228
- });
229
- ```
230
-
231
- The following uncss options are ignored if passed to the module:
232
-
233
- - `stylesheets`
234
- - `ignoreSheets`
235
- - `raw`
236
-
237
- #### With PurgeCSS
291
+ - `alphabetical`: Default option. Sort attribute values in alphabetical order.
292
+ - `frequency`: Sort attribute values by frequency.
238
293
 
239
- Use PurgeCSS instead of uncss by adding `tool: 'purgeCSS'` to the options.
294
+ #### Example
240
295
 
241
- You have to install `purgecss` in order to use this feature:
296
+ **alphabetical**
242
297
 
243
- ```bash
244
- npm install --save-dev purgecss
245
- # if you prefer yarn
246
- # yarn add --dev purgecss
247
- # if you prefer pnpm
248
- # pnpm install --save-dev purgecss
298
+ Source:
299
+ ```html
300
+ <div class="foo baz bar">click</div>
249
301
  ```
250
302
 
251
- ##### Options
252
-
253
- See [the documentation of PurgeCSS](https://www.purgecss.com) for all supported options.
254
-
255
- PurgeCSS options can be passed directly to the `removeUnusedCss` module:
256
- ```js
257
- htmlnano.process(html, {
258
- removeUnusedCss: {
259
- tool: 'purgeCSS',
260
- safelist: ['.do-not-remove']
261
- }
262
- });
303
+ Processed:
304
+ ```html
305
+ <div class="bar baz foo">click</div>
263
306
  ```
264
307
 
265
- The following PurgeCSS options are ignored if passed to the module:
266
-
267
- - `content`
268
- - `css`
269
- - `extractors`
308
+ **frequency**
270
309
 
271
- #### Example
272
310
  Source:
273
311
  ```html
274
- <div class="b">
275
- <style>
276
- .a {
277
- margin: 10px 10px 10px 10px;
278
- }
279
- .b {
280
- color: #ff0000;
281
- }
282
- </style>
283
- </div>
312
+ <div class="foo baz bar"></div><div class="bar foo"></div>
284
313
  ```
285
314
 
286
- Optimized:
315
+ Processed:
287
316
  ```html
288
- <div class="b">
289
- <style>
290
- .b {
291
- color: #ff0000;
292
- }
293
- </style>
294
- </div>
317
+ <div class="foo bar baz"></div><div class="foo bar"></div>
295
318
  ```
296
319
 
297
320
 
298
- ### minifyCss
299
- Minifies CSS with [cssnano](http://cssnano.co/) inside `<style>` tags and `style` attributes.
300
321
 
301
- You have to install `cssnano` and `postcss` in order to use this feature:
302
-
303
- ```bash
304
- npm install --save-dev cssnano postcss
305
- # if you prefer yarn
306
- # yarn add --dev cssnano postcss
307
- # if you prefer pnpm
308
- # pnpm install --save-dev cssnano postcss
309
- ```
310
322
 
311
323
  #### Options
312
- See [the documentation of cssnano](http://cssnano.co/docs/optimisations/) for all supported optimizations.
313
- By default CSS is minified with preset `default`, which shouldn't have any side-effects.
324
+ - `conservative` collapses all redundant white spaces to 1 space (default)
325
+ - `aggressive` collapses all whitespaces that are redundant and safe to remove
326
+ - `all` — collapses all redundant white spaces
314
327
 
315
- To use another preset or disabled some optimizations pass options to `minifyCss` module:
316
- ```js
317
- htmlnano.process(html, {
318
- minifyCss: {
319
- preset: ['default', {
320
- discardComments: {
321
- removeAll: true,
322
- },
323
- }]
324
- }
325
- });
326
- ```
328
+ #### Side effects
329
+
330
+ *all*
331
+ `<i>hello</i> <i>world</i>` or `<i>hello</i><br><i>world</i>` after minification will be rendered as `helloworld`.
332
+ To prevent that use either the default `conservative` option, or the `aggressive` option.
327
333
 
328
334
  #### Example
329
335
  Source:
330
336
  ```html
331
337
  <div>
332
- <style>
333
- h1 {
334
- margin: 10px 10px 10px 10px;
335
- color: #ff0000;
336
- }
337
- </style>
338
+ hello world!
339
+ <a href="#">answer</a>
340
+ <style>div { color: red; } </style>
341
+ <main></main>
338
342
  </div>
339
343
  ```
340
344
 
341
- Minified:
345
+ Minified (with `all`):
342
346
  ```html
343
- <div>
344
- <style>h1{margin:10px;color:red}</style>
345
- </div>
347
+ <div>hello world!<a href="#">answer</a><style>div { color: red; } </style><main></main></div>
346
348
  ```
347
349
 
350
+ Minified (with `aggressive`):
351
+ ```html
352
+ <div> hello world! <a href="#">answer</a> <style>div { color: red; } </style><main></main></div>
353
+ ```
348
354
 
349
- ### minifyJs
350
- Minifies JS using [Terser](https://github.com/fabiosantoscode/terser) inside `<script>` tags.
355
+ Minified (with `conservative`):
356
+ ```html
357
+ <div> hello world! <a href="#">answer</a> <style>div { color: red; } </style> <main></main> </div>
358
+ ```
351
359
 
352
- You have to install `terser` in order to use this feature:
353
360
 
354
- ```bash
355
- npm install --save-dev terser
356
- # if you prefer yarn
357
- # yarn add --dev terser
358
- # if you prefer pnpm
359
- # pnpm install --save-dev terser
360
- ```
361
361
 
362
- #### Options
363
- See [the documentation of Terser](https://github.com/fabiosantoscode/terser#api-reference) for all supported options.
364
- Terser options can be passed directly to the `minifyJs` module:
365
- ```js
366
- htmlnano.process(html, {
367
- minifyJs: {
368
- output: { quote_style: 1 },
369
- },
370
- });
371
- ```
362
+ ## HTML Content
372
363
 
364
+ ### collapseWhitespace
365
+ Collapses redundant white spaces (including new lines). It doesn’t affect white spaces in the elements `<style>`, `<textarea>`, `<script>` and `<pre>`.
373
366
 
374
367
 
368
+ ### removeComments
369
+ #### Options
370
+ - `safe` – removes all HTML comments except the conditional comments and [`<!--noindex--><!--/noindex-->`](https://yandex.com/support/webmaster/controlling-robot/html.xml) (default)
371
+ - `all` — removes all HTML comments
372
+ - A `RegExp` — only HTML comments matching the given regexp will be removed.
373
+ - A `Function` that returns boolean — removes HTML comments that can make the given callback function returns truthy value.
374
+
375
375
  #### Example
376
+
376
377
  Source:
377
- ```html
378
- <div>
379
- <script>
380
- /* comment */
381
- const foo = function () {
382
378
 
383
- };
384
- </script>
385
- </div>
379
+ ```js
380
+ {
381
+ removeComments: 'all'
382
+ }
386
383
  ```
387
384
 
388
- Minified:
389
385
  ```html
390
- <div>
391
- <script>const foo=function(){};</script>
392
- </div>
386
+ <div><!-- test --></div>
393
387
  ```
394
388
 
389
+ Minified:
395
390
 
396
- ### minifyJson
397
- Minifies JSON inside `<script type="application/json"></script>`.
391
+ ```html
392
+ <div></div>
393
+ ```
398
394
 
399
- #### Example
400
395
  Source:
401
- ```html
402
- <script type="application/json">
396
+
397
+ ```js
403
398
  {
404
- "user": "me"
399
+ removeComments: /<!--(\/)?noindex-->/
405
400
  }
406
- </script>
407
401
  ```
408
402
 
409
- Minified:
410
403
  ```html
411
- <script type="application/json">{"user":"me"}</script>
404
+ <div><!--noindex-->this text will not be indexed<!--/noindex-->Lorem ipsum dolor sit amet<!--more-->Lorem ipsum dolor sit amet</div>
412
405
  ```
413
406
 
407
+ Minified:
414
408
 
415
- ### minifySvg
416
- Minifies SVG inside `<svg>` tags using [SVGO](https://github.com/svg/svgo/).
409
+ ```html
410
+ <div>this text will not be indexedLorem ipsum dolor sit amet<!--more-->Lorem ipsum dolor sit amet</div>
411
+ ```
412
+
413
+ Source:
417
414
 
418
- #### Options
419
- See [the documentation of SVGO](https://github.com/svg/svgo/blob/master/README.md) for all supported options.
420
- SVGO options can be passed directly to the `minifySvg` module:
421
415
  ```js
422
- htmlnano.process(html, {
423
- minifySvg: {
424
- plugins: [
425
- {
426
- name: 'preset-default',
427
- params: {
428
- overrides: {
429
- builtinPluginName: {
430
- optionName: 'optionValue'
431
- },
432
- },
433
- },
434
- }
435
- ]
416
+ {
417
+ removeComments: (comments) => {
418
+ if (comments.includes('noindex')) return true;
419
+ return false;
436
420
  }
437
- });
421
+ }
438
422
  ```
439
423
 
440
- #### Example
441
- Source:
442
424
  ```html
443
- <svg version="1.1" baseProfile="full" width="300" height="200" xmlns="http://www.w3.org/2000/svg">
444
- <rect width="100%" height="100%" fill="red" />
445
-
446
- <circle cx="150" cy="100" r="80" fill="green" />
447
-
448
- <text x="150" y="125" font-size="60" text-anchor="middle" fill="white">SVG</text>
449
- </svg>`
425
+ <div><!--noindex-->this text will not be indexed<!--/noindex-->Lorem ipsum dolor sit amet<!--more-->Lorem ipsum dolor sit amet</div>
450
426
  ```
451
427
 
452
428
  Minified:
429
+
453
430
  ```html
454
- <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>
431
+ <div>this text will not be indexedLorem ipsum dolor sit amet<!--more-->Lorem ipsum dolor sit amet</div>
455
432
  ```
456
433
 
457
- ### minifyConditionalComments
458
-
459
- Minify content inside conditional comments.
434
+ ### removeOptionalTags
435
+ 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).
460
436
 
461
437
  #### Example
462
438
 
463
439
  Source:
464
440
 
465
441
  ```html
466
- <!--[if lte IE 7]>
467
- <style type="text/css">
468
- .title {
469
- color: red;
470
- }
471
- </style>
472
- <![endif]-->
442
+ <html><head><title>Title</title></head><body><p>Hi</p></body></html>
473
443
  ```
474
444
 
475
445
  Minified:
476
446
 
477
447
  ```html
478
- <!--[if lte IE 7]><style>.title{color:red}</style><![endif]-->
448
+ <title>Title</title><p>Hi</p>
479
449
  ```
480
450
 
481
- ### removeRedundantAttributes
482
- Removes redundant attributes from tags if they contain default values:
483
- - `method="get"` from `<form>`
484
- - `type="text"` from `<input>`
485
- - `type="submit"` from `<button>`
486
- - `language="javascript"` and `type="text/javascript"` from `<script>`
487
- - `charset` from `<script>` if it's an external script
488
- - `media="all"` from `<style>` and `<link>`
489
- - `type="text/css"` from `<link rel="stylesheet">`
451
+ #### Notice
452
+ 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:
490
453
 
491
- #### Options
492
- This module is disabled by default, change option to true to enable this module.
454
+ - `html`
455
+ - `head`
456
+ - `body`
457
+ - `colgroup`
458
+ - `tbody`
493
459
 
494
- #### Side effects
495
- This module could break your styles or JS if you use selectors with attributes:
496
- ```CSS
497
- form[method="get"] {
498
- color: red;
499
- }
500
- ```
460
+ ### removeOptionalTags
461
+ 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).
501
462
 
502
463
  #### Example
464
+
503
465
  Source:
466
+
504
467
  ```html
505
- <form method="get">
506
- <input type="text">
507
- </form>
468
+ <html><head><title>Title</title></head><body><p>Hi</p></body></html>
508
469
  ```
509
470
 
510
471
  Minified:
472
+
511
473
  ```html
512
- <form>
513
- <input>
514
- </form>
474
+ <title>Title</title><p>Hi</p>
515
475
  ```
516
476
 
477
+ #### Notice
478
+ 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:
517
479
 
518
- ### collapseBooleanAttributes
519
- Collapses boolean attributes (like `disabled`) to the minimized form.
520
-
521
- #### Options
522
- If your document uses [AMP](https://www.ampproject.org/), set the `amphtml` flag
523
- to collapse additonal, AMP-specific boolean attributes:
524
- ```Json
525
- "collapseBooleanAttributes": {
526
- "amphtml": true
527
- }
528
- ```
480
+ - `html`
481
+ - `head`
482
+ - `body`
483
+ - `colgroup`
484
+ - `tbody`
529
485
 
530
- #### Side effects
531
- This module could break your styles or JS if you use selectors with attributes:
532
- ```CSS
533
- button[disabled="disabled"] {
534
- color: red;
535
- }
536
- ```
486
+ ### removeAttributeQuotes
487
+ 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).
537
488
 
538
489
  #### Example
539
490
  Source:
540
491
  ```html
541
- <button disabled="disabled">click</button>
542
- <script defer=""></script>
492
+ <div class="foo" title="hello world"></div>
543
493
  ```
544
494
 
545
495
  Minified:
546
496
  ```html
547
- <button disabled>click</button>
548
- <script defer></script>
497
+ <div class=foo title="hello world"></div>
549
498
  ```
550
499
 
500
+ #### Notice
501
+ 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.
502
+
503
+ For example:
504
+
505
+ ```js
506
+ posthtml([
507
+ htmlnano({
508
+ removeAttributeQuotes: true
509
+ })
510
+ ]).process(html, {
511
+ quoteAllAttributes: true
512
+ })
513
+ ```
551
514
 
515
+ `removeAttributeQuotes` will not work because PostHTML's `quoteAllAttributes` takes the priority.
516
+
517
+
518
+ ## `<style>`, `<script>` and `<svg>` Tags
552
519
  ### mergeStyles
553
520
  Merges multiple `<style>` with the same `media` and `type` into one tag.
554
521
  `<style scoped>...</style>` are skipped.
@@ -597,242 +564,292 @@ Minified:
597
564
  ```
598
565
 
599
566
 
600
- ### custom
601
- It's also possible to pass custom modules in the minifier.
602
- As a function:
603
- ```js
604
- const options = {
605
- custom: function (tree, options) {
606
- // Some minification
607
- return tree;
608
- }
609
- };
567
+ ### minifyCss
568
+ Minifies CSS with [cssnano](http://cssnano.co/) inside `<style>` tags and `style` attributes.
569
+
570
+ You have to install `cssnano` and `postcss` in order to use this feature:
571
+
572
+ ```bash
573
+ npm install --save-dev cssnano postcss
574
+ # if you prefer yarn
575
+ # yarn add --dev cssnano postcss
576
+ # if you prefer pnpm
577
+ # pnpm install --save-dev cssnano postcss
610
578
  ```
611
579
 
612
- Or as a list of functions:
580
+ #### Options
581
+ See [the documentation of cssnano](http://cssnano.co/docs/optimisations/) for all supported optimizations.
582
+ By default CSS is minified with preset `default`, which shouldn't have any side-effects.
583
+
584
+ To use another preset or disabled some optimizations pass options to `minifyCss` module:
613
585
  ```js
614
- const options = {
615
- custom: [
616
- function (tree, options) {
617
- // Some minification
618
- return tree;
619
- },
586
+ htmlnano.process(html, {
587
+ minifyCss: {
588
+ preset: ['default', {
589
+ discardComments: {
590
+ removeAll: true,
591
+ },
592
+ }]
593
+ }
594
+ });
595
+ ```
620
596
 
621
- function (tree, options) {
622
- // Some other minification
623
- return tree;
597
+ #### Example
598
+ Source:
599
+ ```html
600
+ <div>
601
+ <style>
602
+ h1 {
603
+ margin: 10px 10px 10px 10px;
604
+ color: #ff0000;
624
605
  }
625
- ]
626
- };
606
+ </style>
607
+ </div>
627
608
  ```
628
609
 
629
- `options` is an object with all options that were passed to the plugin.
610
+ Minified:
611
+ ```html
612
+ <div>
613
+ <style>h1{margin:10px;color:red}</style>
614
+ </div>
615
+ ```
630
616
 
631
- ### sortAttributesWithLists
632
- Sort values in list-like attributes (`class`, `rel`, `ping`).
633
617
 
634
- 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.
618
+ ### minifyJs
619
+ Minifies JS using [Terser](https://github.com/fabiosantoscode/terser) inside `<script>` tags.
635
620
 
636
- #### Options
621
+ You have to install `terser` in order to use this feature:
637
622
 
638
- - `alphabetical`: Default option. Sort attribute values in alphabetical order.
639
- - `frequency`: Sort attribute values by frequency.
623
+ ```bash
624
+ npm install --save-dev terser
625
+ # if you prefer yarn
626
+ # yarn add --dev terser
627
+ # if you prefer pnpm
628
+ # pnpm install --save-dev terser
629
+ ```
630
+
631
+ #### Options
632
+ See [the documentation of Terser](https://github.com/fabiosantoscode/terser#api-reference) for all supported options.
633
+ Terser options can be passed directly to the `minifyJs` module:
634
+ ```js
635
+ htmlnano.process(html, {
636
+ minifyJs: {
637
+ output: { quote_style: 1 },
638
+ },
639
+ });
640
+ ```
640
641
 
641
- #### Example
642
642
 
643
- **alphabetical**
644
643
 
644
+ #### Example
645
645
  Source:
646
646
  ```html
647
- <div class="foo baz bar">click</div>
647
+ <div>
648
+ <script>
649
+ /* comment */
650
+ const foo = function () {
651
+
652
+ };
653
+ </script>
654
+ </div>
648
655
  ```
649
656
 
650
- Processed:
657
+ Minified:
651
658
  ```html
652
- <div class="bar baz foo">click</div>
659
+ <div>
660
+ <script>const foo=function(){};</script>
661
+ </div>
653
662
  ```
654
663
 
655
- **frequency**
656
664
 
665
+ ### minifyJson
666
+ Minifies JSON inside `<script type="application/json"></script>`.
667
+
668
+ #### Example
657
669
  Source:
658
670
  ```html
659
- <div class="foo baz bar"></div><div class="bar foo"></div>
671
+ <script type="application/json">
672
+ {
673
+ "user": "me"
674
+ }
675
+ </script>
660
676
  ```
661
677
 
662
- Processed:
678
+ Minified:
663
679
  ```html
664
- <div class="foo bar baz"></div><div class="foo bar"></div>
680
+ <script type="application/json">{"user":"me"}</script>
665
681
  ```
666
682
 
667
- ### sortAttributes
668
- Sort attributes inside elements.
669
683
 
670
- 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.
684
+ ### minifySvg
685
+ Minifies SVG inside `<svg>` tags using [SVGO](https://github.com/svg/svgo/).
671
686
 
672
687
  #### Options
673
-
674
- - `alphabetical`: Default option. Sort attributes in alphabetical order.
675
- - `frequency`: Sort attributes by frequency.
688
+ See [the documentation of SVGO](https://github.com/svg/svgo/blob/master/README.md) for all supported options.
689
+ SVGO options can be passed directly to the `minifySvg` module:
690
+ ```js
691
+ htmlnano.process(html, {
692
+ minifySvg: {
693
+ plugins: [
694
+ {
695
+ name: 'preset-default',
696
+ params: {
697
+ overrides: {
698
+ builtinPluginName: {
699
+ optionName: 'optionValue'
700
+ },
701
+ },
702
+ },
703
+ }
704
+ ]
705
+ }
706
+ });
707
+ ```
676
708
 
677
709
  #### Example
678
-
679
- **alphabetical**
680
-
681
710
  Source:
682
711
  ```html
683
- <input type="text" class="form-control" name="testInput" autofocus="" autocomplete="off" id="testId">
684
- ```
685
-
686
- Processed:
687
- ```html
688
- <input autocomplete="off" autofocus="" class="form-control" id="testId" name="testInput" type="text">
689
- ```
712
+ <svg version="1.1" baseProfile="full" width="300" height="200" xmlns="http://www.w3.org/2000/svg">
713
+ <rect width="100%" height="100%" fill="red" />
690
714
 
691
- **frequency**
715
+ <circle cx="150" cy="100" r="80" fill="green" />
692
716
 
693
- Source:
694
- ```html
695
- <input type="text" class="form-control" name="testInput" id="testId">
696
- <a id="testId" href="#" class="testClass"></a>
697
- <img width="20" src="../images/image.png" height="40" alt="image" class="cls" id="id2">
717
+ <text x="150" y="125" font-size="60" text-anchor="middle" fill="white">SVG</text>
718
+ </svg>`
698
719
  ```
699
720
 
700
- Processed:
721
+ Minified:
701
722
  ```html
702
- <input class="form-control" id="testId" type="text" name="testInput">
703
- <a class="testClass" id="testId" href="#"></a>
704
- <img class="cls" id="id2" width="20" src="../images/image.png" height="40" alt="image">
723
+ <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>
705
724
  ```
706
725
 
707
- ### minifyUrls
708
- Convert absolute URL to relative URL using [relateurl](https://www.npmjs.com/package/relateurl).
726
+ ### removeUnusedCss
709
727
 
710
- You have to install `relateurl`, `terser` and `srcset` in order to use this feature:
728
+ Removes unused CSS inside `<style>` tags with either [uncss](https://github.com/uncss/uncss)
729
+ or [PurgeCSS](https://github.com/FullHuman/purgecss).
730
+
731
+ #### With uncss
732
+
733
+ You have to install `uncss` in order to use this feature:
711
734
 
712
735
  ```bash
713
- npm install --save-dev relateurl terser srcset
736
+ npm install --save-dev uncss
714
737
  # if you prefer yarn
715
- # yarn add --dev relateurl terser srcset
738
+ # yarn add --dev uncss
716
739
  # if you prefer pnpm
717
- # pnpm install --save-dev relateurl terser srcset
740
+ # pnpm install --save-dev uncss
718
741
  ```
719
742
 
720
- #### Options
721
-
722
- The base URL to resolve against. Support `String` & `URL`.
723
-
724
- ```js
725
- htmlnano.process(html, {
726
- minifyUrls: 'https://example.com' // Valid configuration
727
- });
728
- ```
743
+ You can also use a mainted fork [@novaatwarren/uncss](https://www.npmjs.com/package/@novaatwarren/uncss) instead.
729
744
 
730
- ```js
731
- htmlnano.process(html, {
732
- minifyUrls: new URL('https://example.com') // Valid configuration
733
- });
734
- ```
735
745
 
736
- ```js
737
- htmlnano.process(html, {
738
- minifyUrls: false // The module will be disabled
739
- });
740
- ```
746
+ ##### Options
747
+ See [the documentation of uncss](https://github.com/uncss/uncss) for all supported options.
741
748
 
749
+ uncss options can be passed directly to the `removeUnusedCss` module:
742
750
  ```js
743
751
  htmlnano.process(html, {
744
- minifyUrls: true // Invalid configuration, the module will be disabled
752
+ removeUnusedCss: {
753
+ ignore: ['.do-not-remove']
754
+ }
745
755
  });
746
756
  ```
747
757
 
748
- #### Example
749
-
750
- **Basic Usage**
751
-
752
- Configuration:
758
+ The following uncss options are ignored if passed to the module:
753
759
 
754
- ```js
755
- htmlnano.process(html, {
756
- minifyUrls: 'https://example.com'
757
- });
758
- ```
760
+ - `stylesheets`
761
+ - `ignoreSheets`
762
+ - `raw`
759
763
 
760
- Source:
764
+ #### With PurgeCSS
761
765
 
762
- ```html
763
- <a href="https://example.com/foo/bar/baz">bar</a>
764
- ```
766
+ Use PurgeCSS instead of uncss by adding `tool: 'purgeCSS'` to the options.
765
767
 
766
- Minified:
768
+ You have to install `purgecss` in order to use this feature:
767
769
 
768
- ```html
769
- <a href="foo/bar/baz">bar</a>
770
+ ```bash
771
+ npm install --save-dev purgecss
772
+ # if you prefer yarn
773
+ # yarn add --dev purgecss
774
+ # if you prefer pnpm
775
+ # pnpm install --save-dev purgecss
770
776
  ```
771
777
 
772
- **With sub-directory**
778
+ ##### Options
773
779
 
774
- Configuration:
780
+ See [the documentation of PurgeCSS](https://www.purgecss.com) for all supported options.
775
781
 
782
+ PurgeCSS options can be passed directly to the `removeUnusedCss` module:
776
783
  ```js
777
784
  htmlnano.process(html, {
778
- minifyUrls: 'https://example.com/foo/baz/'
785
+ removeUnusedCss: {
786
+ tool: 'purgeCSS',
787
+ safelist: ['.do-not-remove']
788
+ }
779
789
  });
780
790
  ```
781
791
 
782
- Source:
783
-
784
- ```html
785
- <a href="https://example.com/foo/bar">bar</a>
786
- ```
787
-
788
- Minified:
789
-
790
- ```html
791
- <a href="../bar">bar</a>
792
- ```
792
+ The following PurgeCSS options are ignored if passed to the module:
793
793
 
794
- ### removeOptionalTags
795
- 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).
794
+ - `content`
795
+ - `css`
796
+ - `extractors`
796
797
 
797
798
  #### Example
798
-
799
799
  Source:
800
-
801
800
  ```html
802
- <html><head><title>Title</title></head><body><p>Hi</p></body></html>
801
+ <div class="b">
802
+ <style>
803
+ .a {
804
+ margin: 10px 10px 10px 10px;
805
+ }
806
+ .b {
807
+ color: #ff0000;
808
+ }
809
+ </style>
810
+ </div>
803
811
  ```
804
812
 
805
- Minified:
806
-
813
+ Optimized:
807
814
  ```html
808
- <title>Title</title><p>Hi</p>
815
+ <div class="b">
816
+ <style>
817
+ .b {
818
+ color: #ff0000;
819
+ }
820
+ </style>
821
+ </div>
809
822
  ```
810
823
 
811
- #### Notice
812
- 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:
813
-
814
- - `html`
815
- - `head`
816
- - `body`
817
- - `colgroup`
818
- - `tbody`
819
-
820
- ### normalizeAttributeValues
821
-
822
- Normalize casing of attribute values.
823
-
824
- 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.
825
-
826
- #### Example
827
-
828
- Source:
824
+ ## Miscellaneous
829
825
 
830
- ```html
831
- <form method="GET"></form>
826
+ ### custom
827
+ It's also possible to pass custom modules in the minifier.
828
+ As a function:
829
+ ```js
830
+ const options = {
831
+ custom: function (tree, options) {
832
+ // Some minification
833
+ return tree;
834
+ }
835
+ };
832
836
  ```
833
837
 
834
- Minified:
838
+ Or as a list of functions:
839
+ ```js
840
+ const options = {
841
+ custom: [
842
+ function (tree, options) {
843
+ // Some minification
844
+ return tree;
845
+ },
835
846
 
836
- ```html
837
- <form method="get"></form>
847
+ function (tree, options) {
848
+ // Some other minification
849
+ return tree;
850
+ }
851
+ ]
852
+ };
838
853
  ```
854
+
855
+ htmlnano's options are passed to your custom plugin by the second parameter `options`.