Hugoのrender-linkでURLエンコードしないリンクを挿入する
render-linkフックを使用している場合に選択的にパーセントエンコードを施さないリンクも作成する方法
Created at

869 Words
⚠️

Hugo v0.62.0以降では、マークダウンからHTMLを生成する際にaタグやimgタグのフォーマットをユーザーがカスタマイズすることができる。
公式リファレンス の通りに用意している場合次のようなrender-linkフックになる。

<a
  href="{{ .Destination | safeURL }}"{{ with .Title}} title="{{ . }}"{{ end }}
  {{ if strings.HasPrefix .Destination "http" }} target="_blank" rel="noopener"{{ end }}>
    {{ .Text | safeHTML }}
</a>

.Destinationはリンク先のURLなのだが、その内容はパス部分にパーセントエンコーディングが施されたものになる。

[link](https://hoge.com/hoge#foo(bar))

<a href="https://hoge.com/hoge#foo%28bar%29" target="_blank" rel="noopener">link</a>

通常はこれで問題ないのだが、困ったことにリンク先によっては正しく遷移できない場合がなくもない。

パーセントエンコーディングを回避する

パーセントエンコードされたURLをデコードした文字列を生成するには次のようにする。

{{ printf "%q" .Destination | safeHTMLAttr }}

printf "%q" xxは文字列としてエスケープした状態で出力される(「“xx”」)。
.Destinationをただそのまま渡すとHugoはセキュリティ的に問題のあるタグ属性であると判断して強制的に代替文字列zgotmplzを挿入してしまう。safeHTMLAttr関数を使用してこれを回避する。

全てのリンクをエンコードしないようにするならrender-linkのhref属性部分を単純に置き換えればいいが、「基本的にはリンクはURLエンコードして、問題がある場合だけデコード状態で挿入する」という風にするにはたとえば次のようにする。

<!-- タイトルを"_noencode"にするとURLエンコードを行わないようにする -->
<!-- 例: [text](url "_noencode") -->
<a
  {{ if eq .Title "_noencode" }}
    {{ printf "href=%q" .Destination | safeHTMLAttr }}
  {{ else }}
    href="{{ .Destination | safeURL }}"{{ with .Title}} title="{{ . }}"{{ end }}
  {{ end }}
  {{ if strings.HasPrefix .Destination "http" }} target="_blank" rel="noopener"{{ end }}>
    {{ .Text | safeHTML }}
</a>
[link](https://hoge.com/hoge#foo(bar) "_noencode")

<a href="https://hoge.com/hoge#foo(bar)" target="_blank" rel="noopener">link</a>

「タイトルに"_noencode"を指定した場合はエンコードしない」という風にしている。この場合タイトルと併用できなくなってしまうわけだが、問題があればreplaceとかreplaceREとか使って_noencodeだけ消して残りの部分をタイトルとして挿入するようにすればいいと思う。

See Also