IPFS上でサイトをホストしたりするとドメインがコロコロ変わったりするので、今のうちにすべてのリンクを相対パスにしておきたくなった。 しかしGitHub Pagesにおいてそれは簡単なことではない。

だめなやつら

  • relative_url フィルタ

名前からしてそれっぽいがこれではダメである。 これは/path/to/fileのような「ルートからの」相対パスになる。

  • Jekyllプラグイン

これが使えれば一番楽だろうが、残念ながらGitHub Pagesではプラグインの導入が自由でない。

自作テンプレート

そういうわけでなんとか今使える機能だけで相対パスを生成する必要がある。 探してみたところこのようなものがあった。

How to deploy a jekyll site locally with css, js and background images included? - Stack Overflow

ここにあった以下のテンプレートを参考に改良を加える。 読めばわかるが、page.urlの最後がファイル名でも/でも動作するようになっている。

{% assign lvl = page.url | append:'X' | split:'/' | size %}
{% capture relative %}{% for i in (3..lvl) %}../{% endfor %}{% endcapture %}

<link href="{{ relative }}css/main.css" rel="stylesheet" />
<script src="{{ relative }}scripts/jquery.js"></script>

まず、すべてのページに2行のテンプレートを入れるのはめんどくさすぎるので別ファイルに分ける。

_includes/relative

{% assign lvl = page.url | append:'X' | split:'/' | size %}
{% capture relative %}{% for i in (3..lvl) %}../{% endfor %}{% endcapture %}

これで/path/to/file/path/to/directory/の形をしたルートからの相対パスの前に {% include relative %}をつけるだけで../..//path/to/fileのような相対パスになる。 ちょっと/が多い気がするが問題なく動作する。

しかしこのままでは改行が入ってしまうので1行にする。

{% assign lvl = page.url | append:'X' | split:'/' | size %}{% capture relative %}{% for i in (3..lvl) %}../{% endfor %}{% endcapture %}

問題はまだある。 このままでは/で使った時に空文字列を返してしまい、URLが/path/to/file、つまりルートからのパスになってしまう。 これではIPFS式のURL/ipfs/<hash>/path/to/fileに対応できない。 しかしこれは最後に.をつけてやるだけで解決する。

{% assign lvl = page.url | append:'X' | split:'/' | size %}{% capture relative %}{% for i in (3..lvl) %}../{% endfor %}{% endcapture %}.

これが完成形である。 ルートから遠いところでは../.././path/to/file、ルートでは./path/to/fileのようなURLをこれを使って作ることができる。

以下は実際の使用例である。

before

{% post_url 2017/10/2017-10-20-ipfs-browser-extention %}
/2017/10/20/ipfs-browser-extention.html

after

{% include relative %}{% post_url 2017/10/2017-10-20-ipfs-browser-extention %}
../../.././2017/10/20/ipfs-browser-extention.html

このとおり、相対的なパスを作ることができた。 ただ、いちばん面倒なのはここではなく、既存のすべてのリンクにもれなくこの{% include relative %}つけることだったりする


追記: このテンプレートは404ページにリダイレクトされた時に動作しない。 /404.htmlのつもりでHTMLが生成されるのだが、実際のlocationはアクセスしようとしたページのものになるので、階層がずれてしまう。 完全な静的サイトだと信じきっていたので思わぬ落とし穴だった。 404ページを内部リンクのないシンプルなHTMLにすることで対処した。 今までどおりの404ページを使う方法を考えたい。