【備忘録】HTMLMinifierの全オプションについて調査した

# 概要
[HTMLMinifier](https://github.com/kangax/html-minifier)は細かくカスタマイズ可能なJavaScriptベースのHTML軽量化ツールで、現在まで1,257,900ものリポジトリで使われてきました。
[HTML Webpack Pluginのminifyオプション](https://github.com/jantimon/html-webpack-plugin/blob/17fbb30737073f1c4b91eac5ea7babbc789f393b/index.js#L118~L126)も、[Nuxt.jsのbuildプロパティ（html.minify）](https://github.com/nuxt/nuxt.js/blob/c17cb24aaa4445c6055b1532cee341d4f814e099/lib/common/options.js#L211~L230)も、内部的にHTMLMinifierを使っています。

ここではオプションについて備忘として残しておきたいと思います。

## インストール
```bash
$ npm -D i html-minifier
$ npx html-minifier -V
4.0.0
```

## CLI経由で実行する場合

### -o、--output <file>

出力ファイルを指定します。

```bash
$ npx html-minifier before.html -o after.html
```

### --input-dir <dir>、--output-dir <dir>
入出力するディレクトリを指定すると、入力したディレクトリ内のHTMLファイルについて処理し、出力先ディレクトリに書き出します。複数ファイルでも複数フォルダがあっても、ディレクトリを維持したまま書き出ししてくれます。

```bash
$ npx html-minifier --input-dir src --output-dir dist
```

### --file-ext <拡張子>
読み込む拡張子を指定します（例：htm）。

```bash
$ npx html-minifier --input-dir src --output-dir dist --file-ext htm
```

### -c、--config-file
JSON形式の設定ファイルを指定できます。
[公式のサンプル](https://github.com/kangax/html-minifier/blob/gh-pages/sample-cli-config-file.conf)があるので参考に。

```bash
$ npx html-minifier before.html -o after.html -c setting/config.js
```

## Node.js経由で実行する場合

実際の利用シーンを考え、Node.jsから実行する方法で各種オプションを確認していきます。
下記のようなJSファイルを```$ node ***.js```にて叩くものとします。
オプションの並びは公式のリファレンスに沿ったものとなります（アルファベット順）。

```javascript
const minify = require('html-minifier').minify;
const before = `
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
...
`;
const after = minify(before, {
  collapseWhitespace: true
});
console.log(after);
```

### caseSensitive（--case-sensitive）
（--XXX）内はCLI経由時の引数です（以下略）。

```javascript
caseSensitive: true
```
属性の大文字と小文字を区別します（デフォルトでfalse）。
属性値は通常minifierにかけるとオプション無しでも小文字に変換されますが、

```html
<a href="#" onClick="test()">aaaa</a>
▼
<a href="#" onclick="test()">aaaa</a>
```

これを変更しないようします。

```html
<a href="#" onClick="test()">aaaa</a>
```

### collapseBooleanAttributes（--collapse-boolean-attributes）
```javascript
collapseBooleanAttributes: true
```
checked="checked"、disabled="disabled"などのブール属性から、属性値を除外します（デフォルトでfalse）。

```html
<input type="checkbox" checked="checked">
▼
<input type="checkbox" checked>
```

### collapseInlineTagWhitespace（--collapse-inline-tag-whitespace）

```javascript
collapseInlineTagWhitespace: true
```
インラインタグの周囲の空白を削除します（デフォルトでfalse）。
※ collapseWhitespace: true（--collapse-whitespace）と共に使用

```html
<p>あいうえお   <span>かきくけこ</span>   </p>
▼
<p>あいうえお<span>かきくけこ</span></p>
```

### collapseWhitespace（--collapse-whitespace）

```javascript
collapseWhitespace: true
```
タグ間の空白を削除します（デフォルトでfalse）。

```html
<div> <p>  あいうえお </p>  </div>
▼
<div><p>あいうえお</p></div>
```

### conservativeCollapse（--conservative-collapse）

```javascript
conservativeCollapse: true
```
タグ間の空白を削除するが、半角スペースを空けます（デフォルトでfalse）。
※ collapseWhitespace: true（--collapse-whitespace）と共に使用

```html
<p>あいうえお   <span>かきくけこ</span>   </p>
▼
<p>あいうえお <span>かきくけこ</span> </p>
```

### continueOnParseError（--continue-on-parse-error）
デフォルトではHTMLのパースエラーが発生したらエラーを表示して処理を中断するが、これを無効にする（デフォルトでfalse）。

```html
<p>あいうえお<
▼
Error: Parse Error: <
```
となるが、下記を設定することで

```javascript
continueOnParseError: true
```

```html
<p>あいうえお<
</p>
```

### customAttrAssign（--custom-attr-assign） [値]

```javascript
customAttrAssign: [/\?=/]
```
独自のタグによる条件式をサポートすることを可能にする正規表現の配列（デフォルト値```[ ]```）。

```html
<div flex?="{{mode != cover}}"></div>
```

### customAttrCollapse（--custom-attr-collapse） <値>

```javascript
customAttrCollapse: /ng\-class/
```
例えば「ng-class」のような独自のカスタム属性を正規表現で指定することで、軽量化できるようにします（デフォルト値なし）。

```html
<p ng-class="
{
one:(flgOne && !flgTwo),
two:flgTwo
}
">あいうえお</p>
▼
<p ng-class="{one:(flgOne && !flgTwo),two:flgTwo}">あいうえお</p>
```

### customAttrSurround（--custom-attr-surround） [値]
独自のタグで囲まれた条件式をサポートする正規表現の配列（デフォルト値```[ ]```）。

```html
<input {{#if value}}checked="checked"{{/if}}>
```

デフォルトでは上記のような独自のタグで囲まれた条件式はパースエラーとなりますが、これを許可させます。正規表現で入り・閉じの配列を指定する必要があります。

```javascript
customAttrSurround: [[/\{\{#[^}]+\}\}/, /\{\{\/[^}]+\}\}/]]
```

### customEventAttributes（--custom-event-attributes） [値]
```javascript
customEventAttributes: [/^ng-click$/]
```
例えば「ng-click」のような独自のイベント属性を正規表現の配列で設定することで、minifyJSで取り扱えるようします（デフォルト値は```[ /^on[a-z]{3,}$/ ]```）。
※ minifyJS: true（--minify-js）と共に使用

```html
<button
ng-click="
count = count + 1
">
+1
</button>
▼
<button ng-click="count+=1">
+1
</button>
```

### decodeEntities（--decode-entities）
```javascript
decodeEntities: true
```
文字実体参照をデコードします（デフォルトでfalse）。

```html
<a href="/?foo=1&amp;bar=2">&copy;テスト</a>
▼
<a href="/?foo=1&bar=2">©テスト</a>
```

### html5（--no-html5）
HTML5仕様に従って入力を解析します（「html5」がデフォルトでtrue）。

### ignoreCustomComments（--ignore-custom-comments） [値]
```javascript
ignoreCustomComments: [/test_comment/, /ignore_comment/]
```
残したい特定のコメントを正規表現の配列で指定（デフォルト値```[ /^!/ ]```）。
※ removeComments: true（--remove-comments）と共に使用

```html
<!-- test_comment -->残すコメント1<!-- /test_comment -->
<!-- ignore_comment -->残すコメント2<!-- /ignore_comment -->
<!-- other_comment -->消えるコメント<!-- /other_comment -->
▼
<!-- test_comment -->残すコメント1<!-- /test_comment -->
<!-- ignore_comment -->残すコメント2<!-- /ignore_comment -->
消えるコメント
```

### ignoreCustomFragments（--ignore-custom-fragments） [値]
特定のタグ内では処理を無視させたい場合、タグを示す正規表現を配列で指定します（デフォルト値は```[ /<%[\s\S]*?%>/, /<\?[\s\S]*?\?>/ ]```）。

※ デフォルトの設定のおかげで、HTMLファイル内のPHPの記述（<?php...?>）などは、その中身の処理が無視されます。

例えば、Vue.jsの&lt;template&gt;要素などはタグの体をなしているため、

```html
<template v-if="ok">
  <h1>Title</h1>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
</template>
```

```collapseWhitespace: true```とすると下記のように空白が取り除かれますが、

```html
<template v-if="ok"><h1>Title</h1><p>Paragraph 1</p><p>Paragraph 2</p></template>
```

```javascript
ignoreCustomFragments: [/<template[\s\S]*?\/template>/]
```

を足すことで、&lt;template&gt;タグ内を無視してくれます。

### includeAutoGeneratedTags（--no-include-auto-generated-tags）
HTMLパーサによって生成されたタグを挿入します（「includeAutoGeneratedTags」がデフォルトでtrue）。
デフォルトでは下記のようにタグに不備がある時、タグを補完してくれますが、

```html
<p><span>aaaa</p>
▼
<p><span>aaaa</span></p>
```

```javascript
includeAutoGeneratedTags: false
```
とすることでこの機能をOFFにします。

```html
<p><span>aaaa</p>
```

### keepClosingSlash（--keep-closing-slash）
空要素の末尾のスラッシュを保持します（デフォルトでfalse）。
デフォルトで空要素の閉じタグは削除されるが、

```html
<hr />
▼
<hr>
```

```javascript
keepClosingSlash: true
```

とすることで、保持されます。

```html
<hr/>
```

### maxLineLength（--max-line-length） <値>
最大行長を指定します（デフォルト値：0）。

```javascript
maxLineLength: 1
```

```html
<p>あいうえお</p>
<p>かきくけこ</p>
▼
<p>
あいうえお
</p>
<p>
かきくけこ
</p>
```

### minifyCSS（--minify-css）
```javascript
minifyCSS: true
```
「clean-css」を使ってCSSを軽量化します（デフォルトでfalse）。

```html
<style>
*{
  margin: 0;
  padding: 0;
}
</style>
▼
<style>*{margin:0;padding:0}</style>
```

### minifyJS（--minify-js）
```javascript
minifyJS: true
```
「uglify-js」を使ってJavascriptを軽量化します（デフォルトでfalse）。

```html
<script>
console.log('test');
</script>
▼
<script>console.log("test")</script>
```

### minifyURLs（--minify-urls） <値>
```javascript
minifyURLs: 'https://aaaaaaaaaaaa.com/'
```
「relateurl」を使ってURLを軽量化します（デフォルトでfalse）。

```html
<a href="https://aaaaaaaaaaaa.com/test/test2/index.html">TEST</a>
<a href="https://aaaaaaaaaaaa.com/test3.html">TEST</a>
▼
<a href="test/test2/">TEST</a>
<a href="test3.html">TEST</a>
```

### preserveLineBreaks（--preserve-line-breaks）
タグ間に改行が含まれる場合、詰めます（デフォルトでfalse）。
※ collapseWhitespace: true（--collapse-whitespace）と共に使用

```javascript
preserveLineBreaks: true
```

```html
<p>あいうえお


<span>かきくけこ</span></p>
▼
<p>あいうえお
<span>かきくけこ</span></p>
```

### preventAttributesEscaping（--prevent-attributes-escaping）
属性値のエスケープを防止します（デフォルトでfalse）。

通常、下記のようになるのを、

```html
<a href='#'>link</a>
▼
<a href="#">link</a>
```

```javascript
preventAttributesEscaping: true
```

とすることで

```html
<a href='#'>link</a>
```
のままにします。

### processConditionalComments（--process-conditional-comments）
条件付きコメントの内容を処理する（デフォルトでfalse）。
例えば、```collapseWhitespace: true```のみだと条件付きコメント内では軽量化が効きませんが、

```html
<!--[if lt IE 9]>
<p>あいうえお</p>
<p>かきくけこ</p>
<![endif]-->
```

```javascript
processConditionalComments: true
```

とすることで

```html
<!--[if lt IE 9]><p>あいうえお</p><p>かきくけこ</p><![endif]-->
```
のように条件付きコメント内でも軽量化が効くようになります。

### processScripts（--process-scripts） [値]
&lt;script&gt;要素のtypeに対応する文字列を配列で指定します（デフォルト値```[]```）。

```html
<script type="application/ld+json">
{
  "@context": "http://schema.org/",
  "@type": "####"
}
</script>
```

などは```collapseWhitespace: true```とした場合、&lt;script&gt;内では軽量化が効きませんが、

```bash
$ npx html-minifier before.html -o after.html --process-scripts "[\"application/ld+json\"]" --collapse-whitespace
```

とすることで&lt;script&gt;内でも軽量化が効くようになります。

```html
<script type="application/ld+json">{ "@context": "http://schema.org/", "@type": "####" }</script>
```

### quoteCharacter（--quote-character） <値>
```javascript
quoteCharacter: "'"
```
属性値に使用するクォートの種類（「'」または「"」）を指定します。（デフォルト値なし）。

```html
<a href="#">link</a>
▼
<a href='#'>link</a>
```

### removeAttributeQuotes（--remove-attribute-quotes）
```javascript
removeAttributeQuotes: true
```
可能であれば、属性を囲むクォートを削除します（デフォルトでfalse）。

```html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
▼
<!DOCTYPE html>
<html lang=ja>
<head>
<meta charset=UTF-8>
```

### removeComments（--remove-comments）
```javascript
removeComments: true
```
HTMLコメントを削除します（デフォルトでfalse）。
<!--コメント-->が消えます。

### removeEmptyAttributes（--remove-empty-attributes）
```javascript
removeEmptyAttributes: true
```
空の属性を削除します（デフォルトでfalse）。

```html
<p id="" style="" title="">foo</p>
<img src="img.png" alt="" height="" id="" class="">
▼
<p>foo</p>
<img src="img.png" alt="" height="">
```

消える一覧は下記。altなどはちゃんと残してくれます。
class / id / style / title / lang / dir / onfocus / onblur / onchange / onclick / ondblclick / mousedown / mouseup / mouseover / mousemove / mouseout / keypress / keydown / keyup

### removeEmptyElements（--remove-empty-elements）
```javascript
removeEmptyElements: true
```
空のコンテンツを持つ全ての要素を削除します（デフォルトでfalse）。

```html
<div></div>
<p></p>
▼
```

### removeOptionalTags（--remove-optional-tags）

```javascript
removeOptionalTags: true
```
不要なタグを削除します（デフォルトでfalse）。
&lt;html&gt;&lt;/body&gt;&lt;/html&gt;など、レンダリングに不要なタグを削除します。

```html
<!DOCTYPE HTML>
<html>
<head>
<title>Hello</title>
</head>
<body>
<p>Welcome to this example.</p>
</body>
</html>
▼
<!DOCTYPE HTML>

<head>
<title>Hello</title>
</head>
<body>
<p>Welcome to this example.</p>
```

### removeRedundantAttributes（--remove-redundant-attributes）
```javascript
removeRedundantAttributes: true
```
値がHTML仕様のデフォルトと一致するとき、属性を削除します（デフォルトでfalse）。

```html
<input type="text">
▼
<input>
```

### removeScriptTypeAttributes（--remove-script-type-attributes）

```javascript
removeScriptTypeAttributes: true
```
スクリプトタグからtype="text/javascript"を削除します。他のtype属性値は残ります（デフォルトでfalse）。

```html
<script type="text/javascript" src="script.js"></script>
▼
<script src="script.js"></script>
```

### removeStyleLinkTypeAttributes（--remove-style-link-type-attributes）

```javascript
removeStyleLinkTypeAttributes: true
```
スタイルタグとリンクタグからtype="text/css"を削除します。他のtype属性値は残ります（デフォルトでfalse）。

```html
<link rel="stylesheet" type="text/css" href="style.css">
▼
<link rel="stylesheet" href="style.css">
```

### removeTagWhitespace（--remove-tag-whitespace）
```javascript
removeTagWhitespace: true
```
可能な限り属性間のスペースを削除します（デフォルトでfalse）。

```html
<img src="/img.png" alt="" width="100" height="50">
▼
<img src="/img.png"alt=""width="100"height="50">
```

### sortAttributes（--sort-attributes）

```javascript
sortAttributes: true
```
属性をソートします（デフォルトでfalse）。

```html
<img src="/img.png" alt="" width="100" height="50">
▼
<img alt="" src="/img.png" height="50" width="100">
```

### sortClassName（--sort-class-name）

```javascript
sortClassName: true
```
class名をアルファベット順でソートします（デフォルトでfalse）。

```html
<p class="red large test2 test1 aiueo">あいうえお</p>
▼
<p class="aiueo large red test1 test2">あいうえお</p>
```

### trimCustomFragments（--trim-custom-fragments）
ignoreCustomFragmentsの周囲の空白を削除します（デフォルトでfalse）。
※ ignoreCustomFragments（--ignore-custom-fragments）と共に使用

```javascript
collapseWhitespace: true,
ignoreCustomFragments: [/<template[\s\S]*?\/template>/]
```

とすると下記のようになりますが、

```html
<template v-if="loginType === 'username'">
  <label>Username</label>
  <input placeholder="Enter your username">
</template> <template v-else>
  <label>Email</label>
  <input placeholder="Enter your email address">
</template>
```

```javascript
trimCustomFragments: true
```

を足すことで詰まります。

```html
<template v-if="loginType === 'username'">
  <label>Username</label>
  <input placeholder="Enter your username">
</template>
<template v-else>
  <label>Email</label>
  <input placeholder="Enter your email address">
</template>
```

### useShortDoctype（--use-short-doctype）
```javascript
useShortDoctype: true
```
DOCTYPE宣言を小文字にします（デフォルトでfalse）。

```html
<!DOCTYPE html>
▼
<!doctype html>
```

## ソース内を部分的に除外したい場合
```<!-- htmlmin:ignore -->```で除外したい箇所を囲みます。

```html
<!-- htmlmin:ignore -->
<p>Hello world!<br>
Hello world!</p>
<!-- htmlmin:ignore -->
<p>Hello world!<br>
Hello world!</p>
```

として```collapseWhitespace: true```のみ設定して実行すると下記の通り。

```html
<p>Hello world!<br>
Hello world!</p>
<p>Hello world!<br>Hello world!</p>
```

## 参考サイト
[kangax/html-minifier: Javascript-based HTML compressor/minifier (with Node.js support)](https://github.com/kangax/html-minifier)
[Experimenting with HTML minifier — Perfection Kills](http://perfectionkills.com/experimenting-with-html-minifier/)
[HTML minifier revisited — Perfection Kills](http://perfectionkills.com/html-minifier-revisited/)
[HTML minifier](https://kangax.github.io/html-minifier/)
[API: build プロパティ - Nuxt.js](https://ja.nuxtjs.org/api/configuration-build#html-minify)
[html-webpack-plugin - npm](https://www.npmjs.com/package/html-webpack-plugin)