🐰

Zennと連携したリポジトリにprettierとtextlintをいれてフォーマットと文章校正を自動で実行する

2022/03/09に公開約7,300字

数多あるブログシステムの中で、Zennで記事を書くようになったのはGitHubと連携できるのが大きいです。Gitで履歴を管理したり、PRでの運用はとても快適です。

その中でも、prettierでマークダウン形式のフォーマットをしたり、textlintで文章の統一感や言葉の揺れチェックできるのは助かります。

今回は、このあたりを自動化した内容を記事にまとめます。

prettierをセットアップ

prettier本体をインストールします。

npm install --save-dev prettier

対象としないファイルを指定します。

.prettierignore
.git
node_modules
package.json
package-lock.json

デフォルトのままだと、半角英数字と全角文字の間にスペースが入るようになってしまうので、それを回避するプラグインをいれます。

npm install --save-dev prettier-plugin-md-nocjsp

設定ファイルを.prettierrcというファイル名で作成します。

.prettierrc
overrides:
  - files:
    - "*.md"
    - README
    options:
      parser: markdown-nocjsp

npm runで実行できるようにします。

package.json
   "scripts": {
+    "format": "prettier -w articles/**/*.md"
   },
   "keywords": [],
   "author": "",

次のコマンドを実行することで、マークダウン形式のファイルをフォーマットします。

npm run format

textlintをセットアップ

文章校正をするため、textlintをセットアップします。

インストール

textlint本体をインストールします。

npm install --save-dev textlint

続いて、ルールをインストールします。

npm install --save-dev textlint-rule-preset-ja-technical-writing textlint-rule-preset-jtf-style

技術文章向けのプリセットルールと「JTF日本語標準スタイルガイド(翻訳用)」に対応したルールを追加しています。特に別に翻訳をするわけではなくても、技術系の文章を書く際に適したルールがあるのでいれるようにしています。

設定ファイルを下記の通りにします。

.textlintrc.js
module.exports = {
  plugins: {
    "@textlint/markdown": {
      extensions: [".md"],
    },
  },
  rules: {
    "preset-ja-technical-writing": true,
    "preset-jtf-style": {
      "2.1.6.カタカナの長音": true,
      "2.2.1.ひらがなと漢字の使い分け": true,
    },
  },
};

preset-jtf-styleのいくつかがデフォルトでオフになっているので、ルールをオンにします。

実行

scriptsにtextlintを実行するためのコマンドを追加します。

記事を追加するたびに毎回全部の記事を対象にして非効率なので、ステージされたファイルを対象にしたり、textlintのオプションにあわせて、いくつか追加します。

package.json
   "scripts": {
     "format": "prettier -w articles/**/*.md",
+    "lint:text": "textlint --cache $(git diff main --name-only) -f pretty-error",
+    "lint:text:fix": "textlint --cache $(git diff main --name-only) --fix",
+    "lint:text:full": "textlint --cache README.md \"articles/**/*.md\" -f pretty-error",
+    "lint:text:full:fix": "textlint --cache README.md \"articles/**/*.md\" --fix"
   },
   "keywords": [],
   "author": "",

エラーの箇所が特定しやすいように-f pretty-errorをオプションに追加しています。

ステージのファイルを対象にチェックする場合は以下を実行します。自動で修正するにはfixを付加します。

npm run lint:text
npm run lint:text:fix

コミット済みのファイルも対象にチェックをかけたい場合はそれぞれ下記を実行します。

npm run lint:text:full
npm run lint:text:full:fix

スクリプトを実行すると、キャッシュが生成されます。キャッシュはリポジトリーに含まなくてもいいので、.gitignoreに追加します。

.gitignore
+.textlintcache

便利なルールを追加

基本的なルールだけでもいいのですが、さらにいくつかルールを追加します。

prh

表記揺れなどを防ぐために、prhというツールのtextlintのプラグインをインストールします。

npm install --save-dev textlint-rule-prh

設定ファイルにルールの設定を追加します。

.textlintrc.js
  rules: {
    "preset-ja-technical-writing": true,
    "preset-jtf-style": {
      "2.1.6.カタカナの長音": true,
      "2.2.1.ひらがなと漢字の使い分け": true,
    },
+   prh: { rulePaths: ["./prh.yml"] },
},

下記の様な内容でprh.ymlというファイルを作成します。

version: 1
rules:
  - expected: Next.js
    pattern:
      - nextjs
      - next.js
  - expected: Zenn
    pattern:
      - zenn

詳しい書き方は次のサイトを参考にしてください。

https://github.com/prh/prh

textlint-filter-rule-comments

