整形はコード作業の中で、ないことに気づくまで誰も気にしない部分だ。壁のように固まった HTML は一語の長い単語のように読まれ、インデントが揃った瞬間、同じ内容が木構造になる。フォーマッタはマークアップの意味を変えず、見ることのコストだけを変える。デバッグ時間はほぼ全部、そのコストに消えている。HTML フォーマッタはブラウザ内で完結する。マークアップを貼り付け、モードを選び、結果が隣のパネルに出る。アップロードはなく、内容はタブの外に出ない。API キーや社内ホスト名、顧客データが入ったページを直すなら、それを第三者サービスに貼らなくていいのは重要だ。
ツールは二つの方向に働き、答える質問が異なる。フォーマットモードは、空白が正直になったときこの構造はどう見えるかと問う。ミニファイモードは、挙動を変えずにこのファイルをどこまで小さくできるかと問う。以下では両パスの仕組み、458 文字の実際のページが各モードでどうなるか、ミニファイが触れない部分と消す部分、インデント設定とインライン要素リストが出力をどう形づくるか、そして同族のフォーマッタの中でこのツールがどの位置に座るかを順に扱う。
HTML フォーマッタの仕組み:2 つのモード、1 つの貼り付け
両モードは同じ入力文字列を読み、結果は同じくテキストとして出力パネルに入る。違うのは適用される変換だ。フォーマットモードはマークアップを js-beautify の HTML エンジンに渡し、固定のオプションセットを使う。インデントは 2 空白か 4 空白、行幅の折り返しなし、末尾改行なし、インライン要素の内側は縮さない、さらに内容を複数行に分けない要素のリストを持つ。ミニファイモードは別につくられたパイプラインを回す。まず script、textarea、pre の全ブロックを文書から引き抜いて横に置き、HTML コメントを除去し、空白の連続を 1 つのスペースに畳み、タグの接合部の前後のスペースを削り、最後に守られたブロックを 1 バイト単位で戻す。
この順序が設計の全部だ。空白の畳み込みはマークアップに対して安全で、style タグの中の CSS に対しても安全だ。両方の言語で、スペースと改行の連続はスペース 1 つと同じ意味だからだ。pre タグの内側では安全ではない。そこでは空白こそが内容だからだ。script 内でも安全ではない。ミニファイは喜んで文を貼りつけてしまう。先に引き抜いてから戻す方式は、文書の残りには積極的なパスを回しつつ、その 3 領域だけが入ったときと全く同じ形、改行込みで出てくることを可能にする。
標準ケース:458 文字のページが 1 行 281 文字になる
手書きのページを取ろう。doctype、charset を持つ head、title、小さな style ブロック、そして見出しと段落とボタンを持つカードを置く body。実際に人が書くように、打ち込むインデントと残ったドラフトのコメント付きで、458 文字・28 行になる。フォーマットモードを回すと、390 文字・27 行で戻ってくる。深く入りすぎた title と見出しはそれぞれの行に収まり、構造は一定の 2 空白リズムに再固定され、ドラフトのコメントはその場で残る。チームの標準が 4 空白なら、同じパスで同じ行数の文書が、広いインデントで返ってくる。
同じ 458 文字のページにミニファイモードを回すと、結果は 1 行の 281 文字で、38.6 % の削減になる。ドラフトのコメントも消え、インデントも消え、タグ間のスペースも消える。残るのは同じ文書だ。同じ要素、同じ属性、同じテキスト、同じ CSS。手計算したくないなら、どちらかのパネルの長さを読んでもらえ。文字カウントは前後の数字を並べてくれ、プルリクエストで引用できる数字がそのまま出る。
ミニファイが守るもの・消すもの
折り畳みの前に引き抜かれるブロックは 3 種類:script、textarea、pre。2 行の代入を持つ script が入ったページは、ミニファイ後にも両行の改行がそのまま残る。開きタグから閉じタグまでのブロック全体が、出力へそのまま運ばれるからだ。二重スペースと改行でコード一覧として描画される pre ブロックも、全文字が守られる。保護はバイト単位であり、行単位ではない。手違いに見えて実は荷を支える空白、例えば行内要素を隣とくっつけないためのスペースも、これで守られる。
保護される領域は境界であり、目的地ではない。script 内側の JavaScript が守られるのは HTML パスにおいてで、HTML パスが約束できるのはそこまでだ。本物の JS ミニファイは構文の話で、死んだ文、余分な括弧、安全な識別子の名前変更まで消せる。それをやるのは、パターンマッチングではなくコードを解析するエンジンを持つJavaScript ミニファイアだ。style タグは境界の反対側にいる。中身の CSS は保護されず、空白処理のみを受ける。ルールの構造は保たれ、空白は狭くなり、結果は依然として有効な CSS だ。スタイルシートがもう一段深いパスを必要とするなら、CSS ミニファイアが引き受ける。このエンジンは、セレクタと宣言の間のどのスペースが荷を支え、どのスペースが純粋なノイズかを分かっていて、CSS に対応した畳み込みを行う。
これらのブロックの外側は全部が完全な処理を受ける。知る価値のある詳細が 2 つある。HTML コメントは完全に削除され、コメント除去は 2 回走る。空白折り畳みの前に 1 回、折り畳みの後に 1 回だ。折り畳みが二つの半端なコメントを近づけ、折り畳み後の形でのみ存在する 1 つのコメントを作ることもあるからだ。タグの接合部の前後のスペース、つまり閉じ括弧と次の開き括弧の間の隙間は消え、出力は途切れのない 1 行のアンブラケットになる。予想できる癖が 1 つある。守られたブロックがあった場所に、スペースが 1 つ残ることがある。折り畳みが占める記号の周りで起きたからで、本当のタグの周りで起きたわけではない。何も壊れない。ファイルは依然として有効だ。ただその場所だけ、行の他の部分ほど締まりがない。
フォーマットモード:インデント、行幅、インライン要素リスト
ユーザーが変える設定は 1 つだけだ:インデントの幅。既定は 2 空白、代わりに 4 空白。コメント 1 行と小さな script を持つ 197 文字の 5 行のサンプルでは、2 空白のパスは 244 文字・18 行を返し、4 空白のパスは同じ 18 行で 278 文字を返す。同じ構造、同じ行数、広い横方向の余白。これはチームの慣例であり、正誤の話ではない。行の折り返しは意図的に無効で、長い属性の列が 1 行に残る。恣意的な桁数で折られることはない。出力は最後のタグが閉じたところで終わり、末尾改行は足されない。
より微妙な設定はインラインとして扱う要素のリストだ:a、span、td、th、small、pre、code。これらのタグでは、エンジンは開きと閉じの括弧の間に改行を入れない。文の途中で 1 語を包むリンクは 1 行に残り、表のセルの内容がページ全体に散らばることもない。文章の多いページの整形結果が、手で揃えたのと同じように読めるのはこのリストのおかげだ。ブロック要素は積み上がり、インライン要素は流れ、文の真ん中に改行が打ち込まれることはない。
隣のフォーマット:JSON、XML、それぞれの位置
HTML はマークアップ言語の中で最も寛容で、だからこそ最も空白を乱用される。JSON は真逆で、余分なカンマや末尾のスペースが 1 つ入るだけで壊れる。そのため専用ツールがあり、検証器は行番号と理由を伝えてくれる。JSON フォーマッタは同じく貼り付けられた塊を読み、整った形で返すか、はっきり拒むかのどちらかだ。直している HTML が script タグの中に設定オブジェクトを埋めているなら、これが必要な相棒になる。XML はより厳しい従兄弟で、括弧の不一致はブラウザの推測ではなく硬いエラーだ。XML フォーマッタは同じフォーマットとミニファイの形式で、その一族の文書に対応する。
仕分けの意義は、各フォーマットが本当にその言語を話すエンジンを得ることだ。HTML は構造の並べ替え、CSS は CSS を理解したエンジン、script は JS のパーサ。4 つのツールを 1 つのページに回すのは、ビルドシステムがツールチェーンで行う同じ分業で、ビルドだけが無い。文書を整形し、ペイロードを圧縮し、結果を出す。HTML フォーマッタはその真ん中で構造の読みやすさを保つ役であり、汎用ミニファイが最初に壊す部分でもある。汎用ミニファイはファイルサイズを最適化するが、まだ編集している最中だからだ。
公開の前に出力を読む
出力パネルは审阅面のすべてなので、レビュアーと同じ読み方をする価値がある。フォーマットモードでは先に構造を見る。ブロックの入れ子是期待通りか、インライン要素リストが文章の読みやすさを保つか、2 空白のリズムで逆に見えにくくなった要素はないか。ミニファイが消すコメントは、二つのモードがファイルの中身について唯一意見を分ける場所だ。そのコメントが次の人のために荷を支えているなら、残すべきはフォーマットモードの版だ。
ミニファイモードの审阅は短く機械的だ。ファイルは 1 行で、タグは連続で、守られた領域は生きていること:出力を開いて script を探し、改行がまだあることを確かめる。納品物がマークアップではなく素の文章なら、HTML → テキストがタグを剥がして読みやすい形を返す。成果物がドキュメントへの貼り付けで、デプロイではないなら、これが正しい一手だ。ページを動かす設定がマークアップの隣の YAML ファイルに置かれているなら、YAML フォーマッタが同じ扱いをする。貼り付け、モードを選ぶ、出力を読む、コピーして出す。ループの全体が、そのファイルを持つタブの中で、最初から最後まで完結する。