ブロック開発に ESLint を導入してみよう

この記事では、WordPress ブロック開発における JavaScript のコード品質改善、およびコーディングスタイル統一のために、ESLint を導入する方法を紹介したいと思います。

ESLint を導入する理由

カッコ文字の前後にスペースがいくつあっても、どこで改行しても、どのような変数名であっても、コードが全て正しければ、ブロックは正しく動作します。ブラウザコンソールに何の警告やエラーも表示されないでしょう。そのコードを自分だけしか読むことがなく、どこにも公開していなければ、それは問題ないかもしれません。

ですが、そのプラグインを GitHub などに公開した場合、誰かがそのプラグインに興味を持ち、コードを読んだり、コードを書いてプルリクエストを送るかもしれません。また一つのブロックに複数の開発者が関わっている場合、自分以外の開発者が書いたコードを目にする事になります。

そのような時にコーディングルールが統一されていなかったらどう感じるでしょうか。読みにくいと感じるかもしれませんし、不具合混入の原因になる可能性もあります。

ブロックのひな形を作成する

ESLint に関して説明する前に、まずはブロックのひな形を作成してみます。自分で一つ一つファイルを作ることもできますが、@wordpress/create-block という便利なライブラリがあるので、それを使ってひな形を作成します。

npx @wordpress/create-block gutenpride

gutenpride というディレクトリを開くと以下のようなファイルで構成されているはずです。

create-block によって作成されるファイル

EditorConfig

ファイル構成を見ると、.editorconfig というファイルがあることが分かります。ESLint を導入する前に、コーディングスタイルを統一するためにもう一つ重要な設定である EditorConfig について説明したいと思います。

EditorConfig を導入する理由の一つとして、次のような例を挙げてみたいと思います。

  • 一つのプラグインに複数の開発者が関わっています。
  • 開発者Aは、自身のコードエディターのデフォルト設定で、2つの半角スペースをインデントとして設定しています。
  • 開発者Bは、自身のコードエディターのデフォルト設定で、タブをインデントとして設定しており、そのインデント幅は4文字分です。

開発者Aが、以下のようなコードを書いたとします。インデントが全てスペースであることに注意してください。

2スペースインデントを持つコード

このコードが書かれたファイルを、開発者Bが自身のコードエディターで開き、34行のインデントを削除し、視覚的にインデントが正しくなるように再調整したとします。

一部にタブインデントを持つコード

しかし、このファイルを開発者Aが再度開くと、34行目のインデントが視覚的に正しくないことが分かります。

一部にタブインデントを持つコード

コードエディターによっては、そのファイルのインデントを自動的に検出する場合がありますが、全ての開発者の間でインデントが視覚的にそろう事は必ずしも保証されていません。

このような差異を防ぎ、コーディングスタイルを一つのプロジェクトで統一する事が、EditorConfig を導入する一つの理由であると思います。

EditorConfig は、非常に多くのコードエディターでサポートされています。いくつかのコードエディターでは、プラグインを導入する必要があります。自身のコードエディターで追加プラグインが必要かどうかは、以下のページで確認してください。

create-block のEditorConfig

ここで、create-block によって作成された .editorconfig の設定を見てみたいと思います。

# This file is for unifying the coding style for different editors and IDEs
# editorconfig.org

# WordPress Coding Standards
# https://make.wordpress.org/core/handbook/coding-standards/

root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = tab

[*.{yml,yaml}]
indent_style = space
indent_size = 2

このファイルの設定内容を見ると、例えば前述したインデントルールに関しては、「タブインデント」が設定されている事が分かります。これらのルールを自分の好みに合わせてカスタマイズする事もできますが、重要なことは「そのプロジェクト全体で、ルールに基づいてコーディングスタイルが統一されているかどうか」です。

ESLint を導入する前に、まずは EditorConfig によるルールを定義する事を強くおすすめします。

JavaScript コードを ESlint でチェックする

ここで ESLint の話に戻します。例えば、ブロックの Edit コンポーネントにおいて、ブロックが表示するテキストを一度 my_variable 変数に格納するように更新したとします。

export default function Edit() {
	const my_variable = __( 'Gutenpride – hello from the editor!', 'gutenpride' );
	return (
		<p { ...useBlockProps() }>
			{ my_variable }
		</p>
	);
}

create-block でブロックのひな形を作成した場合、ESLint によってこのようなコードを解析するための仕組みがデフォルトで内包されています。package.jsonscripts フィールドに定義されているlint:js というスクリプトを実行してみます。

npm run lint:js

このスクリプトを実行すると、以下のようなエラーを確認できるはずです。

lint:js コマンドを実行した後に表示されるエラー

このエラーから、書かれたコードが以下二つのルールに準じていないという事が分かります。

  • my_variable という変数名がキャメルケースではない
  • 改行やインデントが適切ではない

Lint エラーを自動的に修正する

それでは、これらのエラーを解消するために、全て手動で修正し、エラーが解消されるまで lint:js を何度も実行する必要があるのでしょうか。

lint:js と同様に、create-block には、このエラーをある程度自動的に修正するための format スクリプトが存在します。

