静的サイトジェネレータ「Hugo」と技術文書公開向けテーマ「Docsy」でOSSサイトを作る
最近ではWebサイトを構築する際にWordPressなどのCMS(コンテンツ管理システム)を利用することが多いが、今日でも静的なHTMLファイルを使ったサイト構築には多くのメリットがある。今回は、こうしたHTMLファイルベースのサイト構築を支援するHTMLファイル生成ツール「Hugo」を紹介する。
目次
静的ファイルでサイトを作るための支援ツール「Hugo」
コンテンツの作成や編集の容易さから、昨今ではWebサイトを構築する際にCMSを利用する例が多い。ただ、CMSを使用するにはCMS自体のインストールや設定、データベースの準備などが必要であるため、更新頻度の低いサイトやページ数の少ないサイトでは静的なHTMLファイルを使って構築したほうがコストが低くなる場合がある。
とはいえ、HTMLファイルをいちいち手動で作成するのは楽ではない。そのため、HTMLファイルを半自動生成するようなツールが数多く公開されている。今回紹介するのはその1つである「Hugo」というソフトウェアだ(図1)。
HugoはGo言語で実装された「Webサイト構築フレームワーク」で、最初の公開は2013年という比較的新しいツールだ。コンテンツ管理システムではなく「Webサイト構築フレームワーク」と名乗っているとおり、コンテンツの管理ではなく、Webサイトで使われるHTMLファイルやRSSファイルなどの生成に特化した機能を備えている。
Hugoの特徴
Hugoの大きな特徴としては、データベースを使用せず、コンテンツをすべてファイルで管理するという点がある。記事はHTMLもしくはMarkdown形式で記述し、それを一定のルールに従ったディレクトリ下に保存したうえで「hugo」コマンドを実行すると、そこから個別ページやインデックスページのHTMLファイルなどが生成される。また、サイトやコンテンツを動的にプレビューするためのWebサーバー機能もある。本番運用には適していないが、これを利用することで記事執筆やサイト開発が簡単に行える。
それ以外にも、次のような特徴がある。
Go言語の機能を活用
HugoはGo言語で実装されており、Go言語の機能を活用して実装されている。たとえば、HTMLなどを出力するためのテンプレート機構はGo言語のテンプレート機能(「html/template」や「text/template」)をそのまま使っており、また機能を拡張するためのモジュール機構についても、Go言語のモジュール機能が使われている。また、Hugo(hugoコマンド)内にモジュールの管理機能が組み込まれており、hugoコマンドだけでモジュールを追加したり、アップデートを行うこともできる。
多くのテーマが公開済み、Git経由ですぐにインストールできる
Hugoでは、CMSのように「テーマ」を設定することでサイトの外観を簡単に変更できる。Hugoで利用できるテーマは「Hugo Themes」サイトで公開されており、ここで公開されているものだけでも200以上のテーマが存在する。これらテーマはHugo本体とは別のメンテナによって管理されているが、それぞれのGitリポジトリが公開されており、リポジトリをクローンして設定を追加するだけで利用できるようになる。
拡張性が高くさまざまなサイト構築に利用可能
Hugoは「Webサイト構築フレームワーク」を名乗っているとおり、非常に幅広い用途で利用できる。たとえばコンテンツが少ない企業紹介サイトのようなものや、定期的にコンテンツが追加されるブログのようなWebサイトだけでなく、たとえば写真中心のアルバムのようなサイトや、開発者向けのソフトウェア解説サイトやドキュメントサイトといったサイトの構築も可能だ。
ただし、その拡張性の高さから、テーマ毎にコンテンツ管理の流儀がやや異なることがある点には注意したい。
もちろん、独自のテーマを作成することも可能だ。詳しくは後述するが、その場合、最低限4つのテンプレートファイルを用意するだけでHugoの主要な機能が利用できるようなる。Hugoではコンテンツの種類を自由に定義でき、それにあわせて挙動を変えることができるので、テンプレートファイルなどを作り込むことで複雑な構造のサイトを作ることも可能だ。
ソフトウェア開発プロジェクト向けのテーマもある
最近のソフトウェア開発プロジェクトでは、Gitリポジトリでその公式Webサイトやドキュメントサイトなどを管理することが多い。テキストファイルベースでサイトのコンテンツを管理できるHugoはこうした手法と親和性が高く、ソフトウェア開発プロジェクト向けのテーマも複数用意されている。こうしたテーマを利用することで、高品位なドキュメントサイトを簡単に構築できる。その1つとして、本記事後半ではGoogleが提供しているオープンソースプロジェクト向けのテーマである「Docsy」を紹介する。
Hugoのインストールと基本的な使い方
Hugoのコア部分は、「hugo」という単一の実行ファイルで提供されている。HugoはGo言語で実装されているため、使用するプラットフォームに対応するコンパイル済みバイナリファイルをダウンロードして適当なディレクトリに展開するだけで実行できる。コンパイル済みバイナリはGitHubのリリースページから入手が可能だ。ここではWindowsやmacOS、各種Linux、各種BSD向けのコンパイル済みバイナリが公開されている。
なお、WindowsおよびmacOS、Linux向けには「Sass」や「SCSS」といったCSSプリプロセッサ機能が有効になった「extended」バージョンも用意されている。一部のテーマではこの機能が必要なものもあるため、使用したいテーマに応じて選択しよう。extendedバージョンは「hugo_extended」から始まるファイル名で配布されている。
また、FedoraおよびDebian(stretch以降)、Ubuntuではディストリビューションが公式のパッケージを提供しており、aptやdnfなどのパッケージマネージャ経由でもインストールできる。
以下では、すでにパスが通ったディレクトリにhugoがインストールされていることを前提に説明を進めていく。また、今回使用したバージョンはextended版の0.58.3だ。
Hugoの設定ファイルやコンテンツディレクトリの作成
Hugoでは、「hugo new site」コマンドでサイト構築に最低限必要なディレクトリや設定ファイルを作成できる。
hugo new site <作成するディレクトリ>
まずはこのコマンドを実行し、コンテンツを格納するディレクトリや設定ファイルを作成しよう。たとえば「./example」というディレクトリを作成し、そこに設定ファイルなどを格納する場合は次のようになる。
$ hugo new site example Congratulations! Your new Hugo site is created in /home/hylom/hugo/example. Just a few more steps and you're ready to go: 1. Download a theme into the same-named folder. Choose a theme from https://themes.gohugo.io/ or create your own with the "hugo new theme <THEMENAME>" command. 2. Perhaps you want to add some content. You can add single files with "hugo new <SECTIONNAME>/<FILENAME>.<FORMAT>". 3. Start the built-in live server via "hugo server". Visit https://gohugo.io/ for quickstart guide and full documentation.
このコマンドを実行すると、指定したディレクトリ(以下、「サイトディレクトリ」と呼ぶ)以下に次のディレクトリおよびファイルが作成される(表1)。
ファイル/ディレクトリ | 説明 |
---|---|
archetypes | 「hugo new」コマンドで使用するテンプレートを格納するディレクトリ |
archetypes/default.md | 「hugo new」コマンドで使用されるデフォルトのテンプレート |
config.toml | サイトの各種設定を記述する設定ファイル |
content | 公開するコンテンツを格納するディレクトリ |
data | 公開するコンテンツ以外の各種データを格納するディレクトリ |
layouts | カスタムテンプレートなどを格納するディレクトリ |
static | サイトで使用する画像やCSSなどの静的ファイルを格納するディレクトリ |
themes | 外観を設定するテーマファイルを格納するディレクトリ |
Hugoで使われる設定ファイルフォーマット「TOML」
Hugoでは、デフォルトでは「TOML」という形式の設定ファイルが使用される(拡張子は「.toml」)。JSONやYAML形式もサポートされているものの、多くのテーマやモジュールがTOML形式の設定ファイルを使用しているので、TOMLフォーマットに慣れておいた方が良いだろう。
TOML形式はJSONと同様の表現力を持つ構造化データのフォーマットで、数値や文字列、日付や時刻(RFC3339形式)、配列、辞書型データなどを扱える。JSONやYAMLと異なるのは、次のように「<キー> = <値>」という書式でキーに対する値を指定する点だ。JSONと同様、文字列はダブルクォーテーション(")で囲んで指定する。また、YAMLと同様に「#」から行末まではコメントとして無視される。
# 「#」から行末まではコメントとして扱われる foo = 1 bar = "hello" hoge.baz = 2019-09-01T00:00:00+09:00
「hoge.baz」のように、「.」でキー名を連結することで、辞書型のデータを定義できる。このように記述したTOML型データは、JSON形式では次のような辞書型データと一致する(ただし、JSONでは日時を扱う形式がないので厳密には一致しない)。
{ "foo": 1, "bar": "hello", "hoge": { "baz": "2019-09-01T00:00:00+09:00" } }
配列型のデータはJSONやYAMLと同様に角括弧([])を使って表現できる。角括弧中では適宜改行を使用することも可能だ。
hoge = [ 1, 2, 3 ] moge = [ "alpha", "beta" ]
TOMLで特徴的なのは、辞書型データを「[<キー>]」という形式で定義できる点だ。たとえば次の例では、「foo」というキーに「bar」と「hoge」という2つのキーを持つ辞書型データを紐付けている。
[foo] bar = "aaa" hoge = 2
これは、JSON形式では次のような表現となる。
{ foo: { "bar": "aaa", "hoge": 2 } }
また、辞書型データの中に辞書型データを入れる場合は、次の例のようにドット(「.」)を使用して子の辞書型データのキーを定義する。
[foo] bar = "aaa" hoge = 2 [foo.baz] qux = "bbb"
これは、JSON形式では次のような表現となる。
{ foo: { "bar": "aaa", "hoge": 2, "baz": { "qux": "bbb" } } }
なお、ドットを含むキー名を使いたい場合は、次のようにダブルクォーテーションでキー名を囲めば良い。
[foo."alpha.beta"]
また、中括弧({})を使って辞書型データを表現することも可能だ。
foo."alpha.beta" = { hoge = 1, moge = "hello, world" }
辞書型データを含む配列を定義したい場合は、次の例のように「[[<キー名>]]」という表現を使用する。
[[users]] uid = 1 name = "foo" email = "foo@example.com" [[users]] uid = 2 name = "bar" [[users]] uid = 3 name = "baz" email = "baz@example.com"
このデータは、JSON形式では次のように表現される。
users: [ { "uid": 1, "name": "foo", "email": "foo@example.com" }, { "uid": 2, "name": "bar" }, { "uid": 3, "name": "baz", "email": "baz@example.com" } ]
テーマの選定とインストール
さて、前述のとおりHugoでは「テーマ」を利用して機能や外観をカスタマイズできる。テーマには各種HTMLを生成するためのテンプレートや設定ファイルなどが含まれており、テーマを指定しないとHTMLの生成を行うことはできない。テーマは自作することもできるが、まずはHugo Themesサイトで目的や好みに合わせたテーマを選定してHugoの使用感を把握すると良いだろう。たとえばシンプルなブログサイトであればAthenaやBasicなどがある(図2、3)。
また、「Kube Hugo」や「Minimal」など、オープンソースソフトウェアの公式Webサイトなどで使いやすそうなテーマもある(図4、5)。
ソフトウェア開発プロジェクト向けとして使えそうなものとしては、GitHubのホスティングサービス向けの「Github project Page」や、APIドキュメントサイト向けの「DocuAPI」などがある(図6、7)。
そのほか、AMP技術に対応した「gohugo-amp」やミニマムな「XMin」、「Vanilla Bootstrap」などは、独自のテーマを作る際の参考になるだろう。
使用するテーマを決めたら、そのテーマをthemesディレクトリ以下にコピーする。テーマは基本的にはGitHubで公開されているので、themesディレクトリでgit cloneコマンドを実行してクローンすれば良い。たとえば「Ananke Gohugo Theme(Ananke)」を利用する場合、次のようになる。
$ cd themes $ git clone https://github.com/budparr/gohugo-theme-ananke.git
設定ファイルの準備
hugoでは、「hugo new site」コマンドで作成した「config.toml」ファイルでサイトの各種設定が行えるようになっている。デフォルトでは次のような内容のものが作成されているはずだ。
baseURL = "http://example.org/" languageCode = "en-us" title = "My New Hugo Site"
「baseURL」はサイトのURL、「languageCode」は使用する言語、「title」はサイトのタイトルを指定するもので。それぞれ適切なものに変更しておこう。たとえば日本語で使用する場合、「languageCode」は「ja-jp」に変更する。
また、これに加えて使用するテーマのディレクトリ名を指定する「theme」項目も追加しておく。たとえば先にインストールした「gohugo-theme-ananke」を使用する場合、次の1行を追加する。
theme = "gohugo-theme-ananke"
これ以外に、テーマによってはこの設定ファイルに記述した情報を参照するものもある。たとえばAnankeテーマでは「[params]」以下のパラメータ設定で、HTML内で使用されるclassやCSSのカスタマイズが可能だ。こういった情報に付いては、各テーマのドキュメントを参照して欲しい。
コンテンツの作成と管理
Hugoでは、「content」ディレクトリ以下にMarkdown形式で作成したテキストファイルを格納することでコンテンツを作成する。コンテンツの格納後、サイトディレクトリでhugoコマンドを実行するとcontentディレクトリ内が走査され、ディレクトリ内のファイルに応じて個別ページやインデックスページ、トップページ、タグ/カテゴリ別のインデックスページなどのHTMLファイルやRSSファイルなどが作成される仕組みだ。
Hugoではコンテンツに対しタイプ(型、type)を割り当てて管理する仕組みになっており、contentディレクトリ直下にディレクトリを作成することで、そのディレクトリ内のコンテンツに対しそのディレクトリ名と一致するタイプを自動的に割り当てることができる。
たとえば、contentディレクトリ直下に「posts」というディレクトリを作成した場合、そのpostsディレクトリ内にあるコンテンツについては自動的に「posts」というタイプが指定されるようになる。また、デフォルト設定ではこの「posts」ディレクトリ内にサブディレクトリを作成し、そこにコンテンツを格納した場合も、そのコンテンツのタイプは「posts」になる。
contentディレクトリ直下に格納したコンテンツについてはタイプが自動的には割り当てられない点や、コンテンツに割り当てられたタイプをどのように処理するかはテーマによって変わる点には注意したい。たとえばテーマによっては記事に対して「posts」ではなく「post」というタイプを使用することが前提になっているものもある。
なお、Hugoではテンプレートを元にcontentディレクトリ内にファイルを作成する「hugo new」コマンドが用意されている。
$ hugo new <作成するファイルのパス名>
たとえば次のように実行すると、contentディレクトリ下の「posts」というディレクトリ内に「example1.md」というファイルが作成される(postsディレクトリが存在しない場合は自動的に作成される)。
$ hugo new posts/example1.md /home/hylom/hugo/example/content/posts/example1.md created
このファイルは、archetypesディレクトリ(および使用するテーマディレクトリ内にあるarchetypesディレクトリ)内に作成されたテンプレートファイルを元に作成される。テンプレートファイルはタイプ毎に用意でき、作成するファイルのディレクトリ(タイプ)および拡張子に応じて「<タイプ>.<拡張子>」という名前のものをまず探し、それが無ければ「default.md」というファイルが使われる(詳しくはドキュメントのArchetypesページを参照)。
今回archetypesディレクトリ以下には「defualt.md」以外のファイルを用意していないため、このdefault.mdファイルを元に新たなファイルが作成される。このファイルはGo言語のテンプレート形式で記述されており、中身は次のようになっている。
--- title: "{{ replace .Name "-" " " | title }}" date: {{ .Date }} draft: true ---
テンプレートについては後述するが、「{{」および「}}」で囲まれた部分はテンプレートエンジンによって記述された処理結果に置き換えられる。たとえば「{{ replace .Name "-" " " | title }}」はファイル名から「-」を取り除いたものに、「{{ .Date }}」は実行時点の日付に置き換えられる。その結果、作成されるexample1.mdファイルは次のようになる。
--- title: "Example1" date: 2019-09-27T11:23:09Z draft: true ---
なお、「---」で囲まれた部分は「Front Matter Variables」と呼ばれるコンテンツのメタデータとなる。ここではYAML形式での記述が可能で、この例では「title」および「date」、「draft」というメタデータを指定している。ちなみに、「---」ではなく「+++」で囲めばTOML形式で、「{」および「}」で囲めばJSON形式での記述が可能だ。
ここで指定できるメタデータはドキュメントのFront Matterページにまとめられているが、代表的なメタデータとして表2のものがある。
メタデータ名 | 説明 |
---|---|
date | 作成日時 |
description | コンテンツの概要(要約) |
draft | trueの場合、下書きとして認識されコンテンツは公開されない |
expiryDate | 公開終了日時 |
keyword | metaタグなどで指定するキーワード |
lastmod | 最終変更日時 |
publishDate | 公開開始日時 |
slug | コンテンツのURLに使用される文字列 |
summary | コンテンツの要約 |
title | コンテンツのタイトル |
type | コンテンツのタイプ |
url | コンテンツのURL |
weight | コンテンツの表示順を決定する際に使用される重み |
さらに、これらに加えてコンテンツを分類するための任意のメタデータや、「Taxonomy」と呼ばれる分類用のデータも指定できる。HugoではデフォルトのTaxonomyとしてタグを指定する「tags」やカテゴリを指定する「categories」が利用できる。
このメタデータに続く部分がコンテンツの本文となる。ここでは前述のようにMarkdown形式でコンテンツを記述していく。今回は次のように記述した。
--- title: "テスト記事" date: 2019-09-27T11:23:09Z draft: false tags: [ example, test ] categories: [ test ] --- # テスト記事です。 記事はmarkdown形式で記述します。 * foo * bar * hoge ![画像を入れることも可能です](/img/dc.jpg)
なお、ファイルの拡張子を「.md」ではなく「.html」にすることで、HTMLでコンテンツを記述することも可能だ。
画像の管理
Markdownでは「![<代替文字列>](<URL>)」という書式で画像などのコンテンツを埋め込むことができるが、これについては単純に指定したURLを参照するimgタグ(「<img src="<URL>" alt="<代替文字列>">」といったもの)に変換される。そのため、指定したURLに対応するディレクトリに画像を別途用意しておく必要がある。最もシンプルな手段としては、「static」ディレクトリ以下に画像格納用のディレクトリ(たとえば「img」など)を用意して、そこに画像を格納しておく方法がある。ここではその手法を使用し、staticディレクトリ以下に「img」ディレクトリを作成し、そこに「dc.jpg」というファイルを配置した。
staticディレクトリに格納されたファイルは、コンテンツの公開時に自動的にルートディレクトリにすべてコピーされるため、たとえば「static/img/dc.jpg」というパス名のファイルは、「/img/dc.jpg」というURLで参照できる。
トップページのコンテンツ
サイトのトップページに表示するコンテンツは、contentディレクトリ直下に「_index.md」というファイルを作成し、ここに記述する。今回は次のように記述した。
--- --- # Hugo テストサイト Hugoのテストページです。
ここに記述したコンテンツに、テーマに応じたヘッダーやフッター、コンテンツ一覧などが付け加えられたものが実際のトップページとなる。
コンテンツのテスト
Hugoにはテスト用に動的にコンテンツを生成できるサーバー機能が用意されており、これを利用するとコンテンツやデザインなどのテストが容易に行える。サーバーを起動するには、「hugo server」コマンドを使用する。
hugo server
このコマンドを実行すると、デフォルトでは、127.0.0.1(localhost)の1313版ポート(http://localhost:1313)で待ち受けが行われる。WebブラウザでこのURLにアクセスすると、レンダリングされたWebサイトを確認できるはずだ。
なお、localhost以外のIPアドレスで接続を受け付けたい場合は「--bind <IPアドレス/ホスト名>」オプションでそのIPアドレスもしくはホスト名を指定すれば良い。また、デフォルトでは下書き状態の(メタデータで「draft: true」を指定した)コンテンツは表示されないが、「-D」オプションを追加することで下書き状態のコンテンツも確認できるようになる。そのほか指定できるオプションなどは「hugo server --help」コマンドを実行することで確認可能だ。
実際にWebブラウザで指定したURLにアクセスすると、次のようなページが表示される(図8)。
また、記事タイトル(今回の例では「テスト記事」)部分をクリックすると記事個別ページが表示される(図9)。
今回の記事ではメタデータの「tags:」でタグを指定しており、それが記事下に表示されている。このタグ文字列をクリックすると、そのタグがつけられた記事一覧ページが表示される(図10)。
なお、このテストサーバーは実行したディレクトリ以下のファイルやディレクトリの更新を監視しており、それらに更新があった場合自動的にページのリロードやサイト構造の再構築が行われる仕組みになっている。
静的HTMLファイルを出力する
テストサーバーでサイトやページをチェックし、問題がなければhugoコマンドを実行することで静的なHTMLファイルを出力できる。
$ hugo Building sites … WARN 2019/09/27 12:58:24 Page's .URL is deprecated and will be removed in a future release. Use .Permalink or .RelPermalink. If what you want is the front matter URL value, use .Params.url. | EN +------------------+----+ Pages | 14 Paginator pages | 0 Non-page files | 0 Static files | 4 Processed images | 0 Aliases | 1 Sitemaps | 1 Cleaned | 0 Total in 15 ms
デフォルトでは、カレントディレクトリ直下に「public」というディレクトリが作成され、そこにファイルが出力されるが、この出力先は「-d」オプションで変更が可能だ。Webサーバーでこのディレクトリをドキュメントルートに設定する、もしくはディレクトリの中身をWebサーバーのドキュメントルートディレクトリにコピーすればサイトを公開できる。
なお、クラウドサービスに作成したコンテンツを転送するための設定方法などは公式ドキュメントのHosting & Deploymentページで解説されているので、そちらも参照すると良いだろう。
細かい挙動のカスタマイズ
Hugoでは、設定ファイルやテンプレートファイルの追加などで静的HTMLファイルの出力ルールを細かく変更できる。カスタマイズ要素は多岐にわたるが、ここではよく使われると思われるものを紹介しておこう。
URLのカスタマイズ
前述の通り、Hugoではcontentディレクトリ以下に配置したコンテンツがHTMLファイルに変換されて出力される。このとき、Hugoのデフォルト設定では、公開されるコンテンツのURLはcontentディレクトリからの相対パス名によって決定される。たとえば、「content/posts/example1.md」というファイルから生成されたページのURLは「/posts/example1/」となる。このURL決定ルールは、設定ファイル(config.toml)の「permalinks」パラメータで変更できる。
たとえば、タイプが「posts」のコンテンツについて、URLを、公開日時を基準とした「/<年>/<月>/<日>/<拡張子を除いたファイル名>/」としたい場合、次のように設定する。
[permalinks] posts = "/:year/:month:/:day/:filename/"
そのほか、「:slug」(記事で指定した「slug」文字列)や「:title」(タイトル)といった変数を使ってURLを指定することも可能だ。使用できる変数についてはドキュメントのURL Managementページを参照してほしい。
また、Hugoのデフォルト設定では基本的にすべてのコンテンツが「index.html」として出力される。上記の設定を行った場合、例えば「content/posts/example1.md」というファイルから生成されたHTMLは「/<年>/<月>/<日>/example1/index.html」というファイルに保存される。index.htmlではなく「example1.html」というファイルに出力させたい場合、設定ファイルで「uglyurls」というパラメータを「true」に設定することで対応できる。
uglyurls = true
なお、このパラメータは「permalinks」以下ではなく設定ファイルのルート部分に記述する必要がある。
記事のメタデータとして「url」パラメータを指定することで、これらルールに従わせず、記事ごとに個別にURLを指定することもできる。例えば、「url: /test/url.html」と指定すれば、「permalinks」や「uglyurls」の設定にかかわらず「/test/url.html」というURLでその記事にアクセスできるようになる。
タグ/カテゴリページのカスタマイズ
Hugoではデフォルトでタグ(tag)やカテゴリ(category)という記事分類機能が有効になっている。これらは各コンテンツのメタデータで指定が可能だ。たとえば以下のように指定した場合、このコンテンツには「example」および「test」というタグと、「test」というカテゴリが付与される。
tags: [ example, test ] categories: [ test ]
Hugoのデフォルト設定ではこれらのタグおよびカテゴリ情報に基づき、自動的にタグ一覧ページ(「/tags/」)やカテゴリ一覧ページ(「/categories/」)、タグ/カテゴリ毎の記事インデックスページ(「/tags/<タグ名>/」および「/categories/<カテゴリ名>/」が作成される(図11、12、13)。
なお、タグ別/カテゴリ別記事一覧ページのURLは前述の「permalinks」設定で変更できる。たとえば次のように設定すると、「example」タグが付いた記事一覧ページは「/t/example/」でアクセスできるようになる。
[permalinks] tags = "/t/:slug"
また、カテゴリ別記事一覧ページを変更するには「categories」に対して設定を行えば良い。ただし、この設定を行っても、テーマによってはそのままではこれら一覧ページへのリンクが指定したものにならないケースがあるので注意したい。
タグやカテゴリのような分類用のデータ(taxonomy)は、設定ファイルの「taxonomies」項目でユーザーが任意に定義することもできる。たとえば、著者情報を指定する「author」というtaxonomyを使用したい場合、次の設定を追加すれば良い。
[taxonomies] tag = "tags" category = "categories" author = "authors"
このように、taxonomyは「<単数形> = <複数形>」という形式で指定する。この設定を行うと、記事のメタデータで「authors」として著者情報を指定できるようになる。
authors: hylom
また、この場合author一覧は「/authors/」というURLで、authorごとの記事一覧は「/authors/<著者名>/」というURLでアクセスできるようになる(図14)。
ただし、このように独自に定義したtaxonomyへのリンクをどう扱うかはテーマによって異なる。多くの場合、こういった一覧ページへのリンクをページ内に追加するには後述のメニュー機能を使用するか、もしくはテンプレートファイルの修正などが必要になるだろう。
また、設定ファイルに「taxonomies」設定を記述した場合、「tag」および「category」に関する設定に付いては明記しないと無効化される点にも注意したい。これを利用して、たとえば次のように明示的に「category」に関する設定を記述しないことでカテゴリ機能を無効にすることができる。
[taxonomies] tag = "tags"
同様に、「tag」に関する設定を記述しなければタグ機能を無効にできる。ただし、テーマによっては無効にしたタグやカテゴリへのリンクを消すために別途修正が必要となる場合がある。
セクションの管理
前述の通り、Hugoではcontentディレクトリ直下に作成されたディレクトリ内にコンテンツを格納し、そのディレクトリ名がタイプとして設定される。Hugoはサイト構築時、そのタイプが設定された記事の一覧ページ(セクションページ)を作成する。たとえば「posts」ディレクトリ内のコンテンツに対しては、「/posts/」というURLでセクションページが作成される(図15)。
この「セクション」機能は、コンテンツの種類ごとにページを管理する際に有用だ。たとえば、contentディレクトリ内に「news」というディレクトリを作成し、そこにコンテンツを格納することで、「news」というセクションを作成できる。この場合、デフォルト設定では「/news/」というURLでタイプが「news」の記事一覧を、「/posts/」というURLでタイプが「posts」の記事一覧を表示できる。
また、各サブディレクトリの直下に「_index.md」というファイルを作成することで、セクションページに表示するコンテンツを指定できる。図16の例では、「content/news/_index.md」として次を作成している。
--- title: ニュース記事一覧 --- # ニュース記事一覧 最新のニュース記事一覧です。
なお、これらセクションをどのように扱うかはテーマによって異なる。たとえばAnankeテーマの場合、デフォルト設定ではトップページにはタイプが「posts」の記事のみが一覧表示される。表示するタイプについては設定ファイルの「params.mainSections」パラメータで変更できるようになっており、たとえば次のように指定すれば、タイプが「news」の記事がトップページにリスト表示されるようになる(図17)。
[params] mainSections = [ "news" ]
ちなみに、タグやカテゴリ一覧ページではセクション(タイプ)が異なる記事が横断的にまとめて表示される。また、セクションについては階層状にすることも可能だ。詳しくはドキュメントのContent Sectionsページを参照してほしい。
メニュー機能
いくつかのテーマでは、ページ上段やサイドバーなどにメニューを表示し、そこに特定ページへのリンクを掲示できる機能がある。これは、設定ファイルに「menu」項目を追加することで利用できる。
たとえばAnankeテーマでは、「main」という識別子の付いたメニューが用意されており、これに対する設定を追加することでページ左上にメニューを表示できる。次の例は、このメニューに「news」および「posts」という項目を追加する設定だ。
[menu] [[menu.main]] identifier = "news" name = "news" url = "/news/" [[menu.main]] identifier = "posts" name = "posts" url = "/posts/"
この設定を行うと、次のように画面右上に「news」と「posts」というメニューが表示されるようになる(図18)。
コンテンツに「menu:」メタデータを付けることで、そのコンテンツへのリンクをメニューに追加することもできる。たとえば次のように記述した場合、「main」メニューにそのコンテンツへのリンクが追加される。
--- title: "Example2" date: 2019-09-28T09:18:52Z draft: false tags: [example] categories: [test] authors: hylom menu: main ---
メニューは階層構造にすることも可能だ。詳しくはドキュメントのMenusページを参照してほしい。
テーマのカスタマイズと作成
続いては、Hugoでのテーマのカスタマイズについて説明していこう。Hugoでは先に述べた通り、Go言語標準のテンプレート機構を用いてページのレンダリングを行っている。まずはこのテンプレート機能について簡単に説明した後、ページごとに必要なテンプレートファイルについての説明を行っていく。
Go言語のテンプレート機構
Go言語では、text/templateやhtml/templateというテンプレート機構が標準で用意されている。Hugoではこのテンプレート機構をそのままレンダリングに使用している。テンプレートの書式など詳しくはこれらのドキュメントを参照してほしいが、基本的なルールは次のようになっている。
変数展開と関数
テンプレート中で「{{ .<変数名> }}」と記述すると、その部分はレンダリング時に変数の値に置き換えられる。また、「{{ 関数 <引数1> <引数2> ... }}」のように記述すると、レンダリング時に関数が実行され、この部分はその結果に置き換えられる。
「{{ $変数名 := <値> }}」という形式で、テンプレート中で変数を定義して値を代入することもできる。このようにして定義された変数は、「{{ $変数名 }}」といった形で参照できる。
Hugoのテンプレート内で利用できる変数はドキュメントのVariables and Paramsページで、利用できる関数はFunctions Quick Referenceページで確認できる。
制御構造
「{{ if <式> }}」および「{{ else }}」、「{{ end }}」ステートメントを使うことで、ある変数の値が真の場合のみ特定の処理を実行できる。たとえば次のように記述すると、「Foo」が真の場合は「<p>Foo is {{ .Foo }}.</p>」が、そうでない場合は「<p>Foo is false.</p>」というテンプレートが処理される。
{{ if .Foo }} <p>Foo is {{ .Foo }}.</p> {{ else }} <p>Foo is false.</p> {{ end }}
また、ifと似た表記として「{{ with <変数名> }}」というのもある。こちらは変数が存在する場合に「{{ end }}」までの処理を実行するというものだ。「{{ if }}」と異なるのは、囲まれた部分では引数で指定された変数の値に「.」でアクセスできるという点だ。
{{ with .Foo }} <p>Foo is {{ . }}.</p> {{ else }} <p>Foo is false.</p> {{ end }}
また、配列データに繰り返しアクセスするには「{{ range <変数名> }}」および「{{ end }}」ステートメントを使う。この際、「{{ range }}」と「{{ end }}」で囲まれた部分内では、withと同様に「.」として配列から取り出された値にアクセスできる。
よくある使い方としては、次のように配列の中身をリストとしてレンダリングするものがある。
{{ range .Items }} <li>{{ . }}</li> {{ end }}
たとえばItems変数(配列)に「alpha」「beta」「gamma」という3つの値が格納されていた場合、この部分の出力は次のようになる。
<li>alpha</li> <li>beta</li> <li>gamma</li>
また、{{ range $item := .Items }}」のように変数を指定して記述すると、取り出した値を変数に代入しながら処理を繰り返すことができる。
{{ range $item := .Items }} <li>{{ $item }}</li> {{ end }}
配列だけでなく、辞書型(map)のデータに対し繰り返し処理を実行することもできる。その場合、キーと値を格納する変数を指定する。
{{ range $key, $value := .Item }} <li>{{ $key }} : {{ $value }}</li> {{ end }}
この場合、Item変数に格納されているキーおよび値の組み合わせが順に取り出され、キーが「$key」変数に、値が「$value」変数に代入される。
なお、配列について同様の書式を使用することで、インデックスと値を同時に取り出すことができる。
{{ range $index, $item := .Items }} <li>{{ $item }}</li> {{ end }}
また、「{{ else }}」を組み合わせることで、指定した配列・辞書に何もデータが格納されていない場合の処理を定義できる。
{{ range $index, $item := .Items }} <li>{{ $item }}</li> {{ else }} <!-- .Itemsが空の場合、この部分が処理される --> {{ end }}
比較関数
文字列や数値の比較などは、比較関数を使って行う。比較関数は表3のものが用意されている。たとえば「(eq .Foo .Bar)」と記述した場合、FooとBarの値が同じであれば真、そうでなければ偽が返される。
関数 | 説明 |
---|---|
eq | 2つの引数が一致すれば真 |
ne | 2つの引数が一致しなければ真 |
lt | 第1引数が第2引数より小さければ真 |
le | 第1引数が第2引数以下なら真 |
gt | 第1引数が第2引数より大きければ真 |
ge | 第1引数が第2引数以上なら真 |
また、「and」や「or」などで条件を組み合わせたり、「not」で否定したりすることも可能だ。
別のテンプレートを読み込む
Hugoでは、複数のテンプレートから共通で読み出されるテンプレートは「partial template」と呼ばれており、layoutディレクトリ直下のpartialsディレクトリに格納するルールになっている。このディレクトリ内のテンプレートは、別のテンプレートから「{{ partial <テンプレート名> . }}」とすることで呼び出せる。
なお、任意のテンプレートを呼び出せる「{{ template <テンプレート名> . }}」という構文もあるが、こちらはHugoの内部テンプレートを呼び出す際に利用できる。
ホワイトスペース、コメントの扱い
次の例のようにテンプレート内で「{{」の代わりに「{{-」と指定することで、テンプレートのレンダリング時にその「{{-」の前にあるホワイトスペース(スペース、タブ、改行)を削除するよう指定できる。同様に「}}」の代わりに「-}}」を指定することで、「-}}」の後ろにあるホワイトスペースを削除できる。
<span> {{- .Foo -}} </span>
ここでFooの値が「Hello!」だった場合、出力は次のようになる。
<span>Hello!</span>
また、「{{/*」から「*/}}」で囲まれた部分はコメントとして無視される。
ブロック定義と呼び出し
Go言語のテンプレート機能では、「ブロック」としてテンプレートを宣言し、それを使い回す機能がある。Hugoではこの機能を活用することで、ヘッダー部分やフッター部分といったページ内で共通の部分を効率よく処理できるようになっている。
ブロックを定義するには、次の例のように「{{ define "<ブロック名>"}}」を使用する。
{{ define "bar" }} <p>This is 'bar' block</p> {{ end }}
この「{{ define }}」から対応する「{{ end }}」までに記述されたテンプレートがブロックとなり、ここではその部分に「bar」という名前を付けている。ブロックとして定義されたテンプレートはその場では処理されず、次の例のように「{{ block "<ブロック名>" }}」で呼び出すことで、その場所にテンプレートの処理結果が出力される。
<h1>bar</h1> {{ block "bar" }}
この例では、最終的な出力結果は次のようになる。
<h1>bar</h1> <p>This is 'bar' block</p>
テンプレートの検索順
Hugoでは、「layouts」ディレクトリおよび「themes/<設定ファイルのthemeパラメータで指定したテーマディレクトリ>/layouts」ディレクトリ内に格納されたテンプレートファイルを使ってページのレンダリングが行われる。どのテンプレートファイルを使うかはドキュメントのHugo's Lookup Orderページに記載されているが、簡略化してまとめると次のようになる。
・トップページ:index.html → home.html → list.html → _default/index.html → _default/home.html → _default/list.html ・記事個別ページ:<タイプ>/single.html → _default/single.html ・セクションページ:<タイプ>/<タイプ>.html → <タイプ>/section.html → <タイプ>/list.html → section/<タイプ>.html → section/section.html → section/list.html → _default/<タイプ>.html → _default/section.html → _default/list.html ・Taxonomy一覧ページ:<Taxonomy名(複数形)>/<Taxonomy名>.html → <Taxonomy名(複数形)>/taxonomy.html → <Taxonomy名(複数形)>/list.html → taxonomy/<Taxonomy名>.html → taxonomy/taxonomy.html → taxonomy/list.html → <Taxonomy名>/<Taxonomy名>.html → <Taxonomy名>/taxonomy.html → <Taxonomy名>/list.html → _default/<Taxonomy名>.html → _default/taxonomy.html → _default/list.html ・Taxonomy別記事一覧ページ:<Taxonomy名(複数形)>/<Taxonomy名>.<Taxonomyアイテム名>.html → <Taxonomy名(複数形)>/<Taxonomyアイテム名>.html → <Taxonomy名(複数形)>/list.html → taxonomy/<Taxonomy名>.<Taxonomyアイテム名>.html → taxonomy/<Taxonomyアイテム名>.html → taxonomy/list.html → <Taxonomy名>/<Taxonomy名>.<Taxonomyアイテム名>.html → <Taxonomy名>/<Taxonomyアイテム名>.html → <Taxonomy名>/list.html → _default/<Taxonomy名>.<Taxonomyアイテム名>.html → _default/<Taxonomyアイテム名>.html → _default/list.html
なお、layoutsディレクトリとthemesディレクトリ以下のlayoutsディレクトリに同名のテンプレートファイルが存在した場合は、themesディレクトリ内のものが優先される。
また、「Taxonomy名」は「tag」や「category」といった設定ファイルで指定するTaxonomy名、「Taxonomyアイテム名」はそのTaxonomyの値として記事のメタデータで指定された値だ。たとえば「example」というタグがつけられた記事一覧ページで使われるテンプレートは「layouts/tags/tags.example.html」となる。
サイト共通のコンテンツを分離して管理する「Base Template」機能
昨今のWebサイトでは、全ページに共通するフッターやヘッダーが存在することが多い。こういった共通部分をページ本体とは別に定義するため、Hugoでは「Base Template」という仕組みが用意されており、前述のルールで探索されたテンプレート内でブロック定義しか行われていない場合(「{{ define <ブロック名> }}」〜「{{ end }}」以外の定義がない場合)は対応するBase Templateが探索され、それを使ってページをレンダリングするという挙動になっている。
Base Templateを使ったレンダリングで一般的なのは、各ページに対応したテンプレートで「main」という名前のブロックを定義しておき、次の例のようにBase Templateでそのmainブロックを呼び出してレンダリングする、といった手法だ。
<!DOCTYPE html> <html> <head> (ここにサイト共通のhead内要素を記述する) </head> <body> (ここにサイト共通のヘッダー要素を記述する) <div class="main-container"> <div class="main-contents"> {{- block "main" . }}{{- end }} </div><!-- .main-contents" --> </div><!-- .main-container" --> (ここにサイト共通のフッター要素を記述する) </body> </html>
こうすることにより、各テンプレートで定義したコンテンツの上下に共通のコンテンツを挿入できるようになる。
Base Templateの探索順
Base Templateは、layoutsディレクトリ以下から次の順序で探索される。ここで「<タイプ>」はレンダリングするコンテンツのタイプを示す文字列となる。
section/<タイプ>-baseof.html → <タイプ>/baseof.html → section/baseof.html → _default/<タイプ>-baseof.html → _default/baseof.html
最低限必要となるテンプレート
このようにHugoのテンプレート探索ルールはやや複雑だが、基本的には次の4つのテンプレートが存在すればすべてのページが生成できる。テンプレートを自作する場合はまずはこれらのテンプレートを作成し、必要に応じて追加していくと良いだろう。
- layouts/index.html(トップページ)
- layouts/_default/baseof.html(全ページで使われるBase Template)
- layouts/_default/list.html(セクションやTaxonomyごとの記事一覧ページ)
- layouts/_default/single.html(記事個別ページ)
なお、Hugoでは言語別のサイト生成機能やRSS、AMP対応機能などもあり、実際にはもう少し複雑なテンプレート探索ルールがある。より詳しくはドキュメントのTemplatesページを参照してほしい。
テーマの作成と修正
既存のテーマを修正して使用する場合、大規模な変更を加えるのでなければ、テーマディレクトリ内の「layouts」ディレクトリ以下の変更したいテンプレートのみをサイトディレクトリ下のlayoutsディレクトリ以下にコピーして使用すると良いだろう。前述の通り、Hugoではサイトディレクトリ下のlayoutsディレクトリとテーマディレクトリ下のlayoutsディレクトリに同一のテンプレートファイルが存在した場合、サイトディレクトリ下のテンプレートファイルを優先して使用する仕組みだからだ。
また、新規に1からテーマを作成する場合は、Hugoには最小限のテーマファイルやディレクトリを作成してくれる「hugo new theme <テーマ名>」というコマンドが用意されているので、これを利用すると良いだろう。たとえば「foobar」というテーマを作成するには、次のように実行する。
$ hugo new theme foobar
この場合、「themes/foobar」というディレクトリが作成され、そこにファイルとディレクトリが作成される。
$ ls -R themes/foobar LICENSE archetypes layouts static theme.toml themes/foobar/archetypes: default.md themes/foobar/layouts: 404.html _default index.html partials themes/foobar/layouts/_default: baseof.html list.html single.html themes/foobar/layouts/partials: footer.html head.html header.html themes/foobar/static: css js themes/foobar/static/css: themes/foobar/static/js:
また、ここで生成されたBase Template(layouts/_default/baseof.html)は次のようになっており、partialsディレクトリ内の「head.html」や「header.html」、「footer.html」を参照するようになっている。
<!DOCTYPE html> <html> {{- partial "head.html" . -}} <body> {{- partial "header.html" . -}} <div id="content"> {{- block "main" . }}{{- end }} </div> {{- partial "footer.html" . -}} </body> </html>
「Shortcodes」機能
Hugoでは、コンテンツを記述したMarkdownファイルやHTMLファイル内で利用できる「ショートコード(Shortcodes)」という機能がある。これは、ファイル内で「{{< <ショートコード名> <引数>... >}}」もしくは「{{% <ショートコード名> <引数>... %}}」という記述を行うことで、指定したテンプレートをその場所にレンダリングできる機能だ。
Hugoのデフォルトで利用できるショートコードは、ドキュメントのShortcodesページにまとめられている。また、独自のショートコードを定義することもできる。
たとえばデフォルトで定義されている「tweet」というショートコードは、引数にTwitterのTweet IDを指定することで、指定したTweetを埋め込むものだ。次の例は「1177485532525236227」というIDのTweetを埋め込んだものだ。
--- title: "Example3" date: 2019-09-28T15:42:36Z draft: false --- # Tweetの埋め込み {{< tweet 1177485532525236227 >}}
このように記述したページは、次のように表示される(図19)。
また、ショートコードで処理させたい部分を囲んで使用するタイプのショートコードもある。その1つがシンタックスハイライトを行う「highlight」ショートコードだ。このショートコードは、次のように「{{< highlight <ハイライト形式>>}}」と「{{< /highlight }}」でシンタックスハイライト表示を行いたい部分を囲んで使用する。
--- title: "Example4" date: 2019-09-28T15:48:22Z draft: false --- # シンタックスハイライト {{< highlight python >}} def main(): print("hello, world!") if __name__ == "__main__": main() {{< /highlight >}}
このように記述したページは、次のように表示される(図20)。
なお、独自のショートコードを実装する方法についてはドキュメントのCreate Your Own Shortcodesページを参照してほしい。
Googleが公開するオープンソース用のテーマ「Docsy」
さて、Hugoではいくつかソフトウェア開発者に向けたテーマも用意されている。その1つがGoogleが公開しているオープンソースプロジェクト向けのテーマ「Docsy」だ(図21)。
Docsyは技術文書やソフトウェアの公式サイトを構築するために必要なデザインやショートコード、ナビゲーションなどを提供するテーマで、これを利用することで容易にソフトウェアで使われる技術文書などを公開するサイトを構築できる。
DocsyではいくつかHugoの他のテーマとは異なる独自の設定が必要な点もある。そこで、以下ではそれらについて紹介しておこう。
Docsyのインストール
DocsyはほかのHugo向けテーマと同様、そのGitリポジトリをthemesディレクトリ以下にクローンすれば利用可能になる。ただし、このリポジトリではサブモジュール機能が使われているため、クローンの際に次のようにサブモジュールもまとめてクローンするよう指定する必要がある。
$ cd themes $ git clone --recurse-submodules --depth 1 https://github.com/google/docsy
また、Docsyでは設定ファイルの「[params]」以下で指定できる、テーマ固有の設定がある。docsyのディレクトリ内に含まれている設定ファイル例には次のように記述されているので、これも設定ファイルにコピー&ペーストしておこう。
[params] time_format_blog = "Monday, January 02, 2006" time_format_default = "January 2, 2006" # Sections to publish in the main RSS feed. rss_sections = ["blog"]
さらに、Docsyではサーバーの実行時やHTMLの生成時に「PostCSS」というツールを使用する。このツールはNode.jsで実装されており、Node.jsの実行環境やパッケージマネージャのnpmが必要となる。npmが利用できる環境であれば、サイトディレクトリで次のように実行することでこれらをインストールできる。
$ npm install autoprefixer $ npm install postcss-cli
Docsyのcontentディレクトリ構造
Docsyではブログ向けの「blog」、技術文書向けの「docs」、コミュニティページ向けの「community」の3つのタイプをサポートしている。これを踏まえて、contentディレクトリ以下にはこの3つのディレクトリを作成し、そこでコンテンツを管理していくことになる。
「blog」および「docs」セクション(「/blog/」や「/docs/」)や記事ページでは、それぞれに適した形で記事一覧やナビゲーションメニューが表示される(図22〜25)。
独自のショートコード
Docsyではいくつかの独自ショートコードが定義されており、これを利用することで見栄えの良いデザインのページを簡単に実現できる。例えば次の例は、「blocks/lead」や「blocks/section」、「blocks/feature」といったショートコードを使って実装したコミュニティページの例だ(図26)
--- title: "Example5" date: 2019-09-28T16:45:05Z draft: false --- {{% blocks/lead color="primary" %}} # コミュニティ ここにはコミュニティ関連のコンテンツを用意します。 {{% /blocks/lead %}} {{< blocks/section color="dark" >}} {{% blocks/feature icon="fa-lightbulb" title="アイコン付きブロック" %}} アイコン付きの見出しを作成できます! {{% /blocks/feature %}} {{% blocks/feature icon="fab fa-github" title="GitHub!" url="https://github.com/hylom" %}} 「Read more」付きの枠も作成可能。 {{% /blocks/feature %}} {{% blocks/feature icon="fab fa-twitter" title="Follow us on Twitter!" url="https://twitter.com/docsydocs" %}} For announcement of latest features etc. {{% /blocks/feature %}} {{< /blocks/section >}}
そのほか、詳しい機能や利用方法についてはDocsyを使って生成されているドキュメントページを参照してほしい。また、サンプルサイトのソースコードも公開されており、こちらも参考になるだろう。
現代的な静的なコンテンツ生成ツールとして有用
昨今では多くのWebサイトがWordPressなどのCMSを使って作成されているが、静的なHTMLファイルでのサイト構築にもメリットは多い。特に更新頻度の低いサイトなどでは、Hugoのような静的なサイト構築ツールは有用だろう。Hugoは現在でもある程度活発にメンテナンスが行われており、今後の機能強化も期待できる。
また、Hugoと連携できるツールや、ほかのコンテンツ管理システムからマイグレーションを行うためのツールなどもある。これらについては公式ドキュメントのDeveloper Toolsページでまとめられているので、こちらも参照すると良いだろう。