Rust 製スタティックサイトジェネレーター Zola をつかう

はじめに

Rust 製のスタティックサイトジェネレーターの Zola を使ってウェブサイトをひとつつくってみました。本記事はその時に書いた Zola の使い方メモになります。

スタティックサイトジェネレーターを使ったウェブサイト製作に至った経緯ですが、

  • ジオシティーズが 2020/3末に完全にサービス終了する。
  • 伴って、自分が持っていたジオシティーズのサイトのファイルが消えてしまうので救出。
  • せっかくだから内容はそのままに、昨今のモバイルや SNS 対応をしてウェブに再登場させておこう。

といったところです。 😀

今回復活させようとしている旧式サイトは、同じフォーマットの雑多なメモがたくさんおいてあるタイプで、このような形式のウェブサイトをつくる(修正する)場合は、テンプレートととなる .html をひとつ作成し、記事を流し込む方式を取りたくなります。

そこで Zola “スタティックサイトジェネレーター” を用いて、記事となる部分を旧式サイトから Markdown として抽出作成し、最後にビルドをかけ .html をたくさん生成する方式をとることにしました。

WordPress などの CMS を用いなかったのは、そもそも古のコンテンツのため手離れをよくしたかったというのが理由です。関連して、このサイトはドメインもジオシティーズに習い、自分のものを使わず無料のホスティングサービス(ここでは Netlify を利用)に依存したものを使うことにしました。

この記事の内容で Zola でリニューアルしたウェブサイトは次から見ることができます。

適当メモ – maple4estry –

適当に作ったり書いたりしたものを、もしかしたら誰かの役に立つかもと、 記載してみているサイトです。

あ、あと関係ないですが、もちろんリンクフリーです。 こんなところでよければ好きなページにはってください。

ご覧の通り、ページテンプレートはモバイル対応や SNS OGP 対応など最新化してみたものの、コンテンツは15年以上前の古い芸風ですゆえ、内容は気にしないでください。。。

Zola とは

Zola はスタティックサイトジェネレーターと呼ばれるソフトウェアのひとつです。

Zola

Your one-stop static site engine
Forget dependencies. Everything you need in one binary.

Rust でつくられていて(自分が少し Rust をやっていることもあり)勉強も兼ねて選択してみました。同様のソフトでは Go 製の Hugo が有名でしょうか。

なお、現時点 Zola はまだバージョン 0.10.0 で API フリーズしていませんので、この記事の内容は将来変わる可能性があります。ご了承ください。

自分が気がついた Zola の特徴を少しあげておきます。

  • Rust でつくられており高速性を重視。
    • ちなみに、今回のような 100 記事未満の小さなサイトであれば測る時間もないくらいで処理が終了しました。
  • html テンプレートエンジンに、同じく Rust でできた Tera を用いている。
    • “Used to Jinja2, Django templates, Liquid or Twig? You will feel right at home.”とのことで構文は Jinja2 ベースのようです。
    • 神社と寺? 🙂
    • Zola と Tera はプロジェクトリードが同じ方です。
  • Markdown のパーサーを、これもまた Rust でできた CommonMark 互換の pulldown-cmarkを使っている。
    • ソースコードのシンタックスハイライトも標準で対応。
    • ### などに日本語を書いた場合も id を英語スラッグに変換する rust-unidecode が有効になっている。

Zola の導入

macOS、Linux、Windows 版の各導入方法がドキュメントにあります。

Installation

Zola provides pre-built binaries for MacOS, Linux and Windows on the GitHub release page.

自分は Ubuntu に snap のパッケージがありましたので、こちらで導入しています。

snap install --edge zola

初期プロジェクトの作成

Zola を入れると zola コマンドが使えるようになり、新規プロジェクトの作成やファイル監視からの自動ビルドの機能が使えるようになります。

まずは任意のフォルダーで次のコマンドで、ひとつのウェブサイトに対応する初期プロジェクトを作成します。ここでは仮に example.comexample の という名称にしました。

いくつか質問がありますので適宜答えていきます。ここでは次のように設定しています。

  • CSS プリプロセッサーである Sass の利用を有効化。
  • Markdown 中のソースコードブロックを色付けする syntax highlighting を有効化。
  • サイト検索用の JSON インデックスの生成を無効化。
