NKSSG
NakaKen Static Site Generator

NKSSGのしくみ

ここでは、NKSSGがどのようにサイトを作っているか、仕組みを説明していきます。

サイト作成の大まかな流れ

NKSSGでは、nkssg buildなどのコマンドを受け取ると、一からサイトの作成を行います。

サイト(Site)の作成は、大きく分けて次の4ステップです。

  • サイトの初期化
  • サイトの準備
  • サイトの更新
  • サイトの出力

buildだけでなく、serveなども同じ流れです。実は設定項目が少し違うだけで、すべてbuildの内容が処理されています。

個別ページとアーカイブページ

サイトは、個別ページ(Single)アーカイブページ(Archive)があり、サイトの作成はこれらの作成がメインです。

サイト作成の処理から少し話が脱線しますが、処理を理解する上ではこれらを理解しておくことは重要なので、以下では、それぞれの説明を行います。

個別ページ(Single)とは、サイト内の各記事のことです。docsフォルダ内のHTMLファイルやMarkdownファイル1つ1つが個別ページに対応しています。

各個別ページには、投稿タイプ(post_type)が設定されています。同じ投稿タイプごとに、いろいろな設定が行えるようになります。docsフォルダの直下にあるフォルダ名が、投稿タイプ名となります。つまり、docsフォルダの直下にあるフォルダごとに、記事が投稿タイプでグルーピングされている、ということです。

アーカイブページ(Archive)とは、記事の一覧ページのことです。アーカイブページには、次の3つの種類があります。

  • 日付アーカイブ
  • セクションアーカイブ
  • タクソノミーアーカイブ

日付アーカイブ(date archive)とは、記事を日付別に並べたものです。年ごと、月ごとに一覧にしたものです。

セクションアーカイブ(section archive)とは、記事を階層ごとに並べたものです。フォルダごとに記事を一覧にしたものです。

タクソノミーアーカイブ(taxonomy archive)とは、カテゴリーやタグなど、記事の分類項目ごとに並べたものです。

日付アーカイブとセクションアーカイブは、投稿タイプごとにどちらを作るか設定します。ブログのように投稿順に並べたほうがいいなら日付アーカイブを、階層型サイトのように並べたい場合はセクションアーカイブを選択します。両方を選ぶことはできません。どちらも作らないようにすることはできます。

タクソノミーアーカイブは、日付・セクションとは異なり、サイト内の記事全体を分類します。投稿タイプが異なっていても、まとめて表示されます。

例えば、料理関係のサイトを作っていたとして、いろんなレシピなどの記事を載せる "recipe" という投稿タイプ(セクション型)と、日々の食事の記事を載せる "blog" という投稿タイプ(日付型)があるとしましょう。このとき、"wine" というタグのついた記事一覧(タクソノミーアーカイブ)には、ワインを使ったレシピや、ワインと一緒に料理を楽しんでいるブログ記事が表示されることになります。

逆にいうと、「このタグのついている recipe 記事一覧」のようなことはできません。もし投稿タイプごとに表示したいなら、 "recipe" だけで使うタクソノミーと "blog" だけで使うタクソノミーを設定するようにしましょう。ただ、このようにするなら、そもそもサイトを分けたほうがいいかもしれません。

サイトの初期化

さて、サイト作成の処理に戻ります。

サイト作成では、まず初期化を行います。ここでは、次の処理を行っています。

  • テーマの読み込み
  • 投稿タイプの初期化
  • 個別ページの初期化
  • アーカイブページの初期化

テーマの読み込みでは、テーマファイルのあるフォルダを調べたり、設定ファイルの内容を読みこんだりします。

投稿タイプの初期化では、設定ファイルから投稿タイプの設定を読みこみ、docsフォルダの直下にあるフォルダを投稿タイプに登録します。投稿タイプの順番、アーカイブタイプ(日付型かセクション型か)、スラッグ、"with front" の設定をします。

投稿タイプは、設定ファイルにある順番に登録され、その後にdocsフォルダ直下のフォルダが名前順に登録されます。この順番は、記事の並び替えの順番に反映されます。

スラッグは、URLで使う名前です。例えば、投稿タイプを日本語にしたいがURLは英語にしたい場合に使います。

"with front"は、アーカイブのURLに投稿タイプを入れるかどうかの設定です。True なら入れる、Falseなら入れない、です。

この設定は、トップページに何を表示するかに関わっています。デフォルトでは、postという投稿タイプ(日付型)に対してwith_front = Falseと設定されています。これにより、postの日付アーカイブがトップページに表示されるようになっています。

トップページを別のものにしたい場合は、テーマ内でhome.htmlを作って表示するものを作り、投稿タイプpostの設定をwith_front = Trueに変えます。こうすると、/post/にアクセスすれば、postの日付アーカイブページが表示されるようになります。

トップページに表示するものがあるように、投稿タイプに対して"with front"の設定を行っています。

