books.ginpei.dev GitHub

Eleventy

静的サイトジェネレーター。

初期設定

インストール

$ npm install -D @11ty/eleventy

ビルド

何か markdown ファイルを用意しておく。場所は差し当たりどこでも良い。パスとファイル名から URL が用意される。

ビルド。

$ npx @11ty/eleventy

_site/ 以下に出力される。

サーバー

--serve を与えるとウェブサーバーが起動する。ファイル変更を監視し自動で再出力、ブラウザーで開いていれば再読み込みまでしてくれる。便利。

ポートは 8080 。あるいは --port 3000 のように指定。

スクリプト

package.json:

{
  "scripts": {
    "build": "eleventy",
    "start": "eleventy --serve"

テンプレートとレイアウト

_includes/ 以下に *.njk を設置。layout: で指定。

なお njk は Nunjucks (ヌンチャク)。

実装

_includes/basicLayout.njk:

---
title: (Not titled)
---

<!DOCTYPE html>
<html lang="en">
<head>
  <title>{{ title }}</title>
</head>
<body>
  <main>
    {{ content | safe }}
  </main>
</body>
</html>

利用

---
layout: basicLayout.njk
title: Hello World!
---

# Hello World!

変数とエスケープ

{{xxx}} で Nunjucks の変数の埋め込みになる。{{title}} とか。

文章やコード例として画面に {{ を表示したい場合は {% raw %}{% endraw %} で括る。

カスタマイズ

.eleventy.js を設置してカスタマイズできる。

例:

const syntaxHighlight = require("@11ty/eleventy-plugin-syntaxhighlight");
const markdownIt = require("markdown-it");

module.exports = function (eleventyConfig) {
  eleventyConfig.setLibrary(
    "md",
    markdownIt({
      html: true,
      linkify: true,
    })
  );

  eleventyConfig.addPlugin(syntaxHighlight);
};

URL を自動リンク

markdown-it の linkify 機能を有効化する。

文章中に出現する https://example.com のような文字列を URL としてリンクさせる。スキームなしの example.com には反応しない。

const markdownIt = require("markdown-it");

// …

eleventyConfig.setLibrary(
  "md",
  markdownIt({
    html: true,
    linkify: true,
  })
);

見出しにリンク

markdown-it のプラグイン markdown-it-anchor を使う。

const markdownItAnchor = require("markdown-it-anchor");
const md = markdownIt({});

…

md.use(markdownItAnchor, {
  permalink: markdownItAnchor.permalink.headerLink({
    safariReaderFix: true,
  }),
});

permalink の設定でリンクの方法を指定できる。("🔗" を表示する等。) ドキュメント参照

ソースコードのシンタックスハイライト

公式プラグインを利用。中身は Prism.js らしい。

const syntaxHighlight = require("@11ty/eleventy-plugin-syntaxhighlight");

// …

eleventyConfig.addPlugin(syntaxHighlight);

加えてハイライト用の CSS をレイアウトで読み込む。

<link rel="stylesheet" href="https://unpkg.com/prismjs@1.28.0/themes/prism-okaidia.css">

テーマ一覧。

更新日を表示

更新日を自動で得る方法はないので、各記事 *.mddate を設定してテンプレートで表示する。書式は YAML が対応する YYYY-MM-DD 等にする。(例えば月を 1 桁で書くと駄目。)

---
title: なんかすごいおもしろい記事
date: 2021-05-21
---
{% if date %}
  <time>{{ date }}</time>
{% endif %}

書式を守ると JavaScript の Date オブジェクトになる。.eleventy.js に整形フィルターを追加する。

module.exports = function (eleventyConfig) {
  eleventyConfig.addFilter("toDate", (v) => articleDateToString(v));
/**
 * @param {unknown} date
 */
function articleDateToString(date) {
  if (!(date instanceof Date)) {
    throw new Error(
      "[articleDateToString] Date object expected but received " +
        `${typeof date}: ${JSON.stringify(date)}`
    );
  }

  return [
    date.getFullYear(),
    toTwoDigits(date.getMonth() + 1),
    toTwoDigits(date.getDate()),
  ].join("-");
}

/**
 * @param {number} number
 */
function toTwoDigits(number) {
  return number.toString().padStart(2, "0");
}
{% if date %}
<time class="baseLayout-time">
  {{ date | toDate }}
</time>
{% endif %}

記事のファイルパスから目次ページへのリンク作成

プロジェクトのルートに books/ ディレクトリーを用意して、その下に ja/ とか en/ とか置く感じの想定。

module.exports = function (eleventyConfig) {
  eleventyConfig.addFilter("toHomePath", (v) => toHomePath(v));
/**
 * @param {string} path
 * @example
 * // <a href="{{ page.inputPath | toHomePath }}">Home</a>
 */
function toHomePath(path) {
  if (typeof path !== "string") {
    throw new Error(
      "[getPathLang] String expected but received " +
        `${typeof path}: ${JSON.stringify(path)}`
    );
  }

  const [cur, books, lang] = path.split("/");
  if (cur !== "." || books !== "books" || lang.length !== 2) {
    return "/";
  }

  return `/${lang}/`;
}
<a href="{{ page.inputPath | toHomePath }}">Home</a>