onCreateNodeの実装

はじめに

前回までの準備をもとに実装していきます。
今回は、onCreateNode の実装を行います。
onCreateNode では、ファイル名から slug を作成し、指定されたnodeのfieldsキーの下にカスタムノードを作成します。 作成したものは GraphQL で取得可能です。

手順

以下の手順で実装します。

  1. node.internal.type'Markdown' のものを処理対象とする
  2. frontmatterslug を参照
  3. 値が存在すればその値でなければファイル名から slug を生成
  4. createNodeFiled を使用し fieldsslug の値を設定

編集するファイルは以下です。

./src/gatsby-helpers/onCreateNode.ts

onCreateNodeの実装

型の定義

作成する必要のある型は以下です。

  • onCreateNode の引数
  • gatsby-transformer-remark が追加した frontmatter

onCreateNodeで渡ってくるオブジェクトのうち、以下のものを使用します。

  • node
  • getNode
  • actions

使用する型は、Gatsby の型定義ファイルを参考に以下の様に定義しました。

interface OnCreateNodeArgs {
  node: Node;
  getNode: Function;
  actions: Actions;
}

上記のnodeには、gatsby-transformer-remarkによって Markdown ファイルからパースされた frontmatter 付加されています。 frontmatter の部分の型については、今回はアクセスするのが slug だけなので、slug のみ定義します。

interface MDFrontMatter {
  frontmatter: {
    slug?: string;
  };
}

今後frontmatterへアクセスする値が増えたときに、型定義のところだけを抜き出したファイルを作成し使用するようにします。

OnCreateNodeArgsnode に関しては、付加された frontmatter を合成して以下のように変更します。
変更箇所をハイライトします。

interface OnCreateNodeArgs {
  node: MDFrontMatter & Node;  getNode: Function;
  actions: Actions;
}

slugの生成

onCreateNode では、slug の生成とカスタムノードの追加を行います。
slug はページのURLをわかりやすいものにするために作成します。 Gatsbyのチュートリアルではファイル名からslugを作成していましたが、日本語のファイル名が想定される場合は、必ずしも望んだ結果が得られるとは限りません。
そのため Markdown ファイルの先頭に frontmatter を設け、そこに記述があった場合はそのslugを採用することにします。 また、frontmatter で定義された slug については、人為的なミスにより値が重複してしまう可能性がありますが、今の段階ではそのことは考慮しないものとします。
frontmater には以下の記述があることを想定します。

---
title: "H1に相当するタイトルはここで定義"
date: 2019-07-31
slug: "this-is-custom-slug"
---

frontmatterslug の記述があればそれを採用し、なければファイル名から生成するようにします。 その部分の処理は以下の様になります。

const slug = isAvailableSlug(node)
  ? `/${node.frontmatter.slug}/`
  : createFilePath({ node, getNode, basePath: 'posts' });

ここで isAvailableSlug は以下のようにしています。

const isAvailableSlug = (node: MDFrontMatter): boolean => {
  if (!node.frontmatter.slug) {
    return false;
  }
  return node.frontmatter.slug.length > 0;
};

nodeFieldの作成

これらを用いて createNodeFiled を使用して slug を作成します。
この値は、 createPages でページを追加する際に使用します。

createNodeField({
  node,
  name: 'slug',
  value: slug,
});

コード全体

コード全体は以下の様になりました。

// ./src/gatsby-helpers/onCreateNode.ts
import { createFilePath } from 'gatsby-source-filesystem';
import { Actions, Node } from 'gatsby';

interface MDFrontMatter {
  frontmatter: {
    slug?: string;
  };
}

interface OnCreateNodeArgs {
  node: MDFrontMatter & Node;
  getNode: Function;
  actions: Actions;
}

const isAvailableSlug = (node: MDFrontMatter): boolean => {
  if (!node.frontmatter.slug) {
    return false;
  }
  return node.frontmatter.slug.length > 0;
};

export const onCreateNode = ({
  node,
  getNode,
  actions,
}: OnCreateNodeArgs): void => {
  const { createNodeField } = actions;
  if (node.internal.type === 'MarkdownRemark') {
    const slug = isAvailableSlug(node)
      ? `/${node.frontmatter.slug}/`
      : createFilePath({ node, getNode, basePath: 'posts' });

    createNodeField({
      node,
      name: 'slug',
      value: slug,
    });
  }
};

長くなってきたので、残りは次回に記述します。

おわりに

ここまでで、createPages を実装するための準備は整いました。 次回から実際にページを追加する処理を実装していきます。