copy_people copy_things copy_matter copy_heart copy_txt ttl_topics ico_blank ico_facebook ico_twitter ico_hatebu ico_index

2018/05/18

11ty(eleventy)で静的サイト構築

Written by Shunsuke Okoshi

  • HTML
  • node
  • Nunjucks

INDEX

    Githubの★の数などで静的サイトジェネレーターを比較できる StaticGenを見ていたら、Nunjucksが使える 11ty(eleventy)というのを見つけました。

    そういうわけでオオコシです。

    静的サイトジェネレーターというと JekyllHexo などが有名かと思うんですが、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"
        }
      };
    });
    

    LuxonDate オブジェクトを扱いやすくするライブラリです。
    eleventy-plugin-syntaxhighlight は、コードブロックがあった場合、ビルド時に Prism形式に変換するものです。Prism.js本体は不要で、Prism用のテーマCSSを読み込むだけでシンタックスハイライトが適用されます。
    dir.data dir.includesdir.input から見たパスになります。

    グローバル変数、テンプレートの作成〜出力

    まずはサイト全体で使うグローバル変数を .eleventy.jsdir.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.jsaddFilter で追加した独自のフィルターも使えます。

    そして、上記のテンプレートを読み込むコンテンツ側はこんな感じになります。

    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形式で設定します。
    titledescription は普通の変数なんですが、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 に任意のパスを書くことになります。

    layoutdir.includes 配下を参照してページに適用するテンプレートを選択します。
    ただし、こちらでは Nunjucksの {% block %} が使えませんでした。
    そのため、Markdown などで記事を書くときは layout、Nunjucks形式でがっつりコーディングするときは {% extend %} というような使い方になりそうです。

    date はデフォルトでは new Date(); なので、記事の公開日(更新日)を指定したい場合に上書きする形になります。

    pagenationtags はそれぞれ PaginationCollections を読んでみてください。記事一覧なんかに使えそうです。

    そしてここまで長くなりましたが、$ 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">&lt;</span>p</span><span class="token punctuation">&gt;</span></span>コードが入ります<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</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だけでなく、SassWebpack なども使う必要があるので、gulp-shell などで連携させる感じになると思いますが、それはまた別の機会に。

    参考

    簡単な使い方については、11ty公式のMediumにも記載があります。(level3は執筆中のようです)

    CONTACT

    お仕事のご相談や、弊社についてのご質問や
    ご要望など、お気軽にお問い合わせください。

    View