$ zola init example
Welcome to Zola!
Please answer a few questions to get started quickly.
Any choices made can be changed by modifying the `config.toml` file later.
> What is the URL of your site? (https://example.com): https://example.com
> Do you want to enable Sass compilation? [Y/n]: Y
> Do you want to enable syntax highlighting? [y/N]: y
> Do you want to build a search index of the content? [y/N]: N

Done! Your site was created in "/home/hiromasa/web/example"

Get started by moving into the directory and using the built-in server: `zola serve`
Visit https://www.getzola.org for the full documentation.

処理が終わると次のような構造でディレクトリとファイルが作成されます。

  • content/
    • 記事になる Markdown (.md)ファイルや記事の画像リソースなどを格納します。フォルダーも作成可能で、この配下の構造とサイトの URL の構造が一致します。
  • templates/
    • 記事を挿入する Tera 記述を使った .html テンプレートファイルを格納します。
  • sass/
    • CSS をつくつための .scss を格納します。Zola には Sass コンパイラが含まれているためここにつくった .scss は自動でビルドがかかり .css になります。
  • static/
    • サイトの .html で使うヘッダー画像や .js などを格納します。この配下のファイルは。サイトの URL の / 直下に配置されます。
  • themes/
    • Zola サイトなどで提供されているテーマを格納します。既存のテーマを活用することにより templates/ テンプレートを自らつくることなくcontent/ 内の記事を流し込み、手軽にサイトをつくることができます。
  • public/
    • Zola でビルドした結果でできるたくさんの .html が格納されるデフォルトフォルダです。この中身をインターネットに公開します。
  • config.toml
    • サイト名や URL などのサイトの設定を記述します。zola init 時の設定が保存されています。
$ cd exmaple
$ ls -laF
合計 32
drwxr-xr-x 7 hiromasa hiromasa 4096  3月 14 17:12 ./
drwxr-xr-x 5 hiromasa hiromasa 4096  3月 14 17:12 ../
drwxr-xr-x 2 hiromasa hiromasa 4096  3月 14 17:12 content/
drwxr-xr-x 2 hiromasa hiromasa 4096  3月 14 17:12 sass/
drwxr-xr-x 2 hiromasa hiromasa 4096  3月 14 17:12 static/
drwxr-xr-x 2 hiromasa hiromasa 4096  3月 14 17:12 templates/
drwxr-xr-x 2 hiromasa hiromasa 4096  3月 14 17:12 themes/
-rw-r--r-- 1 hiromasa hiromasa  471  3月 14 17:12 config.toml

この記事では Zola を知る上で最初の取り掛かりになるであろう、templates/ テンプレートファイルと content/ のコンテンツの作成方法について解説します。

Tera テンプレートエンジン

Zola に内蔵された Tera テンプレートエンジンは(Zola 専用というわけではなく)汎用的なものです。資料確認の順番的にはまずは Tera テンプレート仕様ドキュメントからみると理解しやすいと思います。

Tera の基本的な構文:

  • {{ 変数名 }} − 変数の内容を html に出力
  • {% 命令 %}ifforblock などの命令
  • {# コメント #} − コメント

典型的な Tera テンプレートを含んだ Zola の .html は次のようになると思います。

  • {{ config.title }} のようにして変数の内容を出力。config.title などの変数は Zola によって設定される。後述。
  • {{ get_url(path="book.css") | safe }} のようにして関数を呼び出し値を出力。get_url 関数は Zola 提供。safe は Tera 提供の値エスケープフィルター処理。
  • {% block 名称 %} で名称ブロックを生成。後述。

次のファイルをつくって templates/ に格納します。

base.html(任意の名前) − サイトの基本的な構造をつくる(空の{% block 名称 %} ブロックを事前にあちこち定義しているのがポイントです)

{# このファイルは base.html #}
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <meta name="viewport" content="width=device-width">
    <title>
        {% block title %}{% endblock title %}
    </title>
    {% block js %}
    {% endblock js %}
    {% block css %}
    <link rel="stylesheet" href="{{ get_url(path="style.css") | safe }}">
    {% endblock css %}
    {% block extra_head %}
    {% endblock extra_head %}
</head>

<body>
    <div class="page">
        <div class="page__content">
            <div class="book-content">
                {% block content %}
                {% endblock content %}
                <footer><a href="{{ config.base_url }}">{{ config.title }}</a></footer>
            </div>
        </div>
    </div>
    {% block js_body %}
    {% endblock js_body %}
</body>
</html>

index.htmlbase.html を Tera の extends 機能で継承指定し、親となった base.html 内で設定した block に出力したいコンテンツを合成する。

{# このファイルは index.html #}
{% extends "base.html" %}

{% block title %}
    {{ config.title }}
{% endblock title %}

{% block content %}
<header>最終更新日 <time datetime="{{ section.extra.date }}">{{ section.extra.date }}</time></header>
<h1>{{ section.title }}</h1>
{{ section.content | safe }}
{% endblock content %}

config.titlesection.titlesection.content などは Zola から設定される変数で、具体的に言えば content/ に配置した .md で設定した内容が挿入されます。

この例のおおざっぱな Zola の処理の流れとしては、.md 内のコンテンツが変数として index.html に挿入され、それが base.html に合成され最終的な .html が生成されるということになります。

Zola コンテンツとテンプレートファイル

templates/ に格納する Tera テンプレートは、Zola のファイル命名規約によりウェブサイト上の次の位置にコンテンツを出力します。

  • index.html − ホームページ(いわゆるサイトのトップ・フロントページ)
  • section.html − 各フォルダ階層のセクション(主に目次・アーカイブ)
  • page.html − ページ

前項「Tera テンプレートエンジン」でつくった index.html は、サイトのホームページ(フロントページ)で出力されるテンプレートファイルということになります。

index.html が継承で使用しているテンプレートファイル base.html は、Zola の命名規約から”外れていれば”なんでもかまいません。Tera テンプレートの機能でモジュール分割などを行う場合もこれらをファイル名を除けば、自由にファイルをつくることができます。

次に、テンプレートファイルとコンテンツとなる .md の対応の基本は次のようになっています。

  • index.htmlcontent/_index.md
  • section.htmlcontent/**/_index.md(フォルダの中の _index.md)
  • page.htmlcontent/ 配下の任意名の .md ファイル

そしてサイトの URL と content/ のフォルダーツリーが対応します。

Zola を使ったウェブサイトの構成の基本はこれらの動きを利用し、

  • content/ 配下内の .md ファイルとフォルダー配置でサイトのツリーをつくる。
  • content/ 直下及びその配下のフォルダーに配置した _index.md でそれらの目次(アーカイブ)をつくる。
  • それぞれのコンテンツは index.htmlsection.htmlpage.html で出力する。(content/ 内にフォルダーがない場合は section.html は不要)

という思想になっています。

index.htmlsection.html テンプレートはセクション系の仲間です。index.html のほうがホームページに配置される特殊な section.html と考えたほうがいいかもです。本で言えば目次とセクションの関係ですね。

さて、テンプレートファイルで使える変数についてですが、上記のセクション系のテンプレートでは sectionpage.html では page を前置した名称でアクセスできます。

index.html 抜粋 − 変数に section.titlesection.content が使われている。

{# このファイルは index.html #}
{% block content %}
<header>最終更新日 <time datetime="{{ section.extra.date }}">{{ section.extra.date }}</time></header>
<h1>{{ section.title }}</h1>
{{ section.content | safe }}
{% endblock content %}

そして、これらの変数は各 .md 内の上部 +++ ブロック「フロントマター」で設定できます。

content/_index.mdindex.html に対応)

+++
title = "適当メモ - maple4estry -"
extra.date = 2020-03-14
+++

適当に作ったり書いたりしたものを、もしかしたら誰かの役に立つかもと、
記載してみているサイトです。

また、フロントマター外の通常の Markdown コンテンツについては content という変数に取得することができます。

フロントマターで設定することができる変数及び、テンプレートから読み出せる変数は次のドキュメントに記載があります。

なお、{{ __tera_context }} とテンプレートに書くとその場で使える変数一覧が見えます。ビルドで変数がないというエラーになった場合は確認してみましょう。

Overview

If you are not sure what variables are available in a template, you can place {{ __tera_context }} in the template to print the whole context.

注意点として、各テンプレートでフロントマターの変数を取得する場合はそれぞれ sectionpage を前置するのを忘れないようにします。またコンフィグ値は config を前置します。

またフロントマター内で extra. を変数に前置するとユーザー定義変数となり、テンプレートで読み出すことが出来ます。

これらを利用したページのテンプレートとフロントマターを示します。

content/test.md

+++
title = "へごへごメモ"
date = 2020-03-13
extra.image = "./hegohego.png"
+++

## はじめに

や゛め゛て゛く゛た゛さ゛い゛よ゛~

page.html

{# このファイルは page.html #}
{% extends "base.html" %}

{% block title %}
    {{ page.title }}
{% endblock title %}

{% block content %}
  <header>最終更新日 <time datetime="{{ page.date }}">{{ page.date }}</time></header>
  <h1>{{ page.title }}</h1>
  {{ page.content | safe }}
{% endblock content %}

{% block extra_head %}
  <link rel="canonical" href="{{ current_url | safe }}" />
  {% if page.extra.image is defined %}
    {% set image = resize_image(path=page.extra.image, width=640, height=360, op='fit_width') %}
    <meta name="twitter:text:title" content="{{ page.title }}" />
    <meta name="twitter:image" content="{{ image.url | safe }}" />
    <meta name="twitter:card" content="summary_large_image" />
  {% endif %}
{% endblock extra_head %}

このテンプレートには次のような要素が入っています。

  • {% block 名称 %} により base.html 内に配置された titlecontentextra_head の3ブロックの内容を定義している。
  • page.html のページテンプレートなので変数の前に page. を前置して取得している。
  • .md のフロントマターで extra.image ユーザー変数を定義し、テンプレート内で page.extra.image で取得し、Tara テンプレートの条件分岐機能で、変数が存在すれば twitter カードの出力をしている。この際にアイキャッチ画像を resize_image 関数を使い任意の大きさで生成している。

その他の機能

以上の仕組みが分かると残りはドキュメントを読めば知恵と勇気でなんとかなると思います。

主なものにリンクをしておきます。

  • 特定の .md ファイルで特例的に別なテンプレートファイルを指定したい。
  • _index.md でアーカイブを出力したい。
    • section.pages 変数に配下のページの配列が入っているのでループして出力。
  • いろいろなサイト内の URL を取得したい。使える関数が知りたい。
  • 画像リサイズしたい
  • sitemap.xml を出力したい。
  • よく分からなくなってしまった。
    • 配布されているテーマのソースコードが大変参考になります。
    • Zola のテーマファイルは、themes 配下とプロジェクトルート配下の構造を同じに持ち、プロジェクトルートのファイルがあれば優先されるタイプの実装になっています。(親で上書きできる WordPress の子テーマと同じ感じです)

ビルド

コンテンツの .mdtemplates/ が準備できたらビルドしてみます。以下のコマンドで Zola 内蔵の http サーバーが起動され確認もすることができます。

$ zola serve
Building site...
-> Creating 57 pages (0 orphan), 0 sections, and processing 0 images
Done in 237ms.

Listening for changes in /home/hiromasa/web/maple4estry/{content, config.toml, static, templates, themes, sass}
Press Ctrl+C to stop

Web server is available at http://127.0.0.1:1111

zola serve で起動後はファイルの修正にウォッチがかかり、テンプレートやコンテンツまた Sass の修正による自動ビルドがかかるようになっています。

なお、サンプルのソースで行っているような resize_image などの画像リサイズ処理はウォッチ対象ではなさそうです。その場合は build コマンドで別途ビルドしてあげると出力されます。

$ zola build

ウェブサイトの公開

うまくできたら public 配下に生成されたファイルをウェブサーバーに載せるだけです。

ホスティングサービス Netlify 使う場合は Zola のビルドに対応していますので、以下のドキュメントどおりプロジェクトルートディレクトリに netlify.toml を配置して、public を除外して、git リポジトリーにコミットしてあげれば自動でデプロイしてくれます。便利ですね。

Netlify | Zola

Netlify provides best practices like SSL, CDN distribution, caching and continuous deployment with no effort. This site is hosted by Netlify and automatically deployed on commits.

Zola はここであげた以外にも、Markdown 内で利用可能なショートコードやフィルターなど便利な機能がまだまだあるようです。

導入も簡単で高速にビルドでき、特に大きなドキュメント系のウェブサイトで威力を発揮しそうです。個人的には AsciiDoc にも対応してくれると嬉しいですが(まだ Rust でのパーサーはなさそうですが)、、引き続き使っていきたいと思います。

Visual Studio Code の PHP 言語サポート vscode-php-intellisense 2017年版

Visual Studio Code Advent Calendar 2017 の 12日目の記事です。 🙂

昨日の「フロントエンド技術を導入した Java ウェブアプリケーション開発」に続きまして今日は PHP です。

実は昨年の Visual Studio Advent Calendar 2016 でも vscode-php-intellisense について書かせていただいておりましたが、2016年 12月時点ではユーザー定義関数の IntelliSense が git master ブランチ上にはあったものの未リリースの段階でした。(VS Code 側の対応待ちがあった)

1年経ちましてユーザー定義関数の補完も無事動作するようになり、また PHP パーサーが Microsoft 謹製のものに変わるなどパフォーマンスを大幅にあげ、実際のプログラミングで活用されている方も多くなったように思います。

準備

vscode-php-intellisense は、同じ作者の方が PHP 7 で実装された php-language-server が使われています。このためお使いの OS に PHP 7.0 以上が導入されている必要があります。

コマンドライン・ターミナルから php -v コマンドを実行し次のようになっていれば使い始められます。(Windows / powershell.exe の例)

PS C:\> php -v
PHP 7.0.13 (cli) (built: Nov 8 2016 13:45:28) ( ZTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies

OS (パス)の実行環境を変えたくないよという方は、VS Code の基本設定の次のキーに PHP 7 実行ファイルへのパスを設定してください。優先して使われるようになります。

{
  "php.executablePath": "...."
}

また、VS Code 内蔵の PHP サポートが有効になっていると IntelliSense が混乱しますので無効に設定してください。

{
  "php.suggest.basic": false
}

これにて準備完了です。 PHP プロジェクトのフォルダを VS Code で開き、いずれかの .php ファイルを開くと php-language-server が浮き上がり vscode-php-intellisense の機能が使えるようになります。

拡張の起動ログは「出力」の PHP Language Server から見ることができます(パースが完了すると IntelliSense が使えるようになります。Microsoft の PHP パーサーになってからかなり速くなりました!)

ログを確認すると分かりますが、現在のところパース対象の .php ファイルの大きさが 150000byte に制限されています。 例えば WordPress では制限容量を超えたファイルがいくつか存在し、IntelliSense の対象から外されてしまいますので、拡張のファイルを直接書き換えて対応してください。

Windows の場合

C:\Users\[ユーザー名]\.vscode-insiders\extensions\felixfbecker.php-intellisense-2.0.4\vendor\felixfbecker\language-server\src\PhpDocumentLoader.php

macOS / Linux の場合

~/.vscode-insiders/extensions/felixfbecker.php-intellisense-2.0.4/vendor/felixfbecker/language-server/src/PhpDocumentLoader.php

ソースファイルを 150000 で検索し、300000 くらいにすれば OK です。

    public function load(string $uri): Promise {
        return coroutine(function () use ($uri) {
            $limit = 150000; // ここを修正します

拡張のソースファイルを直接書き換えているため、バージョンアップがあると上書きされます。(再設定してください)

設定から limit が調整できるようなプルリクが届いていますので、近い将来改善されることを期待しています。(実は結構前のプルリクで進展がないのですが… 😛

以上で拡張を使う準備完了です。

PHP IntelliSense

ユーザー定義系の補完以外は去年紹介しましたので、今年はユーザー定義系のIntelliSense を確認してみます。

WordPress プロジェクトをサンプルに、テーマをコーディングしている想定です。

add_filter は WordPress が定義するグローバル関数ですが正しく IntelliSense できています。

ちなみに PowerShell の拡張などでは次のように引数のホバーを確認しながら入力を進めことができますが、残念ながらこの動きは vscode-php-intelliSense では未実装です。(TODO に入っていますので期待です!)

と思っていたら、昨日のアップデートで実装されてしまいました!

素晴らしい…

IntelliSense 確定後はコードレンズ(定義をここに表示)でシグネチャーを確認できますので、こちらで代用すると良いと思います。

残念ながら PHP の標準関数はコードレンズできませんので、カーソルホバーで確認しましょう。 🙂

PHP は動的型付け言語ですので、完全な型参照はできなく IntelliSense の対応もある程度までになりますが、コメントアノテーションや PHP 7 の型定義で補完が効くようになります。

class Test {
    /** @var wpdb */
    private $wpdb1;

    /**
     * @param wpdb $wpdb2
     */
    public function hoge($wpdb2) {
        $this->wpdb1->query(); // OK!

        $wpdb2->query(); // OK!

        /** @var wpdb $wpdb3 */
        global $wpdb3;
        //$wpdb3->; 無念...
    }
}

例にしている $wpdb は WordPress がグローバルに定義する DB のアクセサです。

  • PHPDoc 付き private メンバーの $wpdb1 は補完 OK!
  • PHPDoc 付きメソッドパラメーターの $wpdb2 は補完 OK!
  • メソッド内のインラインコメントアノテーション $wpdb3 は補完できず

という結果でした。

ちなみに、Eclipse PDT や PhpStome ではインラインコメントアノテーションも OK です。このアノテーション方式が何かしらの規格なのかは存じ上げませんがもしかすると将来対応されるかもしれませんね。 🙂

VS Code で PHP

ここ 1年ほど PHP は全て VS Code と vscode-php-intelliSense でコーディングしましたが、VS Code の操作系ともマッチしており快適に作業を進めることが出来ました。

また、昨日紹介しましたが、VS Code は昨今のフロントエンド系の技術との親和性が高いため、PHP などのウェブアプリケーションと相性が良いです。合わせて使うと強力な相棒となってくれることと思います。

OSC 北海道 2017 baserCMS & WordPress セミナー・ブース出展

7/13, 14に開催されましたオープンソースカンファレンス 2017 北海道にて、baserCMS & WordPress セミナー・ブース出展を行ってきました。

お越しになられた皆様、ご協力いただきました皆様、OSC スタッフの皆様、ありがとうございました!

セミナーで使いましたスライドをこちらに公開いたします。

15万ダウンロード達成!国産 CMS である baserCMS の紹介

baserCMS のほうは、CMS が初めての方、まだ baserCMS をご存じない方向けに特徴を紹介するセミナーとさせていただきました。

WordPress REST API と Vue.js を使ったフロントエンド開発

WordPress と JavaScript は既に経験がある方が多くいらっしゃいますので、Vue.js との組み合わせや利点の部分をサンプルサイドとソースコードベースに解説しています。

解説で使いましたサンプルサイトは以下のような動きになっています。

時間の関係でかなり早く進めてしまいました(すいません…)ので、ゆっくりスライドでみていただければと思います。 省略しないソースコード全体は github にコミットしました。

https://github.com/h1romas4/wp-vuejs-sample

baseCMS & WordPress ブース展示

OSC 大懇親会でもお話がありましたが、今年は学生さんの参加が多くありみなさん熱心にブースで内容を聞かれていきましたので嬉しかったです。

ちょうど baser と WP の 2ブースをやっていましたので、CMS とはどういったものか、それぞれどのような特徴があるのかを実際に動かしてお話させていただきました。

WordBench 札幌(WordPress)のブースには yuriko さんからお送りいただいた、スクロールワプーが登場。こちらに興味を持たれてブースに立ち寄られた方も多くいらっしゃいましたので、baserCMS & WP 公式キャラの経緯などをお話することができました。:)

というわけで数えてみたところ OSC 北海道の出展は 2010 年からなので 8回目となっていました。引き続き 10回連続出場を目指してがんばります。やってみたい!という方がいらっしゃればいつでもお声がけください!よぼよぼ。