Hugoで個人サイトを再構築中

Posted:

放置気味だったサイトをリニューアル中なのですが、全体の作り替えの際に長らくお世話になった手打ちHTML+Hexo(ブログのみ)から全体的にHugoでの作成へと乗り替えた話です。(まだまだ途中だけど!)

別名、「Hexo→Hugoで色々違う部分が多すぎて躓きまくったので、未来の自分のために包み隠さず赤っ恥を晒していこう」案件。


Hexoから乗り替えるに至った経緯

色々あるのだけども。
一番は、今年の5月のGW直前くらいに、自PCのマザーボードに付随しているLANポートが焼けてマザーボードがおシャカになった際に、HDDやSSDが道連れにされて昇天してしまったこと。
パーツをまるっと交換せねばならなくなり、HDDもデータをサルベージしなきゃらないという大仕事を経て環境が新しくなりました。

その前は、色んな開発環境を整えてて、割と何でも出来るような状態だったのだけども。
改めてまた環境を整えるとなるとめんどくささが上回ってしまい、最低限すぐに必要と思われるもの(XAMPPとかPythonとか)しか入れてなくて。
Node.jsも、前はこのライブラリを利用していたアプリを毎日動かしていたので必須だったけども、やっぱり入れるのがめんどくさいそのアプリを使うことがなくなって利用シーンがなくなったので見送りまして。

HexoはNode.js依存なので、Node入れないと利用出来ない。
でもHugoは、他への依存もなく実行ファイルひとつで動く。

そういったHugoのお手軽さに目が眩んで、サイトの全体的な作り替えも兼ねて、お世話になったHexoからHugoへと乗り替えることにしました。

色々躓きまくった

乗り替えはやっぱり簡単じゃなかった…よ……(笑)
簡素化されているから難しい、みたいなところが結構あったような気がします。

同人屋を中心に個人サイトが再注目されている中、サイトを構築するためにこれからHugoに触る非プログラマな方々へ少しでも敷居が下がるように「あまりプログラムに強くない同人屋が弄るとこうなる」みたいな赤っ恥を晒していくよ。

二値関数で悩んだ

if文を使う時、評価する対象の記入する際に比較演算子とか論理演算子を入れると思うんですが。

