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をこれを使って作ることができる。
ただ、いちばん面倒なのはここではなく、既存のすべてのリンクにもれなくこの{% include relative %}
をつけることだったりする。
追記: このテンプレートは404ページにリダイレクトされた時に動作しない。
/404.htmlのつもりでHTMLが生成されるのだが、実際のlocationはアクセスしようとしたページのものになるので、階層がずれてしまう。
完全な静的サイトだと信じきっていたので思わぬ落とし穴だった。
404ページを内部リンクのないシンプルなHTMLにすることで対処した。
今までどおりの404ページを使う方法を考えたい。