勉強した内容をDocusaurus(v2)でドキュメント化してみる

勉強したことは何かしらメモしたり、記録を残したりすることが多いと思うのですが、どうせ後から見直すなら見やすい方がいいですよね。
ドキュメント化して整理するべくDocusaurusを使ってみました。

Docusaurusとは? #

Docusaurus 公式ドキュメント トップ画面

Facebook製の、Reactをベースとしたドキュメント特化の静的サイトジェネレータです。
その特性の通り、主にOSSのドキュメントサイトの作成に使用されています。

なぜDocusaurus? #

昨年末の記事で書いていたので以下、引用します。

個人勉強のコードは学習記録の可視化にもなるということで、上記にも書いているTILリポジトリにコミットしています。これは今後も継続していきます。

TILとは
「Today I Learned」の略で、Github上にTILというリポジトリを作成してそこに今日覚えたことを書いていくというものです。

※TILリポジトリに関する過去記事はこちら

ただ、途中まではいい感じかなーと思っていたのですが、だんだん散らかってきたといいますが、勉強した内容をドキュメントみたいにちゃんとまとめたいという思いが出てきました。

そこで、OSSのドキュメントがどうなっているか見てみようということで、create-react-appのドキュメントのコードを見ていたところ、どうもDocusaurusというドキュメント特化の静的サイトジェネレータを使っていることがわかりました。

今後、これを導入して勉強したことのドキュメントを作ろうか検討中です。
言語のバージョンアップで仕様が変わるなど、ドキュメントの更新や整備が大変では…という懸念点もありますが、まずはやってみようかと。
自己満足みたいなところがあるので、このドキュメントを評価材料にしてもらおうとかはあんまり考えていません。というか、評価材料になりえるものなんでしょうか…?

簡潔に言ってしまえば、冒頭に書いた通り、勉強したことを後から見返しやすいようにしたいからドキュメント化してみよう。といった感じです。

ということで、早速導入してみました。

Docusaurusの導入 #

使用バージョンについて #

安定版としては現時点で1.14.4が最新です。
docusaurus-initという雛形ドキュメントを作成してくれるCLIツールがあるので、比較的楽に導入することができます。
また、これで作成された雛形ドキュメントの中にはDockerに関するファイルも含まれ、ローカルでDockerでサーバを立ち上げて作業するということができるようになっています。

便利だなーと思いながら導入したのですが、実際に使ってみるとちょっと気になる点がありました。

Docusaurusの1系にはi18nによる翻訳機能が搭載されているのですが、どうも英語でドキュメントを書いていく前提の作りになっているようでした。最初に英語で書いて、それを多言語に翻訳していくような。

各ドキュメントのタイトルといった、翻訳対象になりそうなものを検知してi18n/en.jsonファイルにまとめてくれる機能があり、便利ではあるものの、このファイルはあくまで英語用のファイルです。ドキュメントを日本語で書いていようとi18n/en.jsonファイルにまとめられます。

GitHubのコードを見たりもしましたが、ドキュメントを書いていく上でのデフォルト言語は変えられなさそうでした。

そこでいろいろと調べているうちに、開発中のalpha版ではあるものの、2系も使えることに気づきました。
2系は一から作り直されているようで、翻訳機能はまだ搭載されていませんが、それ以外の1系の主な機能は移植されているとのこと。
現状日本語でしか作らないつもりなので十分です。

すでに2系を使われているOSSドキュメントもありますし、むしろ、2系を使った方がいいのではないか?ということで、今回は2系を使うことにしました。
なお、執筆時点での最新バージョンは2.0.0-alpha.40です。

2020/05/03追記
2.0.0-alpha.54に対応して一部修正しました。

導入手順 #

前提として以下が必要です。

  • Node.js:10.9.0以上
  • Yarn:1.5以上

1.Docusaurus導入のCLIツールの導入

$ yarn global add @docusaurus/init@next

導入したCLIツールは以下のようにして使うことができます

$ docusaurus-init init [name] [template]

2.雛形ドキュメントの作成(classicテンプレート使用例)

$ docusaurus-init init website classic

以下のように雛形が作成されます。

website
├── blog
│   ├── 2019-05-28-hola.md
│   ├── 2019-05-29-hello-world.md
│   └── 2020-05-30-welcome.md
├── docs
│   ├── doc1.md
│   ├── doc2.md
│   ├── doc3.md
│   └── mdx.md
├── node_modules
├── src
│   ├── css
│   │   └── custom.css
│   └── pages
│       ├── index.js
│       └── styles.module.css
├── static
│   └── img
│        ├── favicon.ico
│        ├── logo.svg
│        ├── undraw_docusaurus_mountain.svg
│        ├── undraw_docusaurus_react.svg
│        └── undraw_docusaurus_tree.svg
├──.gitignore
├── docusaurus.config.js
├── package.json
├── README.md
├── sidebars.js
└── yarn.lock

