もう使わない設定がネット上に散見されていたり、両者で微妙な違いがあったりする罠があるので、少しだけまとめてみます。
最近のjsではprettierの設定が一般的になってきているので、create-react-appと組み合わせる方法について、少しだけまとめてみます。
※ 本記事はmacを前提としているので、windowsの方は適宜読み替えて下さい。
- BabelかTypescriptか
- Babel版:eslint + prettierの設定
- Typescript版:tslint + prettierの設定
- ファイル保存時にPrettierとLintを自動実行する
- Babel:eslintのエラー・警告集
- Typescript:tslintのエラー・警告集
- 雑感
BabelかTypescriptか
create-react-appには、使用されるトランスパイラは標準でBabelとなります。
しかし、オプションでtypescriptにする事も可能です。(Reactオフィシャルではないようです)
Babelの場合のlintは eslint になります。
Typescriptの場合のlintは tslint になります。
eslintとtslintは目的は同じものですが、設定ファイルの構造や中身が異なります。
という事で、今回はeslintとtslintの両方のパターンでprettierを適用する方法を書いてみます。
Babel版:eslint + prettierの設定
create-react-appをグローバルにインストール
npm install -g create-react-app
babelでプロジェクトを生成
create-react-app eslint-prettier-example
エディタ設定
cd eslint-prettier-example cat<<EOF > .editorconfig # Editor configuration, see http://editorconfig.org root = true [*] charset = utf-8 indent_style = space indent_size = 2 insert_final_newline = true trim_trailing_whitespace = true [*.md] max_line_length = off trim_trailing_whitespace = false EOF
eslintとprettierのモジュールをインストール
yarn add -D eslint prettier eslint-plugin-prettier eslint-config-prettier
eslint-plugin-prettierは、eslintを実行すると、prettier → eslintという順番で実行してくれるモジュールです。(prettierの整形結果をeslintが怒る事があるので)
eslint-config-prettierは、prettierが整形した部分をeslintに無視させるモジュールです。
create-react-appでeslintするとモジュールが足りないと警告されるので、以下をインストールします。
yarn add -D eslint-plugin-react eslint-config-react-app eslint-plugin-import eslint-plugin-flowtype eslint-plugin-jsx-a11y
js標準コーディング規約(JavaScript Standard Style)をインストールします。
yarn add -D eslint-plugin-standard eslint-config-standard eslint-plugin-node eslint-plugin-promise
eslint-config-standardはJavascript標準スタイル(皆で決めた標準lintルール)のeslint版です。例えば「function hoge(){}」で「hoge」と「()」の間にスペースが無い場合はNGとする、といったルール集です。
JavaScript Standard Style
標準スタイルにはgoogle版やairbnb版等があります。任意に変更して下さい。
eslint設定ファイル追加
cat<<EOF > .eslintrc.json { "extends": [ "standard", "plugin:prettier/recommended" ], "plugins": [ "react", "prettier" ], "parser": "babel-eslint", "parserOptions": {}, "env": { "browser": true, "es6": true }, "globals": { "it": false }, "rules": { "prettier/prettier": "error", "react/jsx-uses-react": 1, "react/jsx-uses-vars": 1, "no-console": "warn", // warning Definition for rule 'jsx-a11y/href-no-hash' was not found に対応 "jsx-a11y/href-no-hash": "off", "jsx-a11y/anchor-is-valid": "off" } } EOF
eslintの除外設定ファイル追加
cat<<EOF > .eslintignore node_modules **/*.min.js src/registerServiceWorker.js EOF
registerServiceWorker.jsを追加しているのは、自動生成されるregisterServiceWorker.jsがJavaScript Standard Styleに準拠しておらず、エラーが出まくるためです。。。
prettier設定ファイル追加
cat<<EOF > .prettierrc { "singleQuote": false, "trailingComma": "es5", "semi": false } EOF
prettierに何をさせるかのオプションです。オプション一覧は以下に掲載されています。
package.jsonにscript追加
lint実行コマンドを追加
"lint": "eslint --fix src/**/*.js"
--fixオプションを付けると、prettierの自動整形+eslintの軽い整形を自動保存してくれるようになります。fixを付けないと、ただlintがエラー・警告を発し、ソースコードに編集は入らない挙動になります。
ESLintとPrettierの競合チェックコマンドを追加
eslintの設定とprettierの設定が競合していないかをチェックするコマンドも用意されています。
https://github.com/alexjoverm/tslint-config-prettier#cli-helper-toolhttps://github.com/prettier/eslint-config-prettier#cli-helper-tool
"eslint-check": "eslint --print-config .eslintrc.js | eslint-config-prettier-check"
lintを実行する
lintを実行すると、prettier -> eslintの順に実行されます。つまり、ソースコードの整形+保存 → lintで書式チェック、という順に処理が行われます。
yarn lint
Typescript版:tslint + prettierの設定
create-react-appをグローバルにインストール
npm install -g create-react-app
Typescriptでプロジェクトを生成
create-react-app --scripts-version=react-scripts-ts tslint-prettier-example
エディタ設定
cd tslint-prettier-example cat<<EOF > .editorconfig # Editor configuration, see http://editorconfig.org root = true [*] charset = utf-8 indent_style = space indent_size = 2 insert_final_newline = true trim_trailing_whitespace = true [*.md] max_line_length = off trim_trailing_whitespace = false EOF
tslintとprettierのモジュールをインストール
https://github.com/ikatyang/tslint-plugin-prettier
https://github.com/alexjoverm/tslint-config-prettier
yarn add -D prettier tslint-plugin-prettier tslint-config-prettier tslint-config-standard
tslint-plugin-prettierは、tslintを実行すると、prettier → tslintという順番で実行してくれるモジュールです。
tslint-config-prettierは、prettierが整形した部分をtslintに無視させるモジュールです。(prettierの整形結果をeslintが怒る事があるので)
tslintの設定ファイル追加
cat<<EOF > tslint.json { "rulesDirectory": [ "tslint-plugin-prettier" ], "extends": [ "tslint-config-standard", "tslint-config-prettier" ], "rules": { "prettier": true } } EOF
tslint-config-standardはJavascript標準スタイル(皆で決めた標準lintルール)のtslint版です。例えば「function hoge(){}」で「hoge」と「()」の間にスペースが無い場合はNGとする、といったルール集です。
JavaScript Standard Style
標準スタイルにはgoogle版やairbnb版等があります。任意に変更して下さい。
prettier設定ファイル追加
cat<<EOF > .prettierrc { "singleQuote": false, "trailingComma": "es5", "semi": false } EOF
prettierに何をさせるかのオプションです。オプション一覧は以下に掲載されています。
package.jsonにscript追加
lint実行コマンドを追加
"lint": "tslint -c ./tslint.json --exclude **/*.d.ts --exclude ./node_modules --project . --fix **/*.tsx",
-
- fixオプションを付けると、prettierの自動整形+tslintの軽い整形を自動保存してくれるようになります。fixを付けないと、ただlintがエラー・警告を発し、ソースコードに編集は入らない挙動になります。
TSLintとPrettierの競合チェックコマンドを追加
tslintの設定とprettierの設定が競合していないかをチェックするコマンドも用意されています。
https://github.com/alexjoverm/tslint-config-prettier#cli-helper-tool
"tslint-check": "tslint-config-prettier-check ./tslint.json"
lintを実行する
lintを実行すると、prettier -> tslintの順に実行されます。つまり、ソースコードの整形+保存 → lintで書式チェック、という順に処理が行われます。
yarn lint
ファイル保存時にPrettierとLintを自動実行する
JetBrans WebStorm(Intellij IDEAやGoLandでも同じ)の設定
WebStormの設定はPrettier公式が設定例を提示してくれています。
中でも「Auto-save edited files to trigger the watcher」のチェックを外す事を忘れないようにしましょう。これのチェックがついていると、新規関数を追加しようと改行した瞬間Watcherに介入され、絶改行できなくなります。(自分の改行が即座にPrettierに消される)
File typeを指定できるので、jsxやtsx等の特定の拡張子に限定してWather + Prettierを実行させる事ができます。複数の拡張子を指定した場合は、File Watchersの設定を拡張子毎に複数作成しましょう。
Prettierのオプション設定ですが、Argumentsの実行時引数でオプションをツラツラ書いてしまうとプロジェクト内の.prettierrcと内容が食い違う可能性があるので、Arguments(Prettierには以下のようにプロジェクト内に配置したPrettier設定ファイルを指定した方が安心できそうです。
--config $ProjectFileDir$/.prettierrc --write $FilePathRelativeToProjectRoot$
「$ProjectFileDir$」にはプロジェクトのルートディレクトリが設定されるようなので、プロジェクトに配置した「.prettierrc」を指定すれば、プロジェクト固有のPrettier整形をWebStormに行わせる事が可能になります。
File Watchersについては公式の日本語解説に詳細が書かれているので、是非ウォッチしておくとよいと思います。
ファイル監視機能の使用 - ヘルプ | IntelliJ IDEA
一見これで設定が完了したように見えますが、実はこれでは「未使用importの整理」が実現できていません。
これについてはマクロ機能を利用すると簡単に実現できます。既存のcommand + sは「Save all」ですが、「Optimize imports + Save all」にショートカットを書き換えてしまえばよいのです。
- File -> Edit -> Macros -> Start Macro Recordingをクリックして録画開始。
- Code -> Optimize importsをクリック
- File -> Save Allをクリック
- File -> Edit -> Macros -> Stop Macro Recordingをクリックして録画終了。マクロ名入力を求められるので「Save All with Optimize Imports」等と命名して保存します。
- File -> Preferences -> Keymap -> Macros -> Save All with Optimize Imports(自分で付けたマクロ名)をダブルクリック -> Add Keyboard Shortcut -> cmmand + sを指定。既にcommand + sには「Save All」が割り当てられていると警告されますが、removeして上書き保存します。
これでファイル保存のcommand + sで、Optimize imports+Save Allが実行され、ついでにFile WatchersによってPrettierも実行されるようになります。
もしArgumentsの部分に指定ミスがあった場合、File WatchersのPrettier設定時に「Show console」で「On error」を設定している場合(初期はerror)、コンソールに例えば以下のように表示されるので、どう間違えたかを確認し、修正します。
/Users/tree/go/src/tree-maps-go/node_modules/prettier/bin-prettier.js --find-config-path /Users/tree/go/src/tree-maps-go/.prettierrc --write client/containers/Test.js [error] Cannot use --find-config-path with multiple files Process finished with exit code 1
これで完成です!この設定をすると、以下のような事が自動で行われるようになります。
Visual Studio Codeの設定
Prettierの拡張機能をインストールします。
再起動後にprettierの自動実行設定をしますが、今回はワークスペースのみに設定していきます。(共通の設定にすると全jsが自動整形されてしまうので)
まず、プロジェクト直下に .vscode ディレクトリを作成します。続いて直下にsettings.jsonを作成します。
root └ .vscode └ settings.json
settings.jsonに以下を記述します。
Typescriptの場合のsettings.json
{ "files.insertFinalNewline": true, "javascript.format.enable": false, "prettier.tslintIntegration": true, // for .ts "[typescript]": { "editor.formatOnSave": true, "editor.formatOnPaste": true, "editor.codeActionsOnSave": { "source.organizeImports": true } }, // for .tsx "[typescriptreact]": { "editor.formatOnSave": true, "editor.formatOnPaste": true, "editor.codeActionsOnSave": { "source.organizeImports": true } }, // for .json with comment "[jsonc]": { "editor.formatOnSave": true, "editor.formatOnPaste": true } }
Babelの場合のsettings.json
{ "files.insertFinalNewline": true, "javascript.format.enable": false, "prettier.eslintIntegration": true, // for .js "[javascript]": { "editor.formatOnSave": true, "editor.formatOnPaste": true, "editor.codeActionsOnSave": { "source.organizeImports": true } }, // for .jsx "[javascriptreact]": { "editor.formatOnSave": true, "editor.formatOnPaste": true, "editor.codeActionsOnSave": { "source.organizeImports": true } }, // for .json with comment "[jsonc]": { "editor.formatOnSave": true, "editor.formatOnPaste": true } }
この設定でTypescriptまたはBabelで、ファイル保存時・ペースト時に自動整形され、且つimportの最適化(未使用importの削除+並び替え)が行われるようになります。ワークスペース設定なので、他のプロジェクトの整形に影響を与える事なく、自動整形が設定されます。.vscode/settings.jsonはgitで管理するといいと思います。
後はcommand + sをすると、Prettier+ESLint(TSLint)の整形処理が実行されます。
Prettierの設定は「.prettierrc」を参照してくれているようです。
未使用importの整理についてはできないっぽい?ようです。(ご存知の方いたら教えてください!)
Babel:eslintのエラー・警告集
Cannot find module 'eslint-config-standard'
Cannot find module 'eslint-config-standard' Referenced from: /private/tmp/test/eslint-prettier-example/.eslintrc.json Error: Cannot find module 'eslint-config-standard' Referenced from: /private/tmp/test/eslint-prettier-example/.eslintrc.json at ModuleResolver.resolve (/private/tmp/test/eslint-prettier-example/node_modules/eslint/lib/util/module-resolver.js:74:19) at resolve (/private/tmp/test/eslint-prettier-example/node_modules/eslint/lib/config/config-file.js:515:25) at load (/private/tmp/test/eslint-prettier-example/node_modules/eslint/lib/config/config-file.js:584:26) at configExtends.reduceRight (/private/tmp/test/eslint-prettier-example/node_modules/eslint/lib/config/config-file.js:421:36) at Array.reduceRight (<anonymous>) at applyExtends (/private/tmp/test/eslint-prettier-example/node_modules/eslint/lib/config/config-file.js:403:28) at loadFromDisk (/private/tmp/test/eslint-prettier-example/node_modules/eslint/lib/config/config-file.js:556:22) at Object.load (/private/tmp/test/eslint-prettier-example/node_modules/eslint/lib/config/config-file.js:592:20) at Config.getLocalConfigHierarchy (/private/tmp/test/eslint-prettier-example/node_modules/eslint/lib/config.js:226:44) at Config.getConfigHierarchy (/private/tmp/test/eslint-prettier-example/node_modules/eslint/lib/config.js:180:43)
eslint-plugin-standardはeslint-config-standardに依存しているので、eslint-config-standardも一緒にインストールしましょう。
yarn add -D eslint-plugin-standard eslint-config-standard
ESLint couldn't find the plugin "eslint-plugin-node"
Oops! Something went wrong! :( ESLint: 4.10.0. ESLint couldn't find the plugin "eslint-plugin-node". This can happen for a couple different reasons: 1. If ESLint is installed globally, then make sure eslint-plugin-node is also installed globally. A globally-installed ESLint cannot find a locally-installed plugin. 2. If ESLint is installed locally, then it's likely that the plugin isn't installed correctly. Try reinstalling by running the following: npm i eslint-plugin-node@latest --save-dev If you still can't figure out the problem, please stop by https://gitter.im/eslint/eslint to chat with the team.
メッセージ通り、eslint-plugin-nodeをインストールします。
yarn add -D eslint-plugin-node
ESLint couldn't find the plugin "eslint-plugin-promise".
Oops! Something went wrong! :( ESLint: 4.10.0. ESLint couldn't find the plugin "eslint-plugin-promise". This can happen for a couple different reasons: 1. If ESLint is installed globally, then make sure eslint-plugin-promise is also installed globally. A globally-installed ESLint cannot find a locally-installed plugin. 2. If ESLint is installed locally, then it's likely that the plugin isn't installed correctly. Try reinstalling by running the following: npm i eslint-plugin-promise@latest --save-dev If you still can't figure out the problem, please stop by https://gitter.im/eslint/eslint to chat with the team.
メッセージ通り、eslint-plugin-nodeをインストールします。
yarn add -D eslint-plugin-promise
ESLint couldn't find the plugin "eslint-plugin-import".
Oops! Something went wrong! :( ESLint: 4.19.1. ESLint couldn't find the plugin "eslint-plugin-import". This can happen for a couple different reasons: 1. If ESLint is installed globally, then make sure eslint-plugin-import is also installed globally. A globally-installed ESLint cannot find a locally-installed plugin. 2. If ESLint is installed locally, then it's likely that the plugin isn't installed correctly. Try reinstalling by running the following: npm i eslint-plugin-import@latest --save-dev If you still can't figure out the problem, please stop by https://gitter.im/eslint/eslint to chat with the team.
メッセージ通り、eslint-plugin-importをインストールします。
yarn add -D eslint-plugin-import
Configuration for rule "indent" is invalid:
/private/tmp/test/eslint-prettier-example/node_modules/eslint-config-standard/index.js: Configuration for rule "indent" is invalid: Value "[object Object]" should NOT have additional properties. Referenced from: /private/tmp/test/eslint-prettier-example/.eslintrc.json Error: /private/tmp/test/eslint-prettier-example/node_modules/eslint-config-standard/index.js: Configuration for rule "indent" is invalid: Value "[object Object]" should NOT have additional properties. Referenced from: /private/tmp/test/eslint-prettier-example/.eslintrc.json at validateRuleOptions (/private/tmp/test/eslint-prettier-example/node_modules/eslint/lib/config/config-validator.js:113:15) at Object.keys.forEach.id (/private/tmp/test/eslint-prettier-example/node_modules/eslint/lib/config/config-validator.js:153:9) at Array.forEach (<anonymous>) at validateRules (/private/tmp/test/eslint-prettier-example/node_modules/eslint/lib/config/config-validator.js:152:30) at Object.validate (/private/tmp/test/eslint-prettier-example/node_modules/eslint/lib/config/config-validator.js:230:5) at loadFromDisk (/private/tmp/test/eslint-prettier-example/node_modules/eslint/lib/config/config-file.js:549:19) at load (/private/tmp/test/eslint-prettier-example/node_modules/eslint/lib/config/config-file.js:592:20) at configExtends.reduceRight (/private/tmp/test/eslint-prettier-example/node_modules/eslint/lib/config/config-file.js:421:36) at Array.reduceRight (<anonymous>) at applyExtends (/private/tmp/test/eslint-prettier-example/node_modules/eslint/lib/config/config-file.js:403:28)
これは、create-react-appに内蔵されたeslintのバージョンでは不足していると起きるようです。
create-react-app自体のバージョンと、内蔵eslintのバージョンはそれぞれ以下の通りです。この組み合わせではエラーが起きました。
$ create-react-app --version 1.5.2 $ eslint --version v4.10.0
なので、ちょっと嫌ですが別途eslintの最新バージョンをインストールしてしまいます。
yarn add -D eslint
現状では「v4.19.1」インストールされました。このバージョンであればエラーは出ませんでした。
prettier-eslintは使わないの?
PSA: I'm no longer using prettier-eslint. I use raw prettier and disable all eslint style rules.
— Kent C. Dodds (@kentcdodds) September 29, 2017
My life has been better ever since...
ネット上でprettier-eslintを使った例が見られますが、prettier-eslintの作者が↑のようにもう使ってないよ、と言っているので、使わなくて大丈夫です。
今後はeslint-plugin-prettier と eslint-config-prettierの組み合わせを使えば特に問題ないかと思われます。
Typescript:tslintのエラー・警告集
yarn lintで大量のrule requires type informationが発生する
Warning: The 'await-promise' rule requires type information. Warning: The 'no-unused-variable' rule requires type information. Warning: The 'no-use-before-declare' rule requires type information. Warning: The 'return-undefined' rule requires type information. Warning: The 'no-floating-promises' rule requires type information. Warning: The 'no-unnecessary-qualifier' rule requires type information. Warning: The 'no-unnecessary-type-assertion' rule requires type information. Warning: The 'strict-type-predicates' rule requires type information.
projectを指定すると直ります。
"lint": "tslint -c tslint.json --exclude **/*.d.ts --exclude node_modules --fix **/*.tsx"
↓
"lint": "tslint -c tslint.json --exclude **/*.d.ts --exclude node_modules --project . --fix **/*.tsx"
雑感
これをまとめるの、実はかなり苦戦しました。(単に私が最近のフロントエンド事情・歴史を知らないからですが)もう使われていないモジュールの例に惑わされたり、バージョン違いによる謎エラーなどなど。
こうして記事にまとめておかないと、1週間後に全部忘れて「eslint + prettier動かねー!「tslint+prettier動かねー!」と発狂して全てを諦めてしまいそうです。
lintは最初は調査や設定に手間がかかりますが、lint + prettierの効果は絶大で、チームの内で横行するオレオレコードフォーマットルールを撲滅したり、しょうもない凡ミスをlintで気づく事を自動化する事ができます。
また、本来コードレビューでは設計やアーキテクチャが問題ないかを見て欲しいのに、
「ここにスペースを追加して下さい」
「ファイル末尾に空行を入れて下さい」
「1行が長すぎるので改行を挟んで下さい」
「行末にセミコロンが抜けています」
等という指摘に終始されてしまい、本来のレビュー目的が全スルーされる事がよくありますが、これを防ぐ事に繋がります。
最近私は運悪く実際にそれを経験してしまいましたが、このコードレビューのしょうもない指摘はプロジェクトの品質を下げる事に繋がるだけでなく、退屈な修正作業によってモチベーションが低下する事にも繋がってしまうので、是非設定しておきたいですね!