個別ページの初期化では、docsフォルダ内の各ファイルに対して、対象のものを抜き出して、ファイルパスからわかる情報を取得します。

アーカイブページの初期化では、この段階ではインスタンスを作るだけで特に何もしていません。

サイトの準備

サイトの準備では、主に、各個別ページのファイルから中身を読みこむ処理を行います。

個別ページのファイルに対し、フロントマターの内容を読みこんで、ステータス、日付、タイトル、サムネイル、本文などの情報を読みこみます。ファイルがMarkdownの場合、MarkdownHTMLに変換する処理はこのタイミングで行います。

これらの処理を、全ファイルに対して行います。その後、ドラフトの記事を除外して、並び替えを行います。

記事の並び替えのルールは、次のようになっています。2つの記事の順番を比較する際、次の内容を上から順番に判定していきます。

  • 投稿タイプが異なる場合は、投稿タイプの順番通り。
  • どちらかのorderがマイナスなら、小さい方が前。
  • 日付アーカイブなら、公開日時の新しい方が前。同じならファイル名の順番。
  • フォルダが違うなら、フォルダ名の順番。
  • どちらかのファイル名がindexなら、indexのほうが前。
  • orderが設定されてるなら、小さい方が前。
  • ファイルのパスの順番。

orderは、記事のフロントマターで設定できます。初期値は0です。マイナスの場合は、同じ投稿タイプの中で一番はじめに表示できるので、WordPressでいう「先頭固定表示」を行うことができます。また、複数の記事でマイナスにすれば、複数の記事を先頭固定表示にすることもできます。

ここまでが、サイト準備の主な内容です。各ファイルの中でわかる情報を読みこんでいますが、この段階では以下の内容が確定していません。

  • アーカイブとの対応
  • タクソノミー
  • URL
  • 本文

アーカイブとの対応は、そもそもアーカイブの設定をまだ行っていないのでできていません。あとのステップで行います。

タクソノミーの処理も、アーカイブの設定が終わるまではできません。これもあとのステップで行います。

URLは、パーマリンクにカテゴリーなどのタクソノミーの情報を使う場合があるので、この段階では処理を行いません。

本文は、このステップでMarkdownの処理を行っていますが、例えば、他の内部ページへのリンクなどが記事内にあれば、その処理を行う必要があります。

サイトの更新

サイトの更新では、次の処理を行います。

  • アーカイブページの準備
  • 個別ページのURL更新
  • アーカイブページURL更新
  • 個別ページのHTML更新
  • アーカイブページHTML更新

出力用の情報を確定するための処理がメインです。

アーカイブページの準備

アーカイブページの準備は、各アーカイブ間の関係の読み込みと、各アーカイブと個別ページとの紐づけが主な処理内容です。

まず、各投稿タイプごとに、日付アーカイブ・セクションアーカイブの準備を行います。

日付アーカイブは、各記事の公開日時の情報をもとに、年と月のアーカイブを作成していきます。dateアーカイブをルートアーカイブとして、その下に年アーカイブ、その下に月アーカイブを作ります。

セクションアーカイブは、各フォルダの情報をもとにアーカイブを作成していきます。投稿タイプのslugを名前にしたアーカイブをルートアーカイブとして、フォルダの上下関係と連動して親子関係を設定していきます。

これらを各投稿タイプごとに行った後、タクソノミーアーカイブの準備を行います。

タクソノミーアーカイブは、設定ファイルの中で定義された情報をもとにアーカイブを作成していきます。タームに親が設定されていれば、その親子関係を反映していきます。各記事のタクソノミーの設定からは親子関係はわからないので、各記事のタクソノミーからタクソノミーアーカイブを作るわけではない点に注意してください。

こうしてアーカイブの情報を設定していきます。この後、親子関係の設定を行うことで、以下の内容が更新されます。

  • parent: 親アーカイブ
    • 自分の親。
    • ルートアーカイブの場合はNoneが入っている。
  • parents: 祖先アーカイブ
    • 自分の親たち。
    • ルートから順番にリストになっている。
    • パンくずリストなどで使うことを想定。
  • children: 子どもアーカイブ
    • 自分の子どもたち。
    • 「AをBの親とする」設定をしていれば、「BはAの子ども」も自動で設定される。

続いて、アーカイブと個別ページとの紐づけを行うことで、以下の内容が更新されます。

  • singles
    • 直属の個別ページのリスト。
  • singles_all
    • 自分以降の子孫アーカイブに属する個別ページのリスト。
  • single_index(セクションアーカイブのみ)
    • 対応するindexファイル。

例えば、旅行の記事をたくさん書いているブログがあったとします。このとき、エリアごとにタグが付いていたとしましょう。「アジア」「日本」「東京」という順で親子関係ができているとします。

このとき、「東京」というタグがついている記事があったとします。この記事は、「東京」アーカイブのsinglesに入っていますが、「日本」アーカイブや「アジア」アーカイブのsinglesには入っていません。しかし、アジア旅行や日本旅行について知りたい人に、東京旅行の記事も見せたいでしょう。そのため、singles_allというものも作っています。日本・アジアアーカイブのsingles_allには、東京のタグしかついていない記事も含まれます。

