Стек: React, TypeScript, ESLint 9, IDE: VS Code.

Сразу к делу:  конфиг и результат его применения 

Зачем организовывать импорты

Импорты в приложении – самодокументируемая шапка файла. Взглянув на неё, можно быстро сделать вывод о содержимом: какие библиотеки, компоненты, типы и стили - если речь о React-компоненте - используются в файле, и создать общее впечатление о модуле.

Хорошо, когда эта «шапка» имеет единый порядок: мы быстро можем увидеть, например, какие типы использует файл, много ли компонентов он содержит и т.п. Короче говоря – это ускоряет и точечный, и общий анализ файла. Ну и просто приятно, когда код содержится в порядке и подчиняется единым правилам.

Цели, поставленные для организации импортов

  • Разделить импорты на группы: библиотеки / внешние / внутренние / типы TypeScript / стили и прочее.

  • Типы TypeScript тоже должны быть разделены на группы, соответствующие основным группам импортов. Также необходимо использовать в файле только top-level type imports (import type { ... }), inline type imports не использовать (мне кажется, так удобнее).

  • Чтобы разделители (новые строки) добавлялись автоматически между определёнными группами, а не между всеми подряд.

Делать это вручную или просить команду всегда выделять импорты отдельно – можно, но сложно. Всё равно будут ошибки, что послужит поводом для комментария на ревью.

Попытка использовать только eslint-plugin-import

Первый плагин, который я попробовала – популярный eslint-plugin-import. У него есть важное правило import/consistent-type-specifier-style, которое позволяет отделить все inline-импорты типов в top-level. (Если существуют другие способы выделения типов в top-level imports – было бы интересно узнать.)

С помощью этого плагина я хотела настроить и порядок импортов, но не хватило кастомизации, а именно – выделения стилей в отдельную группу (нужно создавать кастомную с регулярными выражениями), расстановки новых строк только между определенными группами, а не между всеми. Это мелочи, которые можно было решить ручной доработкой, но хотелось добиться полной автоматической правки файла при сохранении.

Добавление плагина eslint-plugin-perfectionist

В итоге я настроила порядок импортов с помощью другого популярного плагина – eslint-plugin-perfectionist. У него больше групп по умолчанию и более гибкая настройка расположения новых строк между группами. Единственный недостаток: нет встроенного правила для автоматической группировки импортов типов в соответствии с порядком основных импортов – расстановку импортов типов нужно прописывать явно в массиве групп.

Получившийся конфиг

В итоге я использовала комбинацию двух вышеуказанных плагинов, и это позволило добиться желаемого результата - автоматической настройки без ручных доработок. И некоторого снижения количества комментариев в код-ревью)

Обратите внимание на customGroups. По задумке импорты React должны идти первыми и в основных импортах, и в типах TypeScript. Группа type-react идёт первой в массиве customGroups, поэтому при сортировке типы React отделяются первыми в отдельную группу. Если поменять порядок групп в customGroups, типы React попадут наверх вместе с обычными импортами React, а это нам не нужно.

import importPlugin from 'eslint-plugin-import';
import perfectionisPlugin from 'eslint-plugin-perfectionist';

export default {
  files: ['**/*.{js,ts,tsx}'],
  plugins: { perfectionist: perfectionisPlugin, import: importPlugin },
  rules: {
    'import/consistent-type-specifier-style': ['error', 'prefer-top-level'], // разрешает только top-level type import
    'import/newline-after-import': ['error', { count: 1 }], // пустая строка после импортов

    'perfectionist/sort-imports': [
      'error',
      {
        type: 'alphabetical', // сортировка элементов по алфавиту
        order: 'asc', // порядок от a к z
        ignoreCase: true, // игнорирование регистра при сортировке
        groups: [
          'side-effect', // side-effect импорты
          { newlinesBetween: 1 },
          'builtin', // node встроенные модули (fs, path)
          { newlinesBetween: 1 },
          'react',
          { newlinesBetween: 0 },
          'external', // npm пакеты
          { newlinesBetween: 1 },
          'internal', // внутренние импорты
          { newlinesBetween: 'ignore' },
          'parent', // ../ импорты
          { newlinesBetween: 'ignore' },
          'sibling', // ./ импорты
          { newlinesBetween: 'ignore' },
          'index', // ./index импорты
          { newlinesBetween: 1 },
          'import', // любые импорты
          { newlinesBetween: 1 },
          // все импорты типов
          'type-react',
          { newlinesBetween: 0 },
          'type-external',
          { newlinesBetween: 1 },
          'type-internal',
          { newlinesBetween: 'ignore' },
          'type-parent',
          { newlinesBetween: 'ignore' },
          'type-sibling',
          { newlinesBetween: 'ignore' },
          'type-index',
          { newlinesBetween: 0 },
          'type-import',
          { newlinesBetween: 1 },
          'side-effect-style', // side-effect импорты стилей
          { newlinesBetween: 0 },
          'style', // импорты стилей
        ],
        customGroups: [
          {
            groupName: 'type-react',
            elementNamePattern: ['^react$', '^react-.*'],
            selector: 'type',
          },
          {
            groupName: 'react',
            elementNamePattern: ['^react$', '^react-.*'],
          },
        ],
        newlinesBetween: 1, // пустая строка между группами
        internalPattern: ['^app/.+'], // петтерны внутренних путей
      },
    ],
  },
};

Результат применения

примеры до

примеры после

Если у вас есть опыт организации импортов в больших проектах или вы знаете другие способы автоматической сортировки – буду рада обсудить в комментариях!