HTMLプリプロセッサの出力をjs-beautifyで整形

PugやEJSなどのHTMLプリプロセッサはとても便利だが、出力後のHTMLファイルはインデントや改行などが汚いので、そのまま客先に納品するのがはばかられる。そこで、[js-beautify](https://www.npmjs.com/package/js-beautify)というパッケージを使ってインデントや空白をバッチで除去できるスクリプトを作成した。

## js-beautifyのインストール

`npm install --save-dev js-beautify`

## js-beautifyのオプション

### オプション一覧

**CLIオプション**

|オプション|説明|
|:---|:---|
|-f, --file|入力ファイルのパス。（'-'を渡すと標準入力を使用）|
|-r, --replace|整形後の出力を直接入力ファイルに上書く|
|-o, --outfile|出力ファイルのパス (デフォルトは標準出力)|
|--config|設定ファイルのパス|
|--type|動作モードの指定 [js&#124;css&#124;html]|
|-q, --quiet|標準出力へのログ出力を抑制|
|-h, --help|ヘルプを表示|
|-v, --version|バージョンを表示|

**HTML用整形オプション**

|オプション|説明|
|:---|:---|
|-s, --indent-size                |インデントサイズ [4]|
|-c, --indent-char                |インデントに使用する文字 [" "]|
|-t, --indent-with-tabs           |インデントにタブを使う。 -s と -c を上書く|
|-e, --eol                        |改行として使用する文字 (デフォルトは"\\n")|
|-n, --end-with-newline           |出力の最後に改行を出力する|
|-l, --indent-level               |インデントの初期サイズ [0]|
|-p, --preserve-newlines          |改行を維持する (--no-preserve-newlines disables)|
|-m, --max-preserve-newlines      |許容する連続改行数 [10]|
|-I, --indent-inner-html          |`<head>``<body>`要素をインデントする。デフォルトはfalse|
|-b, --brace-style                |[collapse&#124;expand&#124;end-expand&#124;none] [,preserve-inline] [collapse,preserve-inline]|
|-S, --indent-scripts             |[keep&#124;separate&#124;normal] ["normal"]|
|-w, --wrap-line-length           |行に含まれる文字数がこの数を越えたら改行を挿入する [250]|
|-A, --wrap-attributes            |属性を改行するかどうか [auto&#124;force&#124;force-aligned&#124;force-expand-multiline] ["auto"]|
|-i, --wrap-attributes-indent-size|この属性値がこの文字数を越えたときに改行する [indent-size] （wrap-attributesが"force-aligned"のときは無視する）|
|-U, --unformatted                |リフォーマットを適用しない要素のリスト(デフォルトはinline)|
|-T, --content_unformatted        |コンテンツにリフォーマットを適用しない要素のリスト(デフォルトはpre)|
|-E, --extra_liners               |終了タグの後に空白行を挿入する要素のリスト(デフォルトはbody,head,/html)|
|--editorconfig                   |EditorConfigを設定として使用する|

## js-beautifyの実行

デフォルト設定のままでもそこそこ綺麗になるが、以下の点が気に入らなかったので設定に反映。

* 改行が連続していてもそのまま保持される
  * ⇒`preserve-new-lines`と`max_preserve_newlines`をそれぞれ無効に
* `<a>``<span>``<svg>`などのタグが整形されない
  * ⇒`unformatted`オプションを絞る
* 属性が長いと改行されてしまう
  * ⇒`wrap-attributes`を無効に

### JavaScript版

* `targetDirs`配列内のディレクトリ直下から、拡張子が`.html`のファイルを走査して整形
 
```js:beautify.js
const fs = require('fs');
const beautify = require('js-beautify');

// HTMLファイルを検索するディレクトリ
const targetDirs = ['./dist/', './dist/subdir/'];

// 整形オプション
// https://www.npmjs.com/package/js-beautify
const beautifyOptions = {
  indent_size: 2,
  end_with_newline: true,
  preserve_newlines: false,
  max_preserve_newlines: 0,
  wrap_line_length: 0,
  wrap_attributes_indent_size: 0,
  unformatted: ['b', 'em']
};

targetDirs.forEach(targetDir => {

  fs.readdir(targetDir, (err, files) => {

    if (err) console.log(err);
    if (err) return;

    const htmls = files.filter(name => {
      return name.match(/\.html$/);
    });

    htmls.forEach(file => {

      fs.readFile(targetDir + file, 'utf8', (err, html) => {

        if (err) console.log(err);
        if (err) return;

        const result = beautify.html(html, beautifyOptions);

        fs.writeFile(targetDir + file, result, 'utf8', err => {
          if (err) console.log(err);
        });

      });

    });

  });

});
```

```bash:シェルから実行
node ./beautify.js
```

### シェルスクリプト版

* ローカルインストールの実行バイナリは`node_modules/.bin/`の中に作成されるので、それを使う
* `$dirs`配列内のディレクトリ直下から、拡張子が`.html`のファイルを走査して整形
* `*`を使うとファイルの一覧を取得できるので、それを`for`で回す

```bash:beautify.sh
#!/usr/bin/env bash
exe="node_modules/.bin/js-beautify"

# HTMLファイルを検索するディレクトリ
dirs=(\
  "dist/" \
  "dist/subdir/" \
)

function main() {
    for dir in ${dirs[@]}
    do
      for entry in "$dir"*.html
      do
        $exe "$entry" -r --preserve-new-lines false --max-preserve-newlines 0 --wrap-line-length 0 --wrap-attributes-indent-size 0 --unformatted 'b em'
      done
    done
}

main
```

```bash：シェルから実行
./beautify.sh
```