$ npm run format

> gutenpride@0.1.0 format
> wp-scripts format

package-lock.json 129ms
package.json 1ms
src/block.json 5ms
src/edit.js 10ms
src/index.js 3ms
src/save.js 2ms
src/view.js 2ms

このコマンドを実行すると、警告された二つのエラーのうち、改行やインデントに関するエラーが自動的に修正されます。

export default function Edit() {
	const my_variable = __(
		'Gutenpride – hello from the editor!',
		'gutenpride'
	);
	return <p { ...useBlockProps() }>{ my_variable }</p>;
}

Lint エラーを手動で修正する

ただし、変数名に関するエラーは自動的に修正されていません。format コマンドは、全てのエラーを自動で修正するわけではないため、残ったエラーは手動で修正する必要があります。以下のように、変数名をキャメルケース (myVariable) に変更します。

export default function Edit() {
	const myVariable = __(
		'Gutenpride – hello from the editor!',
		'gutenpride'
	);
	return <p { ...useBlockProps() }>{ myVariable }</p>;
}

npm run lint:js を実行すると、全てのエラーが解消された事を確認できるはずです。

これらの ESLint ルールはどこで定義されている?

自分で ESLint のためのルールを全く定義していないにもかかわらず、あるルールに基づいてコードが解析される理由はなんでしょうか。

それは、create-block によってファイルのひな形が作成される時に、@wordpress/scripts というライブラリがデフォルトでインストールされ、そのライブラリが依存しているライブラリに ESLint ルールが定義されているからです。そのライブラリは @wordpress/eslint-plugin です。

npm ls コマンドを使用する事で、 @wordpress/scripts@wordpress/eslint-plugin に依存している事が分かります。

$ npm ls @wordpress/eslint-plugin
gutenpride@0.1.0 /home/username/projects/gutenpride
└─┬ @wordpress/scripts@30.3.0
  └── @wordpress/eslint-plugin@21.3.0

lint:js、つまり wp-scripts lint-js スクリプトを実行すると、@wordpress/eslint-pluginで定義されているルールに基づいてコードが解析されることになります。

ESLint そのものについても、依存関係として存在している事が分かります。

$ npm ls eslint
gutenpride@0.1.0 /home/username/projects/gutenpride
└─┬ @wordpress/scripts@30.3.0
  ├─┬ @wordpress/eslint-plugin@21.3.0
  │ ├─┬ @babel/eslint-parser@7.25.7
  │ │ └── eslint@8.57.1 deduped
  │ ├─┬ @typescript-eslint/eslint-plugin@6.21.0
  │ │ ├─┬ @typescript-eslint/type-utils@6.21.0
  │ │ │ └── eslint@8.57.1 deduped
  │ │ ├─┬ @typescript-eslint/utils@6.21.0
  │ │ │ └── eslint@8.57.1 deduped
  │ │ └── eslint@8.57.1 deduped
  │ ├─┬ @typescript-eslint/parser@6.21.0
  │ │ └── eslint@8.57.1 deduped
  │ ├─┬ eslint-config-prettier@8.10.0
  │ │ └── eslint@8.57.1 deduped
  │ ├─┬ eslint-plugin-import@2.31.0
  │ │ └── eslint@8.57.1 deduped
  │ ├─┬ eslint-plugin-jest@27.9.0
  │ │ ├─┬ @typescript-eslint/utils@5.62.0
  │ │ │ └── eslint@8.57.1 deduped
  │ │ └── eslint@8.57.1 deduped
  │ ├─┬ eslint-plugin-jsdoc@46.10.1
  │ │ └── eslint@8.57.1 deduped
  │ ├─┬ eslint-plugin-jsx-a11y@6.10.1
  │ │ └── eslint@8.57.1 deduped
  │ ├─┬ eslint-plugin-playwright@0.15.3
  │ │ └── eslint@8.57.1 deduped
  │ ├─┬ eslint-plugin-prettier@5.2.1
  │ │ └── eslint@8.57.1 deduped
  │ ├─┬ eslint-plugin-react-hooks@4.6.2
  │ │ └── eslint@8.57.1 deduped
  │ ├─┬ eslint-plugin-react@7.37.2
  │ │ └── eslint@8.57.1 deduped
  │ └── eslint@8.57.1 deduped
  └─┬ eslint@8.57.1
    └─┬ @eslint-community/eslint-utils@4.4.1
      └── eslint@8.57.1 deduped

ESLint ルールを拡張する

このデフォルトルールだけに基づいて開発を進める事ももちろん可能です。ですが、一部のルールを自分の好みに合わせて変更したい、またはより厳格なルールを適用したいという人もいるかもしれません。

このデフォルトルールを変更または拡張するために、プロジェクトのルートディレクトリに .eslintrc というファイルを作成し、その中に以下のようなコード記述します。

{
	"extends": [ "plugin:@wordpress/eslint-plugin/recommended" ]
}

lint:js スクリプトを実行すると、ここで定義されたルールに基づきコードが解析されるようになります。

既存のルールを変更する

既存のルールを変更するために、このファイルを更新してみます。