このclassicテンプレートには、基本的なテーマやプラグインがまとまったプリセットである@docusaurus/preset-classicが含まれています。

@docusaurus/preset-classicに含まれているもの

  • @docusaurus/theme-classic
  • @docusaurus/theme-search-algolia
  • @docusaurus/plugin-content-docs
  • @docusaurus/plugin-content-blog
  • @docusaurus/plugin-content-pages
  • @docusaurus/plugin-google-analytics
  • @docusaurus/plugin-google-gtag
  • @docusaurus/plugin-sitemap

3.サーバの起動

$ yarn start

サーバを起動すると、自動でブラウザが立ち上がり、localhost:3000が開きます。

以下のような画面が表示されればOKです。
Docusaurus 2系の雛形ドキュメントのトップ画面


ちなみに最初はDockerで環境を作ろうとして、コンテナ内でサーバ起動はできたのですが、なぜかブラウザからアクセスできないという現象が起きました。
他のコンテナでReactなどのサーバ起動は問題なかったので、Docker自体の問題ではなさそうですが、イマイチ原因がわかりませんでした。

Docusaurusの2系のサーバの仕組みでは、まだDockerに対応してなかったんでしょうか…。まぁ、仮にそうだとしても開発中のバージョンなので仕方ないかなと。
自分は素直にWSLでサーバを起動することにしました。

Docusaurusの仕組み #

Docusaurusではドキュメント作成のほかに、ブログを書くこともできます。
確かにOSSだと開発ブログみたいなものがあることが多いですよね。

それとホットリロードに対応しているので、基本的には変更が即座に反映されます(一部設定はサーバを再起動しないといけないときもありました)

※一応補足として、今回記述しているドキュメントサイトの設定方法は、@docusaurus/preset-classicプリセットに含まれる@docusaurus/theme-classicテーマでのやり方であることに注意です。

ドキュメントサイトの基本設定 #

docusaurus.config.jsにまとめられています。

module.exports = {
  title: 'My Site',
  tagline: 'The tagline of my site',
  url: 'https://your-docusaurus-test-site.com',
  baseUrl: '/',
  favicon: 'img/favicon.ico',
  organizationName: 'facebook', // Usually your GitHub org/user name.
  projectName: 'docusaurus', // Usually your repo name.
  themeConfig: {
    navbar: {
      title: 'My Site',
      logo: {
        alt: 'My Site Logo',
        src: 'img/logo.svg',
      },
      links: [
        {
          to: 'docs/doc1',
          activeBasePath: 'docs',
          label: 'Docs',
          position: 'left',
        },
        {to: 'blog', label: 'Blog', position: 'left'},
        {
          href: 'https://github.com/facebook/docusaurus',
          label: 'GitHub',
          position: 'right',
        },
      ],
    },
    footer: {
      style: 'dark',
      links: [
        {
          title: 'Docs',
          items: [
            {
              label: 'Style Guide',
              to: 'docs/doc1',
            },
            {
              label: 'Second Doc',
              to: 'docs/doc2',
            },
          ],
        },
        {
          title: 'Community',
          items: [
            {
              label: 'Stack Overflow',
              href: 'https://stackoverflow.com/questions/tagged/docusaurus',
            },
            {
              label: 'Discord',
              href: 'https://discordapp.com/invite/docusaurus',
            },
            {
              label: 'Twitter',
              href: 'https://twitter.com/docusaurus',
            },
          ],
        },
        {
          title: 'More',
          items: [
            {
              label: 'Blog',
              to: 'blog',
            },
            {
              label: 'GitHub',
              href: 'https://github.com/facebook/docusaurus',
            },
          ],
        },
      ],
      copyright: `Copyright © ${new Date().getFullYear()} My Project, Inc. Built with Docusaurus.`,
    },
  },
  presets: [
    [
      '@docusaurus/preset-classic',
      {
        docs: {
          sidebarPath: require.resolve('./sidebars.js'),
          // Please change this to your repo.
          editUrl:
            'https://github.com/facebook/docusaurus/edit/master/website/',
        },
        blog: {
          showReadingTime: true,
          // Please change this to your repo.
          editUrl:
            'https://github.com/facebook/docusaurus/edit/master/website/blog/',
        },
        theme: {
          customCss: require.resolve('./src/css/custom.css'),
        },
      },
    ],
  ],
};

ナビゲーションバー #

