render-link
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
だけ消して残りの部分をタイトルとして挿入するようにすればいいと思う。