例えば、__experimental プレフィックスが付いた API を使用するために、以下のようなコードを書いたとします。

注意: これは例であり、@wordpress/foo というライブラリ、および __experimentalFeature という API は存在しません。

import { __experimentalFeature } from '@wordpress/foo';

export default function Edit() {
	const myVariable = __(
		'Gutenpride – hello from the editor!',
		'gutenpride'
	);
	return <p { ...useBlockProps() }>{ myVariable }</p>;
}

lint:js を実行すると、以下の二つのエラーが表示されます。

lint:js コマンドを実行した後に表示される、実験的な API に関するエラー

この二つのエラーのうち、「Usage of __experimentalFeature from @wordpress/foo is not allowed.」というエラーに注目してください。このルールは @wordpress/eslint-plugin でデフォルトで定義されているルールであり、そのルールの詳細はこの README で確認できます。

このルールをプロジェクト全体で無効化するために、.eslintrc ファイルを以下のように変更します。

{
	"extends": [ "plugin:@wordpress/eslint-plugin/recommended" ],
	"rules": {
		"@wordpress/no-unsafe-wp-apis": "off"
	}
}

もう一度 lint:js スクリプトを実行すると、__experimental プレフィックスが付いた API に関するエラーが消える事を確認できるはずです。

ちなみにこのルールは、Gutenberg プロジェクトでも無効化されています。

新しいルールを追加する

ブロック開発におけるコード品質をより高め安全なものにするために、個人的に推奨したいルールをいくつか紹介したいと思います。これらのルールは、@wordpress/eslint-plugin のデフォルトルールには含まれていないため、明示的に追加する必要があります。

react/jsx-boolean-value

React において、prop の値が boolean 値 かつ true である場合のルールを強制します。

{
	"extends": [ "plugin:@wordpress/eslint-plugin/recommended" ],
	"rules": {
		"react/jsx-boolean-value": "error"
	}
}

✅ こうすべき

const Hello = <Hello myProp />;

❌ こうすべきではない

const Hello = <Hello myProp={ true } />;

@wordpress/i18n-text-domain

翻訳関数に正しいテキストドメインを追加する事を強制します。allowedTextDomain フィールドの値は、自身のプロジェクトに合わせて変更してください。

{
	"extends": [ "plugin:@wordpress/eslint-plugin/recommended" ],
	"rules": {
		"@wordpress/i18n-text-domain": [
			"error",
			{
				"allowedTextDomain": "gutenpride"
			}
		]
	}
}

✅ こうすべき

export default function Edit() {
	const myVariable = __(
		'Gutenpride – hello from the editor!',
		'gutenpride'
	);
	return <p { ...useBlockProps() }>{ myVariable }</p>;
}

❌ こうすべきではない

export default function Edit() {
	const myVariable = __(
		'Gutenpride – hello from the editor!'
	);
	return <p { ...useBlockProps() }>{ myVariable }</p>;
}

@wordpress/dependency-group

別のソースからコードをインポートするときに、それらを適切にグループ化してコメントを追加する事を強制します。

{
	"extends": [ "plugin:@wordpress/eslint-plugin/recommended" ],
	"rules": {
		"@wordpress/dependency-group": "error"
	}
}

✅ こうすべき

/*
 * External dependencies
 */
import { camelCase } from 'change-case';

/*
 * WordPress dependencies
 */
import { Component } from 'react';

/*
 * Internal dependencies
 */
import edit from './edit';

❌ こうすべきではない

import { camelCase } from 'change-case';
import { Component } from 'react';
import edit from './edit';

ここまでのルールが適用された .eslintrc

変更されたルールおよび新しいルールを全て .eslintrc に適用すると、以下のようになります。このコードを参考に、自身のプロジェクトや好みに合うように最適なルールを探してみてください。

{
	"extends": [ "plugin:@wordpress/eslint-plugin/recommended" ],
	"rules": {
		"@wordpress/no-unsafe-wp-apis": "off",
		"react/jsx-boolean-value": "error",
		"@wordpress/i18n-text-domain": [
			"error",
			{
				"allowedTextDomain": "gutenpride"
			}
		],
		"@wordpress/dependency-group": "error"
	}
}

コードエディターで Lint ルール違反を検出する

ここまで、Lint エラーの修正、ルールの変更・追加を行いました。では、これらのエラーを検出・修正するために、コードを更新するたびに lint:js または format スクリプトを実行する必要があるのでしょうか。

多くのコードエディターでは、プロジェクトに存在する ESLint ルールを自動的に読み取り、それをコードエディター内でエラーとして検出したり、ファイルを保存するときにルールに違反しているコードを自動的に修正したりしてくれます。

ここでは、私が普段使用しているVSCode での方法を紹介します。

まずは、ESLint 拡張を VSCode にインストールします。ユーザー設定またはワークスペース設定で、ESLint が有効化されている事を確認してください。

ユーザー設定で有効化されている ESLint 拡張

ESLint 拡張が有効であり、ルールに違反するコードが存在する場合、以下のように警告が表示されるはずです。

ESLint 拡張が発する警告

ドキュメント