サマリ
Jekyllが生成するHTMLを圧縮(minify)したいことがあります。 そのような場合はjekyll-compress-htmlを試してみる価値があるかも知れません。
前提バージョン
ソフトウェア | バージョン | 備考 |
---|---|---|
jekyll | 3.8.3 | - |
jekyll-compress-html | 3.0.4 | - |
jekyll-compress-html
jekyll-compress-htmlはpure Liquidで書かれた圧縮したHTMLのjekyllのレイアウトです。 Gemやプラグインではなく、2つほどファイルを配置するだけでjekyllの出力をminifyできます。
以下ではjekyll-compress-htmlの使い方を説明します。
compress layoutの追加
compress.html
をjekyllの_layouts
以下に配置します。 compress.html
はjekyll-compress-htmlの最新のリリースから取得して下さい。
https://raw.githubusercontent.com/penibelst/jekyll-compress-html/master/site/_layouts/compress.html
jekyll-compress-htmlの3.0.4時点のcompress.html
は以下のとおりです。 見ての通り改行や不要な要素を取り除く複雑なLiquidであることがわかります。 なんだこのLiquidはっ! と思いますが、こういうものなのでこのまま利用させてもらいましょう。
---
---
{% capture _LINE_FEED %}
{% endcapture %}{% if site.compress_html.ignore.envs contains jekyll.environment %}{{ content }}{% else %}{% capture _content %}{{ content }}{% endcapture %}{% assign _profile = site.compress_html.profile %}{% if site.compress_html.endings == "all" %}{% assign _endings = "html head body li dt dd p rt rp optgroup option colgroup caption thead tbody tfoot tr td th" | split: " " %}{% else %}{% assign _endings = site.compress_html.endings %}{% endif %}{% for _element in _endings %}{% capture _end %}</{{ _element }}>{% endcapture %}{% assign _content = _content | remove: _end %}{% endfor %}{% if _profile and _endings %}{% assign _profile_endings = _content | size | plus: 1 %}{% endif %}{% for _element in site.compress_html.startings %}{% capture _start %}<{{ _element }}>{% endcapture %}{% assign _content = _content | remove: _start %}{% endfor %}{% if _profile and site.compress_html.startings %}{% assign _profile_startings = _content | size | plus: 1 %}{% endif %}{% if site.compress_html.comments == "all" %}{% assign _comments = "<!-- -->" | split: " " %}{% else %}{% assign _comments = site.compress_html.comments %}{% endif %}{% if _comments.size == 2 %}{% capture _comment_befores %}.{{ _content }}{% endcapture %}{% assign _comment_befores = _comment_befores | split: _comments.first %}{% for _comment_before in _comment_befores %}{% if forloop.first %}{% continue %}{% endif %}{% capture _comment_outside %}{% if _carry %}{{ _comments.first }}{% endif %}{{ _comment_before }}{% endcapture %}{% capture _comment %}{% unless _carry %}{{ _comments.first }}{% endunless %}{{ _comment_outside | split: _comments.last | first }}{% if _comment_outside contains _comments.last %}{{ _comments.last }}{% assign _carry = false %}{% else %}{% assign _carry = true %}{% endif %}{% endcapture %}{% assign _content = _content | remove_first: _comment %}{% endfor %}{% if _profile %}{% assign _profile_comments = _content | size | plus: 1 %}{% endif %}{% endif %}{% assign _pre_befores = _content | split: "<pre" %}{% assign _content = "" %}{% for _pre_before in _pre_befores %}{% assign _pres = _pre_before | split: "</pre>" %}{% assign _pres_after = "" %}{% if _pres.size != 0 %}{% if site.compress_html.blanklines %}{% assign _lines = _pres.last | split: _LINE_FEED %}{% assign _lastchar = _pres.last | split: "" | last %}{% assign _outerloop = forloop %}{% capture _pres_after %}{% for _line in _lines %}{% assign _trimmed = _line | split: " " | join: " " %}{% if forloop.last and _lastchar == _LINE_FEED %}{% unless _outerloop.last %}{{ _LINE_FEED }}{% endunless %}{% continue %}{% endif %}{% if _trimmed != empty or forloop.last %}{% unless forloop.first %}{{ _LINE_FEED }}{% endunless %}{{ _line }}{% endif %}{% endfor %}{% endcapture %}{% else %}{% assign _pres_after = _pres.last | split: " " | join: " " %}{% endif %}{% endif %}{% capture _content %}{{ _content }}{% if _pre_before contains "</pre>" %}<pre{{ _pres.first }}</pre>{% endif %}{% unless _pre_before contains "</pre>" and _pres.size == 1 %}{{ _pres_after }}{% endunless %}{% endcapture %}{% endfor %}{% if _profile %}{% assign _profile_collapse = _content | size | plus: 1 %}{% endif %}{% if site.compress_html.clippings == "all" %}{% assign _clippings = "html head title base link meta style body article section nav aside h1 h2 h3 h4 h5 h6 hgroup header footer address p hr blockquote ol ul li dl dt dd figure figcaption main div table caption colgroup col tbody thead tfoot tr td th" | split: " " %}{% else %}{% assign _clippings = site.compress_html.clippings %}{% endif %}{% for _element in _clippings %}{% assign _edges = " <e;<e; </e>;</e>;</e> ;</e>" | replace: "e", _element | split: ";" %}{% assign _content = _content | replace: _edges[0], _edges[1] | replace: _edges[2], _edges[3] | replace: _edges[4], _edges[5] %}{% endfor %}{% if _profile and _clippings %}{% assign _profile_clippings = _content | size | plus: 1 %}{% endif %}{{ _content }}{% if _profile %} <table id="compress_html_profile_{{ site.time | date: "%Y%m%d" }}" class="compress_html_profile"> <thead> <tr> <td>Step <td>Bytes <tbody> <tr> <td>raw <td>{{ content | size }}{% if _profile_endings %} <tr> <td>endings <td>{{ _profile_endings }}{% endif %}{% if _profile_startings %} <tr> <td>startings <td>{{ _profile_startings }}{% endif %}{% if _profile_comments %} <tr> <td>comments <td>{{ _profile_comments }}{% endif %}{% if _profile_collapse %} <tr> <td>collapse <td>{{ _profile_collapse }}{% endif %}{% if _profile_clippings %} <tr> <td>clippings <td>{{ _profile_clippings }}{% endif %} </table>{% endif %}{% endif %}
compressレイアウトを指定したレイアウトの定義
続いてcompress
を指定したレイアウトを定義します。
レイアウトを指定していないすべてのページでcompressを適用したいなら_layouts/default.html
として以下の内容を書きます。 他のレイアウトでも構いませんが、その場合はcompressを適用したいページのlayout:
で、明示的にcompress
を適用しているレイアウトを指定する必要があります。
このレイアウトではlayout: compress
がポイントです! これでこのレイアウトが適用されたページは先程配置したcompress.html
で出力前にレイアウトされ、HTMLでは不要な空白などが除去されるようになります。 この例では<html>
要素内に{{ content }}
を配置していますが、layout: compress
さえ指定していれば内容は任意です。
---
layout: compress
---
<html>
{{ content }}
</html>
jekyll-compress-htmlの設定
_config.yml
でcompress.html
の挙動を変更することができます。
改めて元のLiquidテンプレートを良く見ると、所々に分岐が書かれていることがわかります。 これが_config.yml
で挙動を切り替える処理です。
compress_html
以下に以下の値を設定できます。
compress_html:
clippings: []
comments: []
endings: []
ignore:
envs: []
blanklines: false
profile: false
startings: []
詳細な解説は公式の解説をご覧下さい。
私が試行錯誤した限り、おそらく大抵の用途を満たしつつ問題を起こさずにHTMLを圧縮する設定は以下のとおりです。
compress_html:
clippings: all
comments: all
endings: all
ignore:
envs: []
blanklines: true
profile: false
startings: []
この設定は以下の挙動になります。
- 空白を除去しても安全なすべての要素に囲まれた空白を除去する
- すべてのコメントを除去する
- 任意の終了タグをすべて除去する
- 環境変数によってcompressレイアウトを無効化(無視)しない
- 空白行を除去する
- プロファイルモードを無効化
- 任意の開始タグを除去しない
制限事項
jekyll-compress-htmlには制限事項がドキュメントに明記されています。 ここだけはきちんと翻訳しておきます。
- Whitespaces inside of the textarea element are squeezed. Please don’t use the layout on pages with non-empty textarea.
- Inline JS can become broken where // comments used. Please remove the comments or change to /**/ style.
- Invalid markup can lead to unexpected results. Please make sure your markup is valid before.
- textarea要素の内部の空白が除去されます。textareaが空でないページでcompressレイアウトを使用しないでください。
- インラインJavaScriptは
//
のコメントが使用されている箇所で破壊されることがあります。コメントを削除するか/ ** /
スタイルのコメントに変更してください。 - 不正なマークアップは予期しない結果を招く恐れがあります。マークアップが正しいことを確認してください。
まとめ
jekyll-compress-htmlはLiquidだけでminifyを実現していて、なかなか興味深いレイアウトです。 Gemも必要ありませんので、必要に応じてレイアウトをコピペするだけで、Jekyllで生成されるHTMLをminifyできます。 defaultのレイアウトにしてしまえば勝手にminifyされますので、1バイトでもHTMLの容量を縮めたいときには重宝します。
なおこのブログはjekyll-compress-htmlでminifyしてあります。