Prettier + ESLint、それぞれのプラグインを組み合わせ、tsのimport周りを改善する方法をご紹介します!
以前にPrettierを使ってimport周り整えるプラグインについて記載しました
しかし、どちらもモヤモヤが残る結果になりました
| prettier-plugin-sort-imports | prettier-plugin-organize-imports | |
|---|---|---|
| 導入のしやすさ | ◯ | ◎ |
| importの並び順ルールの定義 | ◯ | ✕ |
| importのソート | ◯ | ◯ |
| 冗長なimportをまとめる | ✕ | ◯ |
| 未使用のimportの削除 | ✕ | ◯ |
prettier-plugin-organize-imports はほぼ完璧なものの
自分で並び順のルールを定義することが出来ません。
加えて、
改行がある場合・ない場合でソートの結果が変わります
// before
import React, { useState } from 'react'
import clsx from 'clsx'
// after
import React, { useState } from 'react'
import clsx from 'clsx'// before
import React, { useState } from 'react'
import clsx from 'clsx'
// after
import clsx from 'clsx'
import React, { useState } from 'react'しかし、今回記載するESLintのプラグインと組み合わせると
それらのモヤモヤも解消出来る事が分かったため備考欄も兼ねて記載していきます!!
検証した環境
| 1 | prettier | 3.2.5 |
| 2 | prettier-plugin-organize-imports | 3.2.4 |
| 3 | eslint | 8.57.0 |
| 4 | eslint-plugin-import | 2.29.1 |
前段(IDEの設定)
今回記載する方法を実施するにあたり prettier --write そして eslint --fix がファイルを保存した際に実行されるようにします。
これを設定するだけでDXが爆上がりするのでおすすめです!
VS Code
動作する事は確認したものの、
普段IntelliJ IDEAを使っているので自信が持てない部分ありです 🙏
VS Code用の2つのプラグインを追加
.setting.jsonの設定を更新
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"[typescript]": {
"editor.formatOnSave": false
}
}参考にさせていただいたページ曰く
“editor.formatOnSave”をfalseに設定するとESLintのfixが対象でないファイルのフォーマットが行われないため、ESLintでfixを実行するファイルのみ”editor.formatOnSave”をfalseにしましょう。
【VSCode】ESLint(fixあり/fixなし)とPrettierを保存時に走らせる #VSCode - Qiitaより転記
とのことですmm
Jet Brains系
Web Storm と Intellij IDEA のみになりそうですがそれぞれ設定ページから設定します
Prettier
言語 & フレームワーク > JavaScript > Prettier
- 自動 Prettier 構成を選択
- 次のファイルに実行: 必要なファイルを指定
- ex:
**/*.{js,ts,jsx,tsx,vue}
- ex:
- 保存時に実行に✓
ESLint
言語 & フレームワーク > JavaScript > コード品質ツール > ESLint
- 自動 ESLint 構成を選択
- 次のファイルに実行: 必要なファイルを指定
- ex:
**/*.{js,ts,jsx,tsx,vue}
- ex:
- 保存時に eslint —fix を実行に✓
import周りの改善
本題です!
手順としては、Prettier・ESLint合わせて、2つのプラグインを導入
- prettier-plugin-organize-imports
- eslint-plugin-import
最後に eslint-plugin-import の調整をします
prettier-plugin-organize-imports
まず prettier-plugin-organize-imports を導入します
npm install -D prettier-plugin-organize-importsmodule.exports = {
plugins: ['prettier-plugin-organize-imports'],
}ここまで設定しPrettierを実行すると
// before
import { initializeApp } from '@core/app'
import { updateSettings, createConnection } from '@server/database'
import { useState } from 'react'
import clsx from 'clsx'
import { Button } from '@ui/form/Button'
import { SampleComponent } from '@ui/sample/SampleComponent'
import React from 'react'// after
import { createConnection, updateSettings } from '@server/database'
import { Button } from '@ui/form/Button'
import { SampleComponent } from '@ui/sample/SampleComponent'
import clsx from 'clsx'
import React, { useState } from 'react'- 不要なimportを削除
- import順のソート
- import内のソート
- 冗長だったimportがひとまとめ
になります!
課題としては以下が残ります
- importの順番を自分で決められない
- 改行が入った場合の挙動が違う
そこでこの2点を解決するために、ESLintのプラグインを使います!
eslint-plugin-import
今回の最も重要となるプラグイン eslint-plugin-import を導入します
npm install -D eslint-plugin-importmodule.exports = {
plugins: ['import'],
rules: {
'import/order': [
'error',
{
'newlines-between': 'always',
groups: ['builtin', 'external', 'parent', 'sibling', 'index'],
},
],
},
}ポイントが2つあります
- rulesに
warnorerrorを設定する- これにより
eslint --fixを実行すると import/order の修正を行ってくれる
- これにより
'newlines-between': 'always'を設定する- eslint-plugin-import独自のグルーピング毎に改行が入ります
- なぜ必要かについての詳細は後述します
この状態でPrettierの実行・eslint —fix を実行すると
// after
import clsx from 'clsx'
import React, { useState } from 'react'
import { createConnection, updateSettings } from '@server/database'
import { Button } from '@ui/form/Button'
import { SampleComponent } from '@ui/sample/SampleComponent'react と clsx のみでグルーピングされ、それ以外のimportとで改行が入っているのが分かります!
これは先ほど記載した groups オプションに準拠した並び順になったためです
groups: ['builtin', 'external', 'parent', 'sibling', 'index'],公式の情報を日本語化して記載するとgroupsはそれぞれ以下の意味になっています
// 1. node "builtin" モジュール
import fs from 'fs';
import path from 'path';
// 2. "external" モジュール
import _ from 'lodash';
import chalk from 'chalk';
// 3. "internal" モジュール
// (パスやwebpackを設定して内部パスを異なる方法で扱っている場合)
import foo from 'src/foo';
// 4. "parent" ディレクトリからのモジュール
import foo from '../foo';
import qux from '../../foo/qux';
// 5. 同じディレクトリまたは兄弟ディレクトリからの "sibling" モジュール
import bar from './bar';
import baz from './bar/baz';
// 6. 現在のディレクトリの "index"
import main from './';
// 7. "object" インポート (TypeScriptでのみ利用可能)
import log = console.log;
// 8. "type" インポート (FlowとTypeScriptでのみ利用可能)
import type { Foo } from 'foo';’newlines-between’: ‘always’ が必要な理由
prettier-plugin-organize-imports は改行が入った箇所にはソートを行いません。
逆に言うと改行が入っていない場合は常にorganize-importのルールでソートを行います
もし 'newlines-between': 'always' を設定しなかった場合
- eslint-plugin-import はgroupsで決まった順でソート
- prettier-plugin-organize-imports はorganize-importのルールでソート
をそれぞれ行い、並び順がバッティングしてしまいます。
そこで常にグルーピング毎に改行を入れ
グループ毎のソートは prettier-plugin-organize-imports に任せる、としています
eslint-plugin-import の調整
eslint-plugin-import はかなり柔軟なプラグインで、
オプションを使用する事でかなり細かく自分好みな設定をする事が出来ます。
例えば以下のようなimportがあります
import clsx from 'clsx'
import Link from 'next/link'
import React, { useState } from 'react'
import { TestConstants } from '@/constants'
import { createConnection, updateSettings } from '@server/database'
import { Button } from '@ui/form/Button'
import { SampleComponent } from '@ui/sample/SampleComponent'- react は1番上に書きたい
@ui/で始まるコンポーネントは別途グループ化したい
といった要望があった場合に eslint-plugin-import は実現可能です!
先に結論を記載すると以下のようになります
'import/order': [
'error',
{
'newlines-between': 'always',
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
pathGroups: [
{
pattern: 'react',
group: 'builtin',
position: 'before',
},
{
pattern: '@ui/**',
group: 'internal',
position: 'before',
},
],
pathGroupsExcludedImportTypes: ['builtin'],
},
],この状態でprettier,eslint —fixを実行すると以下のようになります、見通し良くなった気がします ✨
import React, { useState } from 'react'
import clsx from 'clsx'
import Link from 'next/link'
import { Button } from '@ui/form/Button'
import { SampleComponent } from '@ui/sample/SampleComponent'
import { TestConstants } from '@/constants'
import { createConnection, updateSettings } from '@server/database'pathGroups
pathGroups は自分でグループを定義出来る機能です。
以下は「react を builtin の前に記述するグループとする」という意味合いになります
{
pattern: 'react',
group: 'builtin',
position: 'before',
},- pattern・・グルーピングしたいimport群を指定
minimatchの記述を使用可能- ex:
pattern: '{react,next/**}',
- group・・基準となる規定グループを指定
- position・・
groupで指定したグループ前後どちらに持ってくるか- 指定をしなかった場合は
groupで記述したグループに含まれるようになります
- 指定をしなかった場合は
pathGroupsExcludedImportTypes
ここに記載したグループのimportは pathGroups が適用されません
例えば react や next/link は external グループに元々含まれます。
つまり pathGroupsExcludedImportTypes で external を指定するとpathGroupsに記述しても反応しなくなります
pathGroupsExcludedImportTypes: ['builtin', 'external'],
pathGroups: [
{
// react や next/** はexternalグループに属す
// pathGroupsExcludedImportTypes にて external を指定しているため、
// 以下の記述は効果がない
pattern: '{react,next/**}',
group: 'builtin',
},
// @ui/** はinternalグループに属す
// pathGroupsExcludedImportTypes に internal は指定していないため、
// 新たなグループとなる
{
pattern: '@ui/**',
group: 'internal',
position: 'before',
},
],import clsx from 'clsx'
import Link from 'next/link' //新たなグループが作られない
import React, { useState } from 'react' //新たなグループが作られない
// @ui/**は新たなグループとなる
import { Button } from '@ui/form/Button'
import { SampleComponent } from '@ui/sample/SampleComponent'
import { TestConstants } from '@/constants'
import { createConnection, updateSettings } from '@server/database'注意点としてはデフォルト値が以下のように定義されていること
pathGroupsExcludedImportTypes: ['builtin', 'external', 'object']pathGroups が思ったように反応しない場合は pathGroupsExcludedImportTypes を調査してみて下さい