prettier-plugin-organize-imports で import 文を自動フォーマットする

2023/02/27に公開

これはなに

筆者は ES Modules ベースのコードの import 文を自動フォーマットするツールとして長らく prettier-plugin-organize-imports を愛用しています。一時期は ESLint でフォーマットしていましたが、試行錯誤の末にこのプラグインによるフォーマット方法に至りました。

本稿ではその導入手順をフォーマットの仕組みを軽く交えながら紹介します。

prettier-plugin-organize-imports の概要

https://github.com/simonhaenisch/prettier-plugin-organize-imports

TypeScript ( or JavaScript ) コードにある import 文を自動フォーマットする Prettier のプラグインです。ここでいうフォーマットは主に以下の 3 つの挙動を意味します。

  1. import 文をアルファベット昇順でソート。

    • {} 内のモジュールもソートする。
  2. 冗長に書かれた import

    - import { useEffect } from ‘react’;
    - import { type FC } from ‘react’;
    + import { useEffect, type FC } from ‘react’;
    
  3. インポートされたモジュールのうち未使用のものを削除する。

内部は TypeScript の organizeImports という API をほぼデフォルトオプションのまま呼び出しているだけです。非常にシンプルな実装ですね。

https://github.com/microsoft/TypeScript/blob/2f43308dc456396efb132629e37f82daa4c94447/src/services/types.ts#L639

ソートがアルファベット昇順に限定されているなど利用者側に設定を変更する余地が無いのは不便にも感じられますが、TypeScript という言語が推奨する形(= デフォルトオプション)に寄せることである種の堅牢性を担保しているとも言えるでしょう。

元ネタは VSCode に標準搭載されているフォーマット機能

TypeScript or JavaScript ファイルを開いた状態で Organize Imports を実行すると、先述のルールで自動フォーマットされます。

sort

こちらもソートのルールは原則アルファベット昇順のみであり、それ以外のルールで上書きする余地はないようです。

未使用となっているモジュールがあれば、併せて消去されます。

remove unused

./vscode/settings.json に以下の設定を追加することでファイル保存するたびにこのフォーマット機能が発火するため、都度コマンドを実行する手間を省けます。

./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 の場合

.prettierrc.js
module.exports = {
  //...
  plugins: ['prettier-plugin-organize-imports'],
};

https://github.com/simonhaenisch/prettier-plugin-organize-imports#prettier-3

prettier 2 の場合

.prettierrc.js
const organizeImports = require('prettier-plugin-organize-imports');

module.exports = {
  //...
  plugins: [organizeImports],
};

https://github.com/simonhaenisch/prettier-plugin-organize-imports/issues/34

オプション

ソートのルールは変更できませんが、未使用のモジュールを削除するか否かは設定できます。

.prettierrc.js
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 の再現も可能でしょう。

https://github.com/import-js/eslint-plugin-import

しかし未使用モジュールの自動削除は eslint-plugin-imports では実現できないため、別の ESLint プラグインで補う必要があります。いくつか選択肢がありますが、 eslint-plugin-unused-imports は Organize Imports を再現するのに十分な性能を備えています。

https://github.com/sweepline/eslint-plugin-unused-imports

.eslintrc.js
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

https://palantir.github.io/tslint/rules/ordered-imports/

TSLint は ordered-imports という import 文に関するルールを標準搭載していました。こちらはソートのルールやグルーピングなどに関するオプションがいくつか用意されているのが特徴ですが、基本的には VSCode の Organize Imports と同等の振る舞いをすることがコンセプトとなっています。そのため未使用モジュールを自動削除する機能も搭載していました。

なぜ Linter ではなく Prettier でフォーマットするのか

コードの見栄えというコンテキストには Prettier、プログラムの振る舞いに影響しうるコンテキストには ESLint という理屈で筆者は双方を使い分けているためです。

先述の通り ESLint でも同等の自動フォーマットは実現可能です[1]。しかし import 文の順序や未使用モジュールの有無はあくまでコードの見栄えに関連する話であり、プログラムの振る舞いに直接影響しないものである[2]というのが筆者の現在の考えです。

import 文のソートルールをカスタマイズできないことをデメリットとも見なせますが、もともと VSCode の Organize Imports に慣れ親しんでいたため筆者はこれをデメリットと感じておらず、先述の使い分けの理屈から Prettier を使うのが妥当と考えています。

脚注
  1. 事実、2019 年に TSLint の開発終了が告知されるまで筆者は TSLint で自動補正していました。 ↩︎

  2. IE のみ import 順序で挙動が変わる事はありましたが、それはイレギュラーケースであって、本来ならば影響すべきではありません。 ↩︎

Discussion