prettier-plugin-organize-imports で import 文を自動フォーマットする
これはなに
筆者は ES Modules ベースのコードの import 文を自動フォーマットするツールとして長らく prettier-plugin-organize-imports を愛用しています。一時期は ESLint でフォーマットしていましたが、試行錯誤の末にこのプラグインによるフォーマット方法に至りました。
本稿ではその導入手順をフォーマットの仕組みを軽く交えながら紹介します。
prettier-plugin-organize-imports の概要
TypeScript ( or JavaScript ) コードにある import 文を自動フォーマットする Prettier のプラグインです。ここでいうフォーマットは主に以下の 3 つの挙動を意味します。
-
import 文をアルファベット昇順でソート。
-
{}
内のモジュールもソートする。
-
-
冗長に書かれた import 文を結合。
- import { useEffect } from ‘react’; - import { type FC } from ‘react’; + import { useEffect, type FC } from ‘react’;
-
インポートされたモジュールのうち未使用のものを削除する。
内部は TypeScript の organizeImports
という API をほぼデフォルトオプションのまま呼び出しているだけです。非常にシンプルな実装ですね。
ソートがアルファベット昇順に限定されているなど利用者側に設定を変更する余地が無いのは不便にも感じられますが、TypeScript という言語が推奨する形(= デフォルトオプション)に寄せることである種の堅牢性を担保しているとも言えるでしょう。
元ネタは VSCode に標準搭載されているフォーマット機能
TypeScript or JavaScript ファイルを開いた状態で Organize Imports
を実行すると、先述のルールで自動フォーマットされます。
こちらもソートのルールは原則アルファベット昇順のみであり、それ以外のルールで上書きする余地はないようです。
未使用となっているモジュールがあれば、併せて消去されます。
./vscode/settings.json
に以下の設定を追加することでファイル保存するたびにこのフォーマット機能が発火するため、都度コマンドを実行する手間を省けます。
{
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
}
ちなみに VSCode による Organize Imports
を適用すると強制的に curly brackets 前後にスペースが入りますが、Prettier の設定が bracketSpacing: false
となっていればそちらが優先されます。よって Prettier とフォーマットがコンフリクトする心配は無用です。
プラグインを導入する
node モジュールをインストール
npm install -D prettier prettier-plugin-organize-imports typescript
# or yarn
yarn add -D prettier prettier-plugin-organize-imports typescript
# or pnpm
pnpm add -D prettier prettier-plugin-organize-imports typescript
一応このプラグインは *.ts
, *.tsx
ファイルだけでなく*.js
, *.jsx
ファイルに対しても適用可能ですが、TypeScript の API を使って動作する都合上 typescript
のインストールが必須となります。
*.vue
ファイルにも適用したい場合は、これに加え @volar/vue-typescript
のインストールが必須となります。
pnpm を利用するプロジェクトではプラグインを手動でロードする必要がある
基本的に Prettier はプラグインを自動的に検出する仕様のため特別な設定は不要なのですが、Node パッケージマネージャーに pnpm を使用している場合は以下のように手動でロードする必要があります。
prettier 3 の場合
module.exports = {
//...
plugins: ['prettier-plugin-organize-imports'],
};
prettier 2 の場合
const organizeImports = require('prettier-plugin-organize-imports');
module.exports = {
//...
plugins: [organizeImports],
};
オプション
ソートのルールは変更できませんが、未使用のモジュールを削除するか否かは設定できます。
module.exports = {
// ...
// 削除したくない場合は true
organizeImportsSkipDestructiveCodeActions: true, // default: false
};
フォーマットを局所的に無効化する
実装上の理由などで自動フォーマットを局所的に無効化したい場合は、以下のように // prettier-ignore
コメントを都度挿入します。
import "./App.css";
// prettier-ignore
import reactLogo from './assets/react.svg';
// prettier-ignore
import {useState,useEffect} from 'react';
Linter で同様の自動補正は可能か
元来 Lint は様々なコーディングルールを機械的に強制するシステムですが、当然ながら import に関するルールおよびそれを強制(≒ 自動補正)する口も用意されています。
ESLint
eslint-plugin-imports
という import に関する様々なルール機能を持つプラグインがあり、 import/order
ルールを設定することで任意の import 順序を定義できます。設定次第では VSCode の Organize Imports の再現も可能でしょう。
しかし未使用モジュールの自動削除は eslint-plugin-imports
では実現できないため、別の ESLint プラグインで補う必要があります。いくつか選択肢がありますが、 eslint-plugin-unused-imports
は Organize Imports を再現するのに十分な性能を備えています。
module.exports = {
plugins: ['unused-imports'],
rules: {
// ...
'no-unused-vars': ['off'], // or '@typescript-eslint/no-unused-vars': ['off'],
'unused-imports/no-unused-imports': 'error',
'unused-imports/no-unused-vars': [
'warn',
{
vars: 'all',
varsIgnorePattern: '^_',
args: 'after-used',
argsIgnorePattern: '^_',
},
],
},
};
TSLint
TSLint は ordered-imports
という import 文に関するルールを標準搭載していました。こちらはソートのルールやグルーピングなどに関するオプションがいくつか用意されているのが特徴ですが、基本的には VSCode の Organize Imports と同等の振る舞いをすることがコンセプトとなっています。そのため未使用モジュールを自動削除する機能も搭載していました。
なぜ Linter ではなく Prettier でフォーマットするのか
コードの見栄えというコンテキストには Prettier、プログラムの振る舞いに影響しうるコンテキストには ESLint という理屈で筆者は双方を使い分けているためです。
先述の通り ESLint でも同等の自動フォーマットは実現可能です[1]。しかし import 文の順序や未使用モジュールの有無はあくまでコードの見栄えに関連する話であり、プログラムの振る舞いに直接影響しないものである[2]というのが筆者の現在の考えです。
import 文のソートルールをカスタマイズできないことをデメリットとも見なせますが、もともと VSCode の Organize Imports に慣れ親しんでいたため筆者はこれをデメリットと感じておらず、先述の使い分けの理屈から Prettier を使うのが妥当と考えています。
Discussion