日付アーカイブやタクソノミーアーカイブでは、singles_allすべてが一覧に表示されるようになっています。一方、セクションアーカイブでは、singlesだけが一覧に表示されるようになっています。

single_indexは、セクションアーカイブだけに設定できるものです。フォルダ内にファイル名がindexのものを入れておくと、このフォルダに対応するアーカイブとこのファイルとが紐づきます。indexファイルにタイトルやURL、本文などを書いておけば、それをアーカイブページに反映させることができます。

URLの更新

URLと出力先を更新していきます。plubicフォルダに保存されるHTMLファイルの場所がそのままURLに反映されます。

個別ページのURLは、まず、ファイル内のフロントマターにあるurlの設定が最優先されます。

これがない場合は、投稿タイプのパーマリンクの設定により計算されます。パーマリンクの設定がない場合は、フォルダ構造がそのままURLに反映されます。

パーマリンクがある場合、公開日付、ファイル名、スラッグ、タクソノミーを反映していきます。

HTMLの更新

最後に、個別ページとアーカイブページの HTML の中身を更新していきます。

まず、個別ページは、本文を Jinja で変換してから、全体を再び Jinja で変換します。本文では、ショートコードやフロントマターの内容が反映されます。全体の変換では、single.htmlのテンプレートが使われます。

この時点では、各ファイルのURLの計算が済んでいるので、サイドバーなども含め、すべて計算できます。

アーカイブページは、タイプに合わせて、記事の一覧に何を含めるかが変わります。

  • 日付:singles_all
  • セクション:singles
  • タクソノミー:singles_all

この記事一覧を、分割して各ページを作成します。1つのページに載せる数は、日付とタクソノミーの場合は最大10個、セクションはすべて、となっていますが、設定で変えることができます。

また、セクションの場合、対応するフォルダ内にindex.htmlがあれば、その内容がアーカイブページに渡されます。そのセクションの説明などを入れることができます。

サイト出力

サイト出力では、次の処理を行います。

  • 静的ファイルの出力
  • 個別ページの出力
  • アーカイブページの出力
  • その他ページの出力

静的ファイルの出力では、staticフォルダに入っているものを、そのまますべてpublicフォルダに移します。staticフォルダには、画像、css、js、json、テキストファイルなどが入っていることを想定しています。出力先のパスは、staticpublicに変えたものになります。フォルダ構造がそのまま反映します。

また、themes内の静的ファイルも移動します。移動するファイルは、テーマ内のフォルダにあるもののうち、設定ファイルで指定したファイルです。static_includeで指定したもののうち、static_excludeで指定したもの以外が移動対象となります。cssやjs、テーマのロゴ画像などを想定しています。

出力先のパスは、次のようになります。

themes {theme-name} img sample.png

puglic themes {theme-name} img sample.png

個別ページ・アーカイブページの出力では、すでに計算している出力先パスに、すでに計算しているHTMLファイルの中身を書き出します。

また、このタイミングで、サムネイル画像の移動とリダイレクト用ファイルの作成を行います。

サムネイル画像は、thumbフォルダの下に保存されます。記事の公開日時に応じて年と月のフォルダができ、その下に移動します。

リダイレクト用ファイルは、その記事へリダイレクトされるようにかかれたHTMLファイルが、指定した場所に作成されます。 例

その他ページの出力では、個別ページ・アーカイブページに該当しないページの出力を行います。デフォルトでは、次の4つのファイルを作ろうとします。

  • home.html
  • 404.html
  • sitemap.html
  • sitemap.xml

いずれも、テーマ内にファイルがあれば作成されます。ない場合は無視されます。

home.htmlは、トップページ用のテンプレートです。トップページのみデザインを変えたい場合に使います。これがない場合は、トップページには、メイン投稿タイプのアーカイブページが表示されます。

404.htmlは、ページがない場合に表示されるページです。しかし、存在しないページにアクセスがあった場合に、実際にこれが表示されるようにするには、サーバー側に追加の設定が必要です。TODO。このファイルを作るだけでは表示されません。

sitemap.htmlsitemap.xmlは、サイトマップ用のファイルです。どちらも、1つのファイルにすべてを出力するようになっています。

これ以外にも、テーマでextra_pagesを設定し、テンプレートを作っていれば、このタイミングで作成されます。テンプレートには、個別ページ、アーカイブ、サイト設定、テーマ設定のデータが渡されるので、それらを使います。

出力先は、テーマの基準フォルダから見た相対パスが、publicフォルダから見た相対パスになります。 例

テンプレートは、HTMLファイルである必要はありません。そのため、例えば、出力した個別ページ・アーカイブページのデータを一覧にしたlog.txtファイルを作ったり、ファイルのデータを入れたjsonファイルを作ることも可能です。