docusaurus.config.jsのthemeConfigのなかに設定があります。

react-router-domのLinkタグの書き方に似ていますね。
toには相対パスでURLを指定します。
内部リンクだけでなく、外部リンクも設定することができます。

docusaurus.config.js

※一部抜粋
navbar: {
  title: 'My Site',
  logo: {
    alt: 'My Site Logo',
    src: 'img/logo.svg',
  },
  links: [
    {
      to: 'docs/doc1',
      activeBasePath: 'docs',
      label: 'Docs',
      position: 'left',
    },
    {to: 'blog', label: 'Blog', position: 'left'},
    {
      href: 'https://github.com/facebook/docusaurus',
      label: 'GitHub',
      position: 'right',
    },
  ],
},

画面側
ナビゲーションバー

サイドバー #

sidebars.jsに設定があります。

ここで指定しているdoc1などは各ドキュメントのマークダウンファイルの先頭で指定しているidになります。
ここでサイドバーとドキュメントの紐づけが行われ、表示上ではsidebar_labelが使われます。

sidebars.js

module.exports = {
  someSidebar: {
    Docusaurus: ['doc1', 'doc2', 'doc3'],
    Features: ['mdx'],
  },
};

ドキュメントのマークダウンファイル

※一部抜粋
---
id: doc1
title: Style Guide
sidebar_label: Style Guide
---

画面側
サイドバー

階層構造にしたい場合は以下のように書けばいいそうです。

sidebars.js

module.exports = {
  docs: {
    Guides: [
      'creating-pages',
      {
        type: 'category',
        label: 'Docs',
        items: ['markdown-features', 'sidebar'],
      },
    ],
  },
};

画面側
階層構造にしたサイドバー

フッター #

docusaurus.config.jsのthemeConfigのなかに設定があります。

docusaurus.config.js

※一部抜粋
footer: {
  style: 'dark',
  links: [
    {
      title: 'Docs',
      items: [
        {
          label: 'Style Guide',
          to: 'docs/doc1',
        },
        {
          label: 'Second Doc',
          to: 'docs/doc2',
        },
      ],
    },
    {
      title: 'Community',
      items: [
        {
          label: 'Stack Overflow',
          href: 'https://stackoverflow.com/questions/tagged/docusaurus',
        },
        {
          label: 'Discord',
          href: 'https://discordapp.com/invite/docusaurus',
        },
        {
          label: 'Twitter',
          href: 'https://twitter.com/docusaurus',
        },
      ],
    },
    {
      title: 'More',
      items: [
        {
          label: 'Blog',
          to: 'blog',
        },
        {
          label: 'GitHub',
          href: 'https://github.com/facebook/docusaurus',
        },
      ],
    },
  ],
  copyright: `Copyright © ${new Date().getFullYear()} My Project, Inc. Built with Docusaurus.`,
},

画面側
フッター

URL #

docs配下のドキュメント
/docs/{id}

ブログトップ
/blog

ブログ記事
/blog/{id}

デプロイ #

今回はNetlifyでデプロイして、いつでもサイトとして見られるようにしておきます。

初回デプロイ #

1.docusaurus.config.jsの内容を確認
urlとbaseUrlの設定を確認してください。
urlはNetlifyで公開する上でのURL。baseUrlは/のままでも問題なかったです。

2.netlify.tomlを用意
自分はTILリポジトリの中にDocusaurusディレクトリを作って、そこでDocusaurusを導入しているので設定を用意しました。
リポジトリトップでDocusaurusを導入してる場合は恐らく不要です。

[build]
  base    = "Docusaurus/website"
  publish = "build"
  command = "yarn build"

3.Netlifyとリポジトリを連携させてデプロイ
基本的なやり方は過去記事に書いているので、こちらをご覧ください。

デプロイの設定の個所は以下のように設定。
フッター

これでうまくいっていればデプロイされて、サイトとして閲覧できるようになっていると思います。

2回目からのデプロイ #

初回デプロイ以降は、デプロイブランチが更新されるたびに自動でデプロイを行ってくれます。
そのほか、デプロイブランチに対するプルリク(マジリク)を出したときに、デプロイのプレビューを生成してくれるので、マージした後にどうなるか事前に確認ができます。

Nodeのバージョン指定 #

バージョン指定をしたい場合は、以下の2通りのやり方があります。

  • 環境変数にNODE_VERSIONでバージョンを設定する(設定方法は以下のいずれか)
    • netlify.tomlに記述する
    • Netlifyの管理画面の Settings → Build & Deploy → Environment で設定
  • .node-version.nvmrcをリポジトリ直下に作成して、バージョンを記述する。

