Hugo. フロントマター(Front Matter)を書きたくないけどフラグを立てたいとき

#hugo

Hugo で、ショートコード内で立てたフラグを head タグ内で読み込んでほしいけれど、 md ファイルのフロントマターを追加であれこれ書きたくないとき。

例えば、特定のページで Youtube の動画を貼り付けるために、ショートコードを使っていたりする場合。クリックされたときに始めて iframe をロードするような JavaScript や、そこでしか使われない CSS を別のファイルにしておくと無駄がない。そういうときに、フロントマターを使えば、個別に設定できる。

しかし、フロントマターに書くのがめんどくさい、うっかり忘れてしまうということもある。だったら問答無用で全ページで読み込んでしまえば間違いがない。使われない CSS や JS のコードを全ページで読み込んだところで個人ブログ程度の規模であればたかが知れているのでさほど影響はないかもしれない。しかし、なんとなく気持ち悪い。知っていて放置するのは気が引ける。

じゃあ、ショートコードの中に CSS も JS のコードも書けばいいじゃないかということになるかもしれない。しかし、これも無駄が多い。1つのページで複数の動画を貼った場合、 body の中に 何度も同じ内容の style が入ることになる。JS もいらない心配が増える。だったら問答無用で全ページの head内で読み込ませるほうがまだましな気がする。

ではどうすればいいのだろうか。

解決策: ショートコード内でフラグを立てる

ショートコードの中でフラグを立てて、head 内で読み込ませる。ということをやりたい。そうすれば、ショートコードを使ったページではそれ用の CSS や JavaScript のファイルを読み込んでくれるようになるだろう。いちいちフロントマターに書かなくても、「それをやった」ということが勝手に反映される。二度手間を防げる。ショートコードを削除するときも、それ以外に何かしなくてはならないということもない。

フラグは “hasYouTube” とでもしておく。セットするにはショートコードの中で {{ .Page.Store.Set "hasYouTube" }} と書く。そういう仕組みが Hugo にはある。

そうすれば、head タグの中で {{ if .Page.Store.Get "hasYouTube }} というような条件分岐に使うことができる。Set と Get でわかりやすい。

レンダリング順と Store の値が確定するタイミング

ところがこれはそのままだとうまくいかない。head の閉じタグの直前の JS の読み込みはできるのに、CSS のところがうまくいかない。

なぜなら、ショートコードを読み込む前に .Content の読み込みが始まるから。つまり、“hasYouTube” というフラグが立っていない状態でレンダリングが始まってしまうらしい。

ショートコードは .Content の中で読み込まれるわけだから当たり前っちゃ当たり前なのだろうが、困る。もっぱら CSS 関係の何かはショートコードを読む前に処理を終わらせようとするらしい。.Content に対してスタイルを当てるのだからこれもまた当然だと思う。

何度試しても、ショートコード内でフラグを立てたはずの JS はきちんと head の中に記述されているが、CSS は読み込まない。JS の場合はすべての記述が終わった後にしれっと head 内に挿入したとしても動くから問題ないとされているのだろうか。とにかく、CSS をどうにかして確実に head 内で読み込ませたい。

{{ $noop .Content }}

そこで、{{ $noop .Content }} とhead タグの中の、フラグを読み込んでほしい位置よりも前に書いておく。使わない変数なので名前は何でも良い。何はともあれそこで一回読み込んでくれということだ。これでやりたいことができた。

ショートコード

なんらかのコード
...
{{ .Page.Store.Set "hasYouTube" }}

head

<head>
<meta charset="UTF-8">
...
色々なコード
...
{{ $noop .Content }}
{{ if .Page.Store.Get "hasYouTube" }}
    <link href="/css/youtube-lite.css" rel="stylesheet">
{{ end }}
...
色々なコード
...
{{- if .Page.Store.Get "hasYoutube" }}
    <script defer src="{{- (resources.Get "js/youtube-lite.js" | resources.Minify).RelPermalink }}"></script>
{{- end }}
</head>

フロントマターで愚直にやる場合

フロントマターに書くほうが、仕組みとしてはまっとうな気がする。最初から用意されている仕組みであり、かつそれをやれば絶対確実なのだから。ただし、その場合はテキストエディタで保存のときに特定の文字列を見つけて youtube: true みたいなものを自動で追加するようにしておきたい。フォーマッターの import 忘れ防止みたいな感じのことをやれば良い。

と、思ったけど、こういう小さい追加処理は際限なく増加してエディタの設定が膨れ上がりそうな気もするので迷う。

目次なんかも似たようなパターンになりそう。目次を入れたいページと入れなくていいページを分けたいなどと言い出すと途端にめんどくさいことになる。