特定の文章ではチェックをオフにしたい場合があります。その時のためのルールを追加します。

npm install --save-dev textlint-filter-rule-comments

設定に追加します。

.textlintrc.js
     },
     prh: { rulePaths: ["./prh.yml"] },
   },
+  filters: {
+    comments: true,
+  },
 };

Zennの場合、アコーディオン(トグル)があるのですが、その記述が以下のようになっています。

:::details タイトル

これだと、日本語と半角アルファベットの間にスペースをいれないというルールに引っかかってしまいます。

下記の記述にしておくと処理対象から除外できます。

<!-- textlint-disable -->
:::details ファイル全体
<!-- textlint-enable -->

コミット時に自動実行

prettierとtextlintが実行できるようになってので、コミットした際に自動で実行するようにします。huskyとlint-stagedを使います。

必要なパッケージをインストールします。

npm install --save-dev husky lint-staged

Git hooksを有効にします。

npx husky install

npm installをした後、Git hooksを自動で有効化するために、package.jsonのscriptsに先ほどのコマンドを追加します。

あわせて、lint-stagedも実行できるように追加します。

package.json
   "scripts": {
     "format": "prettier -w articles/**/*.md",
     "lint:text": "textlint --cache $(git diff main --name-only) -f pretty-error",
     "lint:text:fix": "textlint --cache $(git diff main --name-only) --fix",
     "lint:text:full": "textlint --cache README.md \"articles/**/*.md\" -f pretty-error",
     "lint:text:full:fix": "textlint --cache README.md \"articles/**/*.md\" --fix",
+    "prepare": "husky install",
+    "lint-staged": "lint-staged"
   },
   "keywords": [],
   "author": "",

コミット時に実行するスクリプトを作成します。

npx husky add .husky/pre-commit "npm run lint-staged"

.husky/pre-commitというファイル名でスクリプトが生成されます。

内容は下記のようになります。

.husky/precommit
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npm run lint-staged

lint-stagedを実行する内容をpackage.jsonに記述します。

package.json
+  "lint-staged": {
+    "*.md": [
+      "prettier -w",
+      "textlint --cache --fix",
+      "textlint --cache"
+    ]
+  },

textlintを2回実行しています。最初のtextlintの呼び出しで、自動で直せる箇所は修正します。2回目の呼び出しで、エラーがあった場合はコミットできないようにします。

GitHubアクションを追加

以上で普段Zennの記事を更新していくには十分なのですが、最後にGitHubでプルリクが作成されてたら、チェックが走るようにします。

下記のファイルを作成します。

.github/workflows/lint.yml
name: Lint

on:
  - pull_request

jobs:
  textlint:
    strategy:
      matrix:
        os:
          - ubuntu-latest
        node-version:
          - 14.x

    runs-on: ${{ matrix.os }}

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v2.3.0
        with:
          node-version: ${{ matrix.node-version }}

      - name: Get the number of processors currently available
        id: processors
        run: echo "::set-output name=number::$(getconf _NPROCESSORS_ONLN)"

      - name: Cache node modules
        uses: actions/cache@v2
        env:
          cache-name: cache-node-modules
        with:
          path: ~/.npm
          key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-build-${{ env.cache-name }}-
            ${{ runner.os }}-build-
            ${{ runner.os }}-

      - name: Setup reviewdog
        run: |
          mkdir -p $HOME/bin && curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh | sh -s -- -b $HOME/bin
          echo "$HOME/bin" >> $GITHUB_PATH
          echo "$(go env GOPATH)/bin" >> $GITHUB_PATH # for Go projects
      - name: Install dependencies
        run: npm install
        env:
          CHILD_CONCURRENCY: ${{ steps.processors.outputs.number }}
          NETWORK_CONCURRENCY: ${{ steps.processors.outputs.number }}

      - name: textlint
        run: npm run lint:text:full -- ${FILES} -f checkstyle --experimental --parallel --max-concurrency ${{ steps.processors.outputs.number }} | reviewdog -f=checkstyle -name="textlint" -reporter=github-pr-review -level=${LEVEL}
        env:
          FILES: README.md articles/**/*.md
          LEVEL: error
          REVIEWDOG_GITHUB_API_TOKEN: ${{ github.token }}

このファイルをコミットしてプルリクを作成すると、チェックが走っているのを確認できます。

もし、何かエラーがあれば、reviewdogでプルリクにコメントがつくようになります。

おわりに

ZennがGitリポジトリーと連携しくれるお陰で、こういう仕組みができてすごく助かります。

がんばって記事をあげていきます。

GitHubで編集を提案

Discussion

ログインするとコメントできます