Next.js 採用プロダクトにおける Lint ツール導入事例

mediba Advent Calendar 2021 の 18 日目の記事です。

こんにちは。エンジニアの中畑(@yn2011)です。最近は Switch の月姫で直死の魔眼について学んでいます。

初めに

プロダクトを新規に開発する際に、どんな Lint ツールを導入し、どのような設定で利用するかは悩むことが多いです。最終的にはチームメンバーと議論をして決めていく必要がありますが、社内外の他のプロダクトでどのようにしているか参考事例が欲しい場合があります。業務で開発したプロダクトのコードベースは社外に公開されることは少ないため、他社でどのような Lint ツールを導入しているのかを知る機会は少ないです。

そういった背景から、直近のプロダクト開発において ① どんな Lint ツールを導入しているか、② 特に工夫している設定は何か、という Lint にフォーカスした内容を書いたらどうだろうと考え、今回の記事のテーマとしました。

昨年から引き続き、mediba のプロダクトでは Next.js の採用が増えています。その中のとある Next.js 採用プロダクトを例として今回ご紹介します。

tsc

TypeScript です。Next.js 9 以降は、TypeScript 関連の機能はビルトインされていて yarn build 時に型検査を行いますが、型検査だけを任意のタイミングで行う場合は、tsc -p . --noEmit のようなコマンドを実行します。Lint ツールではありませんが、型検査のためにだけ使用しているので一応記載しました。

ESLint

ESLint です。Next.js 11 以降は、ESLint 関連の設定をビルトインするようになり、next lint で実行と設定ができます。

Next.js 向けのコンフィグとして、eslint-config-next があります。以下のルールはオフにしています。

"@next/next/no-img-element": "off",
"@next/next/no-html-link-for-pages": "off",
"@next/next/next-script-for-ga": "off",
"@next/next/no-sync-scripts": "off",

Next.js の機能を活用するためのルールをなぜオフにしているか疑問に思われるかもしれませんので理由を説明します。

no-img-element は、Next.js が提供する next/image を使用せずに、img 要素を使用している場合に警告を行うルールです。プロダクトでは、SSR ではなく、SSG を使用していて next/image は未使用なのでオフにしています。

no-html-link-for-pages は、Next.js が提供する next/link を使用せずに anchor 要素のみを使ってリンクを実装している場合に警告を行うルールです。next/link を使用することで、CSR でページ遷移を実現することが可能になりますが、mediba で利用している Google Analytics 向けの実装ではページ遷移時の集計に懸念があることが分かっています。そういった理由から意図的に next/link を使用しないようにしているため、ルールをオフにしています。

next-script-for-gano-sync-scripts は、next/script の仕様をきちんと調査できていなかったので暫定的に script 要素を使用していましたが今後対応するかもしれません。

Stylelint

Stylelint です。プロダクトでは、styled-components のような JS in CSS ではなく、.scss ファイルに定義した CSS を CSS Modules から利用しているためプロパティの並び順の auto fix 等、Stylelint の機能を全て活用することができています。

プロパティの並び順定義は、stylelint-config-recess-order を使用しています。定義されているプロパティの数とメンテナンスの頻度の面で、他のライブラリや自身で作成するよりも良さそうだったため選定しました。

また、Prettier を使用しているので stylelint-config-prettier で競合を防いでいます。

markuplint

markuplint です。HTML Living Standard の仕様に準拠した HTML になっているかを検証できます。React (JSX) にも対応しています。

ルールを独自に追加しています。

{
  "nodeRules": [
    {
      "selector": "meta[property]",
      "rules": {
        "invalid-attr": {
          "option": {
            "attrs": {
              "property": {
                "type": "String"
              },
              "content": {
                "type": "String"
              }
            }
          }
        }
      }
    }
  ]
}

meta 要素の属性についてです。SNS でページを共有された際の挙動を定義するため、このような meta 要素を実装することが多いと思います。

<meta content="ページタイトル">

この実装は markuplint が 2 つ警告を出します。まず、HTML Living Standard の meta 要素の仕様には、property 属性が定義されていないため警告を出します。 また、name 属性が定義されていない場合に、content 属性を使用することができないため、こちらについても警告を出します。

では一体この属性は何なんだ、という話になりますが、property 属性については RDFa で定義されており、OGP に従ってこの meta 要素を SNS 側が解釈します。したがってここでは例外として取り扱うようにルールを変更しています。

なお、React 独自の属性(keydangerouslySetInnerHTML) は @markuplint/react-spec に含まれているため独自にルールを追加する必要はありません。便利です。

まとめ

直近の Next.js 採用プロダクトを例に、使用している Lint ツールと、その設定についてご紹介しました。これらの Lint ツールは全て GitHub Actions で commit push をトリガーにして実行していますので、ルールに違反しているコードをコミットするとすぐに気づくことができ、Lint ツール運用の形骸化を防ぐことができています。

Lint ツールの導入によって、コードベースをクリーンに保つことはもちろんですが、標準仕様やベストプラクティスを知る良いきっかけにもなっています。

Lint ツール導入時の参考になれば幸いです。最後までお読み頂きありがとうございました。