ブログ
11ty(eleventy)で静的サイト構築
Githubの★の数などで静的サイトジェネレーターを比較できる StaticGenを見ていたら、Nunjucksが使える 11ty(eleventy)というのを見つけました。
そういうわけでオオコシです。
静的サイトジェネレーターというと Jekyll や Hexo などが有名かと思うんですが、11tyは Google Open Source Award にも選ばれており、テンプレートは Nunjucks だけでなく Liquid / PUG / Mustache / Handlebars / EJS / HAML / JavaScript Template Literals、もちろんHTMLやMarkdown が使えるので、例えば Nunjucks やめて PUG 使いたい!っていうケースにも対応できるし、A simpler static site generator. って書いてあるし、なかなか良いんじゃないかと思って試してみました。
インストール〜設定
11tyを使う場合の Node.js のバージョンは8以上となります。
Requires version 8 of Node.js or higher.
今回は Node.js の 10.1.0 を使おうと思うので nodenv で指定します。
11tyのREADMEにはグローバルにインストールするように記載があるのですが、プロジェクトごとに管理したいのでローカルにインストールします。
例)11ty_sampleというディレクトリ配下で作成する場合
$ cd 11ty_sample
$ nodenv local 10.1.0
$ npm i -D @11ty/eleventy
グローバルに入れた場合は eleventy
だけで実行できるのですが、ローカルなので npx
で実行します。
$ npx eleventy
このままではデフォルトの設定(--input . --output _site
)で出力されてしまうので .eleventy.js
という名前で設定ファイルを作成します。
設定項目については Configration (optional) を参照してください。
今回は下記のようなディレクトリ構成にしようと思います。
11ty_sample/
htdocs/ :出力
src/
html/ :入力
_includes/ :テンプレート格納場所
_data/ :データ(JSON)格納場所
この場合の.eleventy.js
は下記のようになります。
※ターミナルで npx eleventy --input ./src/html --output ./htdocs/
と入力したものと同等
module.exports = {
dir: {
input: "src/html",
output: "htdocs"
}
};
プラグインや独自フィルタの設定をする場合は関数にします。
module.exports = function(eleventyConfig) {
return {
dir: {
input: "src/html",
output: "htdocs"
}
};
};
そしてスターターキットとして用意されている eleventy-base-blog を参考に、いろいろやってみたものが下記になります。
.eleventy.js
const { DateTime } = require("luxon");
const syntaxHighlight = require("@11ty/eleventy-plugin-syntaxhighlight");
module.exports = (function(eleventyConfig) {
// 日付表示変換フィルタ
eleventyConfig.addFilter("readableDate", dateObj => {
return DateTime.fromJSDate(dateObj).setLocale('ja').toFormat("yyyy'年'M'月'd'日'");
});
// シンタックスハイライト
eleventyConfig.addPlugin(syntaxHighlight);
// 11ty設定
return {
templateFormats: [
"md",
"njk",
"html"
],
pathPrefix: "/",
markdownTemplateEngine: "liquid",
htmlTemplateEngine: "njk",
dataTemplateEngine: "njk",
passthroughFileCopy: true,
dir: {
input: "src/html",
includes: "_includes",
data: "_data",
output: "htdocs"
}
};
});
※ Luxon は Date オブジェクトを扱いやすくするライブラリです。
※ eleventy-plugin-syntaxhighlight は、コードブロックがあった場合、ビルド時に Prism形式に変換するものです。Prism.js本体は不要で、Prism用のテーマCSSを読み込むだけでシンタックスハイライトが適用されます。
※ dir.data
dir.includes
は dir.input
から見たパスになります。
グローバル変数、テンプレートの作成〜出力
まずはサイト全体で使うグローバル変数を .eleventy.js
の dir.data
で指定したフォルダに site.json
という名前で下記のようなファイルを作成します。
src/html/_data/site.json
{
"name": "11ty test site",
"url": "http://11ty.sample.local"
}
ファイル名に決まりはありませんが、filename.key
という感じで {{ site.name }}
でサイト名が、{{ site.url }}
でサイトURLが全ページで取得できるようになります。
データファイルは他にもいろいろな使い方があるので、詳しくは Using Data を参照してください。
次に全体のベースとなる Nunjucksテンプレートを dir.includes
で指定したフォルダに base.njk
として作成します。
src/html/_includes/base.njk
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta name="viewport" content="width=device-width">
<title>{% if page.url !== '/' %}{{ title }} | {% endif %}{{ site.name }}</title>
<meta name="description" content="{{ description }}">
<link rel="stylesheet" href="{{ '/css/styles.css' | url }}">
{% block css -%}{%- endblock %}
</head>
<body>
<header>ヘッダー</header>
<main>
{% block main -%}
コンテンツ
{%- endblock %}
<p>Update:{{ page.date | readableDate }}</p>
</main>
<footer>フッター</footer>
<script src="{{ '/js/common.bundle.js' | url }}"></script>
{% block js -%}{%- endblock %}
</body>
</html>
site.json
で設定したグローバル変数の他に、11tyでは page
で現在のページの情報が取得できます。(下記コードブロック参照)
{{ page.url }}
は出力先のルートからのパスになります。
というわけで、if文の中の page.url!=="/"
はTOPページ以外(になるはず)です。
{
url: "/current/page/file.html",
date: new Date(),
inputPath: "/current/page/file.md",
fileSlug: "file"
}
その他、特殊な変数は Special Variables で確認できます。
{% block js -%}{%- endblock %}
などは Nunjucks の記法になるので Nunjucksのドキュメント を参考に。
{{ '/css/styles.css' | url }}
は 11ty 独自のフィルターで、URLの出力を最適化してくれます。
.eleventy.js
の addFilter
で追加した独自のフィルターも使えます。
そして、上記のテンプレートを読み込むコンテンツ側はこんな感じになります。
src/html/index.njk
---
title: トップページ
description: ページ概要がはいります。
---
{% extends 'base.njk' %}
{% block css -%}
<link rel="stylesheet" href="{{'/css/top.css' | url }}">
{%- endblock %}
{% block main -%}
<h2>トップページのコンテンツ</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. <br>
Adipisci tempora asperiores earum, <br>
aspernatur pariatur officia cumque mollitia quos ab sapiente porro nulla cupiditate. <br>
Animi unde voluptatibus hic aspernatur deleniti minima.</p>
{%- endblock %}
{% block js -%}
<script src="{{ '/js/top.bundle.js' | url }}"></script>
{%- endblock %}
ページ単位で使う変数は、ページ上部にYAML形式で設定します。
title
や description
は普通の変数なんですが、Front Matter on any Templateにあるように、permalink
pagination
layout
tags
date
は特殊な変数となります。
permalinks
は 出力先のURLを個別に指定することができます。
デフォルトではCool URIs don’t changeのルールを採用しており、
subdir/template.njk
で作成しても subdir/template.html
にはならず、subdir/template/index.html
になるとのことで、
これに当てはまらない場合は permalinks
に任意のパスを書くことになります。
layout
は dir.includes
配下を参照してページに適用するテンプレートを選択します。
ただし、こちらでは Nunjucksの {% block %}
が使えませんでした。
そのため、Markdown などで記事を書くときは layout
、Nunjucks形式でがっつりコーディングするときは {% extend %}
というような使い方になりそうです。
date
はデフォルトでは new Date();
なので、記事の公開日(更新日)を指定したい場合に上書きする形になります。
pagenation
や tags
はそれぞれ Pagination、Collections を読んでみてください。記事一覧なんかに使えそうです。
そしてここまで長くなりましたが、$ npx eleventy
を実行すると下記のように出力されます。
htdocs/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta name="viewport" content="width=device-width">
<title>11ty test site</title>
<meta name="description" content="ページ概要がはいります。">
<link rel="stylesheet" href="/css/styles.css">
<link rel="stylesheet" href="/css/top.css">
</head>
<body>
<header>ヘッダー</header>
<main>
<h2>トップページのコンテンツ</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. <br>
Adipisci tempora asperiores earum, <br>
aspernatur pariatur officia cumque mollitia quos ab sapiente porro nulla cupiditate. <br>
Animi unde voluptatibus hic aspernatur deleniti minima.</p>
<p>Update:2018年5月18日</p>
</main>
<footer>フッター</footer>
<script src="/js/common.bundle.js"></script>
<script src="/js/top.bundle.js"></script>
</body>
</html>
layout
を使ったパターン
ちなみに layout
を使った場合はこんな感じになります。
プラグインで指定したシンタックスハイライトを活かすために、prismのテーマCSSを追加で読み込んでいます。
テンプレート:src/_includes/blog.njk
{% extends 'base.njk' %}
{% block css -%}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.14.0/themes/prism.min.css">
{%- endblock %}
{% block main -%}
{{ content | safe }}
{%- endblock %}
原稿1:src/html/blog/test.md
---
title: マークダウンで書く記事のテスト
description: ページ概要がはいります。
layout: blog.njk
---
# {{title}}
{{ desctiption }}
```html
<p>コードが入ります</p>
```
## 表
| 表見出し | 表見出し | 表見出し |
|:-- | :--: | --:|
|左揃え| 中央 | 右揃え |
原稿2:src/html/blog/test2.md
---
title: マークダウンで書く記事のテスト2
description: ページ概要がはいります。
layout: <blog class="njk"></blog>
permalink: /blog/test2.html
---
(以下略)
結果1:htdocs/blog/test/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta name="viewport" content="width=device-width">
<title>マークダウンで書く記事のテスト | 11ty test site</title>
<meta name="description" content="ページ概要がはいります。">
<link rel="stylesheet" href="/css/styles.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.14.0/themes/prism.min.css">
</head>
<body>
<header>ヘッダー</header>
<main>
<h1>マークダウンで書く記事のテスト</h1>
<pre class="language-html"><code class="language-html">
<div class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>コードが入ります<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></div>
</code></pre>
<h2>表</h2>
<table>
<thead>
<tr>
<th style="text-align:left">表見出し</th>
<th style="text-align:center">表見出し</th>
<th style="text-align:right">表見出し</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">左揃え</td>
<td style="text-align:center">中央</td>
<td style="text-align:right">右揃え</td>
</tr>
</tbody>
</table>
<p>Update:2018年5月18日</p>
</main>
<footer>フッター</footer>
<script src="/js/common.bundle.js"></script>
</body>
</html>
結果2:htdocs/blog/test2.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta name="viewport" content="width=device-width">
<title>マークダウンで書く記事のテスト2 | 11ty test site</title>
(以下略)
終わりに
実際制作するとなると11tyだけでなく、Sassや Webpack なども使う必要があるので、gulp-shell などで連携させる感じになると思いますが、それはまた別の機会に。
参考
簡単な使い方については、11ty公式のMediumにも記載があります。(level3は執筆中のようです)