Textlintの導入(2020/1/19追記) #

Textlintは文書向けのLintツールです。
せっかくなので使用してみることにしました。

導入手順 #

1.インストール

$ yarn add -D textlint textlint-rule-preset-ja-technical-writing

Textlint単体だと何もルールを持っていないので、技術書向けのルールセットであるtextlint-rule-preset-ja-technical-writingを一緒にインストールしています。

2.設定ファイルの作成

$ ./node_modules/.bin/textlint --init

以下のような.textlintrcファイルが作られます。
すでにインストールしているルールを認識して作ってくれるようです。

{
  "filters": {},
  "rules": {
    "preset-ja-technical-writing": true
  }
}

コマンドで実行 #

この時点でコマンドが使えるようになっています。
設定ファイルで設定したルールに沿ってチェックされます。

$ ./node_modules/.bin/textlint (ファイルパス)

また、以下のコマンドではルールにもよりますが、自動修正できるものは修正してくれるようです。

$ ./node_modules/.bin/textlint --fix (ファイルパス)

VSCode上で実行 #

都度都度コマンド実行するのも手間なので、VSCodeのエディタ上で確認できるようにしておきます。

1.VSCodeの拡張をインストールして、必要に応じてVSCodeを再起動
→ 拡張:vscode-textlint

プロジェクトフォルダ直下に.textlintrcnode_modulesがある場合は、この時点で動作するため、以降の手順は不要です。

自分の場合は、TIL-Engineer(※プロジェクトフォルダ)/Docusaurus/websiteの配下にそれらのファイルやフォルダがあったため、別途設定が必要になりました。

2..textlintrcnode_modulesのパスの指定
ファイル → 基本設定 → 設定 でワークスペースタブを選択し、textlintで設定を検索

設定例

  • Textlint: Config Path → Docusaurus/website/.textlintrc
  • Textlint: Node Path → Docusaurus/website/node_modules

その他の設定には以下のようなものがあります。この辺はお好みで。

  • Textlint: Auto Fix On Save(保存時に自動修正できるものを修正するかどうか)
  • Textlint: Run(入力時にチェックするか、保存時にチェックするか)

これでこんな感じで動いてくれるようになります。
VSCode上で動作するTextlint

個人的にはESLintのようにフォルダごとの設定で指定したかったのですが、現状できなかったので、しぶしぶワークスペースの設定を使っています。
ワークスペースでの設定となると、複数Textlintを使いたいプロジェクトがあった場合、別のワークスペースにしないとダメな気がして…。

設定のカスタマイズ #

今回使用しているtextlint-rule-preset-ja-technical-writingは複数のルールから構成されており、ルールそれぞれのカスタマイズが可能です。
デフォルト値が厳しめに設定されているため、自分に合った設定にカスタマイズしてみましょう。

デフォルトルール一覧

  • 1文の長さは100文字以下とする
  • カンマは1文中に3つまで
  • 読点は1文中に3つまで
  • 連続できる最大の漢字長は6文字まで
  • 漢数字と算用数字を使い分けます
  • 「ですます調」、「である調」を統一します
  • 文末の句点記号として「。」を使います
  • 二重否定は使用しない
  • ら抜き言葉を使用しない
  • 逆接の接続助詞「が」を連続して使用しない
  • 同じ接続詞を連続して使用しない
  • 同じ助詞を連続して使用しない
  • UTF8-MAC 濁点を使用しない
  • 不必要な制御文字を使用しない
  • 感嘆符!!、感嘆符??を使用しない
  • 半角カナを使用しない
  • 弱い日本語表現の利用を使用しない
  • 同一の単語を間違えて連続しているのをチェックする
  • よくある日本語の誤用をチェックする
  • 冗長な表現をチェックする
  • 入力ミスで発生する不自然なアルファベットをチェックする
  • 対になっていない括弧をチェックする

設定例

{
  "filters": {},
  "rules": {
    "preset-ja-technical-writing": {
      "sentence-length": {
        "max" : 90
      },
    }
  }
}

この場合、sentence-length(1文の長さ)を90以下にして、それ以外のルールはデフォルト値が使われます。
他の設定や例外の設定の仕方などはGitHub - textlint-rule-preset-ja-technical-writingのREADMEを参考にしてください。


ここまで簡単に概要を書きました。
開発版とはいえ、2系の方が扱いやすい感じがします。
これから少しずつ、TILリポジトリにあげた勉強内容をドキュメント化していきたいと思いますー。

また、プログラマーとしての経歴や経験をまとめたページなんかも作ったり、当ブログとリンクさせたりしていきたいですね。

参考リンクまとめ #