問題の概要
Hugoをバージョン0.18から0.19にアップデートした際、それまでは問題なく出力されていたloodashテンプレートの記号<%=
や%>
が、意図せずエスケープされてしまう問題が発生した。
HTMLテンプレート:
<script type="text/template" id="js-qiita-posts-tmpl">
<% list.forEach(function (item) { %>
<li class="qiita-tl__item">
<a class="qiita-tl__link" href="<%= item.url %>" target="_blank">
<span class="qiita-tl__stock" target="_blank">
<%= item.stock_count %> <span class="qiita-tl__stock-unit">STOCKS</span>
</span>
<h3 class="qiita-tl__title"><%= item.title %></h3>
<p class="qiita-tl__tags">
<% item.tags.forEach(function (tag) { %>
<span class="qiita-tl__tag"><%= tag.name %></span>
<% }); %>
</p>
</a>
</li>
<% }); %>
</script>
コンパイル結果:
<script type="text/template" id="js-qiita-posts-tmpl">
<% list.forEach(function (item) { %>
<li class="qiita-tl__item">
<a class="qiita-tl__link" href="<%= item.url %>" target="_blank">
<span class="qiita-tl__stock" target="_blank">
<%= item.stock_count %> <span class="qiita-tl__stock-unit">STOCKS</span>
</span>
<h3 class="qiita-tl__title"><%= item.title %></h3>
<p class="qiita-tl__tags">
<% item.tags.forEach(function (tag) { %>
<span class="qiita-tl__tag"><%= tag.name %></span>
<% }); %>
</p>
</a>
</li>
<% }); %>
</script>
関数を使って出力してみても直らない。
HTMLテンプレート:
{{ printf "<%= contribution %>" }}
{{ htmlUnescape "<%= contribution %>" }}
{{ htmlUnescape "<%= contribution %>" }}
コンパイル結果:
<%!=(MISSING) contribution %!>(MISSING)
<%= contribution %>
<%= contribution %>
質問してみた
仕方がないのでHugoのフォーラムで質問することにした:
Lodash template tags are escaped since Hugo 0.19 - support - Hugo Discussion
The lodash tags outside double quotes are escaped unexpectedly. I also tried these functions but the result isn't desirable. Currently, I haven't tried 0.20, because it hasn't appeared to Homebrew, yet.
My questions :
1. Is it a bug? or new expected behavior from 0.19?
2. Are there any solutions to keep lodash tags unescaped?
得られた回答
bepさんという、中心的な開発メンバーの方がすぐに返答してくれた。
It is probably changed behaviour in the Go version in use, you may search for it here:
https://github.com/golang/go/issues
まず、Go言語のアップデートにより、テンプレートエンジンの挙動が変わったことが原因だった。GoのIssueを見てみると、既に僕と同じ問題がバグとして報告されていた。
既に変更はマージされているため、この挙動はGo1.9以降で解消されるだろう。
では、今現在はどうすれば良いのか?
This escaping of HTML templates in Go's HTML templates are mostly security motivated -- which may not be an issue when you control the input yourself. In Hugo >= 0.20 you can switch to using Go's text template package per output format by defining isPlainText=true.
Hugoのバージョン0.20以降に追加された新しいオプションによって、エスケープの挙動を変えられるようになったとのことだ。これを設定すれば、Goのアップデートを待たずに問題を解消できる。
Hugo 0.20 での解消方法
まず、問題のEJSタグが含まれるファイルのパーシャルの拡張子を.ejs
に変更する。これは、Hugoの新しい仕様であるCustom Output Format
を使い、拡張子がejsのファイルには通常と違うoutputFormat
を定義するためだ。
mediaType
とoutputFormat
の追加はconfig.toml
へ以下を追記することによって行う。
[mediaTypes."text/template"]
suffix = "ejs"
[outputFormats.EJS]
mediaType = "text/template"
isHTML = true
isPlainText = true
最後に、パーシャルの読み込み時にsafeHTML
フィルターを適用する。これがないとうまくいかない。
{{ (partial "my-ejs-partial.ejs" .) | safeHTML }}
これによって問題を解決できた。
Custom Output Format
については下記のドキュメントを参照:
Hugo - Output Formats