1
2
3
4
5
if (a == 100) {
  // 真だったときの処理
else {
  // 偽だったときの処理
}

みたいな感じで。

Go言語のGoテンプレートでは、==(イコール)や>(大なり)とかの記号ではなく、eq関数やlt関数を使用します。 こういう感じ。(ep関数(==)の例)

1
2
3
4
5
{{ if eq a 100 }}
  // 真だったときの処理
{{ else }}
  // 偽だったときの処理
{{ end }}

見たらわかるけど、記号じゃないの。単語なの。
しかも、比較に使用する関数が先に来て、後に比較する値が2つ(arg1 == arg2 || arg1 == arg3なら3つ)続くの。
これを忘れていつもの調子で記号で打ってプレビューしようと思ったらエラーになるの。
親であるGo言語は普通に記号で使えるのに!!w

コレが一番ストレスでした。
この時点で「Hugo…やめようかな…」ってなってました(ぁ

日付のテンプレートで悩んだ

次に、日付のパラメータで「???????????」ってなってました。

Hugoのデフォルトの時間の形式はUTC時間(2023-12-01T10:26:43Z)です。
これをわかりやすいようにフォーマットして、2023-12-01 10:26:43とかにします。見慣れた形式ですね。
このフォーマットがね……Go言語はあまりにも変態すぎて「???????????」ってなりました。

多く採用されてる日時フォーマットって、YYYY-MM-DD hh:mm:ssみたいなフォーマットだと思うんですよ。
Go言語ではJan 2 15:04:05 2006 MSTとなります。

ん?ってなるでしょ…。 この並び、

①月 ②日 ③時 ④分 ⑤秒 ⑥年 ⑦タイムゾーン
Jan 2 15 : 04 : 05 2006 MST

という並びだそうで。
なんでアメリカ式なんだよパッと見でわかるかーーーーーー!!ってなりました。

それから、タイムゾーンの設定でアホをやりました。

デフォルト状態でHugo newで作るファイルに挿入される時間はUTC時間なので、日本のJST時間より9時間遅いです。
なので、Hugoの設定ファイル(hugo.toml)にタイムゾーンを設定します。

1
timeZone = 'Asia/Tokyo'

この記述をした後に、ふたたびHugo newで新規ファイルを作れば、UTC時間に+9時間された時間(上記例では2023-12-01T19:26:43+0900)が Archetypeテンプレート内の{{ .Date }}に挿入されます。
まかり間違ってもtimeZonetimezoneと打ってはいけません。
この間違いに気付かず、しばらく右往左往したのも良い思い出。

ページバンドルで悩んだ

これはいまだによく理解出来てなくて、ぼんやりとした理解で使ってるのだけども。
元々Hugoは画像等のメディアファイルは、contentフォルダの外にあるstaticフォルダから読み込む必要があったようで(この辺はHexoも同じ)。
それがユーザーの不満を買い、現在はcontentフォルダ以下で記事と一緒にメディアファイルなどを管理出来るようになったそうで。
これがページバンドルという仕組みらしく。

このブログ(NOTE)は、contentフォルダ以下にセクション(フォルダ)を作り、その中に記事と画像ファイルを配置しています。

当サイトのフォルダ例

  • content
    • logs ←Branch Bundle
      • _index.md
      • cg ←Branch Bundle
        • inno ←Leaf Bundle
          • index.md
          • reiji001.png
          • syugo001.png
    • note ←Branch Bundle
      • 200201.md(slug = 2020-nen)
      • 220509.md
        • postimg
          • 200201_01.jpg ←不正なリソース
          • 200201_02.webp
          • 220509_t.png

noteがNOTEセクション(ブログのフォルダ)で、logsは MAINLOGセクション(コンテンツ倉庫のフォルダ)です。

リーフバンドル(Leaf Bundle)

リーフバンドルというがのつまり、シングルページ(index.md)とそこのみで使われるページリソースがあるフォルダ内部。
上記例で言えば、logs/cg/inno/がリーフバンドルのフォルダになります。
リーフバンドルは下層にリソースファイルを含むサブフォルダを作れます。
階層はどれだけ深くてもそのバンドルのリソースとして利用できます。
ただしindex.mdを含むフォルダの中に別のindex.mdを含むサブフォルダは作ることが出来ません。
つまり、個別記事フォルダの中に別の個別記事は作れない。
これは名前的にもリーフ(葉)だから、ブランチ(枝)みたく分かれる事が出来ないから、なのかな。
通常、ブログ等の記事を作る際は、リーフバンドルになるように作るのが一般的な気がします。

ブランチバンドル(Branch Bundle)

ブランチバンドルというのがつまり、セクション直下に_index.mdがあるフォルダ内部。
このバンドルのindexは_index.mdのみ(index.mdは通常ページとしてカウントされる)で、これがレンダリングされるとindex.htmlになります(つまり、note/index.htmllogs/index.html)。
こういう用途から、_index.mdはTOPページ、セクションページ、タクソノミー、タームページのリストページに利用されます。
ブランチバンドル内に_index.mdが見つからない場合は、Hugo側で勝手に補完して上記と同じ振る舞いをするようです。
よく、タグとかアーカイブのページで「○○(×件)」みたいな表記がありますが、_index.mdが無い場合、このセクションページのタイトル(○○の部分)はセクション名(フォルダ名)から拝借するらしい。
その場合はセクションページに表示するタイトル名を変えたり、それに付随するカスタム情報などは表示出来ないので、○○の部分を変えたい場合は、ちゃんと_index.mdを作ってフロントマターにtitleを設定するのが吉。

また、ブランチバンドルにある_index.md以外のMarkdownファイルは、そのひとつひとつが個別記事扱いされるようで、サイト設定(hugo.toml)と記事のフロントマターでslugを使うように設定してあると、ファイル名が何であろうと設定したslugでフォルダが作られます(上記例では200201.md2020-nen/index.html)。
当サイトのNOTEはそれを利用して、記事はリーフバンドルでフォルダ管理ではなく、日付+slugのファイル名で記事を一元管理しています。
だってフォルダごとで別々にするとめんどくさい……。

それから、ブランチバンドルは画像などを管理するリソースのための下層フォルダを作っても、各個別記事などからそこに置いたリソースを直接利用できません。
ここで画像などを使用する場合は、_index.mdと同じ階層(ここでは「note」フォルダ直下)にファイルを置けば利用出来ます。
上の例で言えば、note直下にある postimg フォルダ内の画像群は不正なリソースなので、noteセクションにある他のMarkdownファイルからは利用出来ないのが本当なのですが(関数を駆使してショートコードを作れば別だけど)。
実は、

1
<img src="/note/postimg/200201_01.jpg">

のようにルートからの絶対パスを通すと画像が表示されます。
どうやらレンダリングされるMarkdownファイル以外はそのまま同名フォルダへとコピーされてるみたいなので、実質リソースフォルダ扱いにできます。
とはいえ、これは想定されている使い方ではないでしょうから、安全を期するのなら、staticフォルダへ該当セクションと同じフォルダを作り(ここでは「note」)、その中へリソースフォルダ(ここでは「postimg」)を置いておけば、ビルドの際、同名のフォルダ(「note」フォルダ内)へとコピーされます(サーバープレビューでもちゃんと表示されます)。
画像等のメディアファイルも、記事のバンドルで管理した方がわかりやすいんだろうけど。
ファイル名は日付+連番にしてあるので一元管理がしやすく、記事と一緒でフォルダごとに別々にするのがめんどくry

partialテンプレートで悩んだ

これもめちゃくちゃ悩みました。

テンプレートを組む際、ベーステンプレート(baseof.html)と他のテンプレート(list.htmlだったりsingle.htmlだったり)を組み合わせると、基本骨格を何度も入力しなくても使い回すことが出来ます。 ベーステンプレートの事は公式ドキュメントを読んでいただくとして、partialテンプレートですよ……。 こいつにマジでハマったんよ……。

結論から言えば、partialテンプレートはどんな深い階層からでも

1
{{ partial "footer" . }}

とかで呼べるということ。

以下、当サイトの実例(NOTE関連のみの抜粋)

  • layouts
    • _default
      • baseof.html
    • note
      • index.html
      • single.html
    • partials
      • footer.html
      • fscript.html
      • head.html
      • pagination.html
    • note
      • head_note.html
      • single_note.html
    • widgets
      • archives.html
      • searchform.html
      • taxonomies.html
    • index.html

あんまり階層深くないんだけども(ぉ

普通、階層の深い場所から違う階層のファイルを呼ぼうと思ったら、呼び出し元ファイルから呼び出し先ファイルまでのパスを書くと思うんですよ。
上記例でいえば、noteフォルダ内のsingle.htmlからpartialsフォルダ内のwidgetsフォルダのarchives.htmlを呼ぼうと思ったら、呼び出しのパスって

1
../partials/widgets/archives.html

とかになると思うんですよ。Hexoはそういう感じだったので。
けど、Hugoは{{ partial }}で呼び出す際は、{{ partial }}と書いた時点で既に

1
/layouts/partials/

と書いたも同然な振る舞いをしているらしく。

上記例でいえば、single.htmlのテンプレートには

1
{{ partial "widgets/archives" . }}

と書けばそこにwidgetsフォルダのarchives.htmlが呼ばれます。

何を当然と思うかもしれないでしょ……手打ちHTMLの相対パスで慣れすぎるとこんな沼に陥るかもしれないんだぜ……(笑)
これに気付くのが一番時間掛かりました……階層気にせず他から呼べるって書いておいてほしいわ……。

日本語URLを英語URLにするのに悩んだ

Hugoは日本語をはじめとする2バイトURLはきちんとURLエンコードしてくれます。
日本語ドメインとかも割と浸透しているようで、URLが日本語になってるサイトも結構見掛けます。

が。

古い人間なので、URLが2バイト文字なのは正直気持ち悪いハラハラします。
そもそもとして、ウチは長くあるサイトなので(今数えたら22年もやってた)、ブログのURLとかも全部半角英数字で統一してあります。
なので、ここでもURLは全部半角英数字で揃えたい。

Hexoは基本機能としてURLマッピングがあり、とても判りやすく日本語のタグやカテゴリに半角英数字を割り当てることが出来ていました。
WordpressからHexoへ乗り替える時も、Hugoは最後まで候補になっていたんだけど、この2バイトURL問題をどうしても解決出来なかったのでHexoを採用しHugoを見送ったんです。
どうしたものかとドキュメントを見ながら考えて、解決に至ったので、これはまた別の記事にてご紹介します。

でも、まどろっこしいことせずに、Hexoみたくサイト設定ファイル内でマッピング出来たら話は早い気もするよ……w
(個人的には、てがろぐのハッシュタグもなんとかならないかなと思っている……)

そんなこんなで

デフォルトでアーカイブの一覧が作れない(タクソノミーを駆使してリストを作る)とか、ようやく最近になって内外ともにブログカードでリンクが作れるようになったのかな?とか、まだ若干不満は無いわけではないんだけども。
一番不満を抱いていた「2バイトURLを半角英数字にする」が解決しただけでもHugoを使う理由になります。
ショートコードがHexoよりも作りやすく、keyで値がショートコードを使ったヵ所を後から見ても「これ何の値だったっけ…?」ってならないのもポイント高いです。

それから、ビルドが激早で驚いた。
色んな所で早い早いと言われているので、「ふーん、早いのか」くらいにしか思ってなかったんですが。
ターミナルで hugo と打ってエンターキー押したら、もうpablishフォルダに全部ファイルが出来てる。

早すぎる(笑)

Hexoの時は、最低でも30秒くらいは待ってたよ……Hugoは10秒も掛かってないんじゃないかな……。

あと、ベーステンプレートにあるブロック機構がとても便利だと思いました。
partialテンプレートにするまででもない、でもセクション毎に表記を変えたい、みたいなワガママにもしっかり応えてくれる機構だと思いました。

まだ改装途中でTOPページとNOTEしか作り替えてないんだけど、他のページも出来たら、制作手順とか書きたいなと思います(いつになるかわかんないけど)

参考サイト



▲PageTOPへ