Утилита @eslint/migrate-config

Утилита @eslint/migrate-config предназначена для автоматизированной миграции конфигураций ESLint из устаревших форматов в современную структуру, основанную на flat config. Основная задача инструмента — снизить объём ручной работы при переходе проектов на новые версии ESLint, где традиционный формат .eslintrc постепенно заменяется конфигурацией через JavaScript-модули и массивы конфигурационных объектов.

Миграция конфигурации затрагивает не только синтаксис, но и архитектурную модель ESLint. Старые подходы опираются на каскадное наследование и множество неявных правил, тогда как flat config строится на явной композиции конфигурационных блоков без скрытого объединения.

Ключевая функция утилиты заключается в анализе существующих конфигурационных файлов и генерации эквивалентной структуры в новом формате с учётом совместимости плагинов, правил и окружений.


Архитектурные различия конфигураций ESLint

Понимание работы @eslint/migrate-config требует различения двух моделей конфигурации.

Классическая модель (.eslintrc)

Старая система конфигурации опирается на несколько типов файлов:

  • .eslintrc
  • .eslintrc.json
  • .eslintrc.js
  • .eslintrc.yml

Основные характеристики:

  • каскадное наследование конфигураций;
  • автоматическое объединение правил из разных уровней проекта;
  • использование extends для подключения пресетов;
  • глобальные настройки через env и globals.

Эта модель удобна, но скрывает множество правил объединения, что усложняет отладку.


Flat config

Новая система конфигурации строится вокруг единого JavaScript-модуля:

export default [
  {
    files: ["**/*.js"],
    rules: {
      semi: "error"
    }
  }
];

Особенности:

  • отсутствует неявное наследование;
  • конфигурация представляет собой массив объектов;
  • каждый объект явно определяет область действия;
  • плагины подключаются как обычные модули;
  • порядок объектов имеет значение.

Роль @eslint/migrate-config в процессе перехода

Утилита выполняет преобразование между двумя моделями конфигурации. Она анализирует входные данные и формирует эквивалентную структуру flat config, сохраняя поведение правил максимально близким к исходному.

Основные этапы работы:

  1. чтение конфигурационных файлов;
  2. разрешение extends и подключаемых пресетов;
  3. преобразование env, globals, plugins;
  4. маппинг правил на новый формат;
  5. генерация итогового массива конфигураций.

Установка и запуск утилиты

Установка осуществляется через пакетный менеджер:

npm install -D @eslint/migrate-config

После установки доступна CLI-команда:

npx @eslint/migrate-config

При запуске инструмент автоматически ищет конфигурационные файлы ESLint в проекте и начинает процесс преобразования.


Обработка конфигурационных файлов

Поддерживаемые источники

Утилита обрабатывает:

  • .eslintrc.js
  • .eslintrc.json
  • конфигурации внутри package.json

При наличии нескольких источников применяется приоритетность, аналогичная ESLint:

  1. JavaScript-конфигурации;
  2. JSON/YAML конфигурации;
  3. настройки из package.json.

Разрешение extends

Одним из самых сложных этапов является обработка extends.

Пример старой конфигурации:

{
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended"
  ]
}

В процессе миграции:

  • eslint:recommended разворачивается в набор правил;
  • plugin:react/recommended преобразуется в flat-представление плагина;
  • результирующие правила объединяются в один или несколько конфигурационных блоков.

Утилита старается сохранить порядок применения правил, так как он влияет на итоговое поведение линтера.


Преобразование правил

Правила ESLint в flat config остаются концептуально теми же, но изменяется способ их группировки.

Базовое преобразование

Исходная конфигурация:

{
  "rules": {
    "eqeqeq": "error",
    "no-console": "warn"
  }
}

После миграции:

export default [
  {
    rules: {
      eqeqeq: "error",
      "no-console": "warn"
    }
  }
];

Плагины

В старом формате плагины указываются строками:

{
  "plugins": ["react"]
}

В flat config они становятся импортируемыми объектами:

import react from "eslint-plugin-react";

export default [
  {
    plugins: {
      react
    }
  }
];

Утилита автоматически пытается сопоставить имя плагина с установленным пакетом.


Работа с env и globals

env

Старый формат:

{
  "env": {
    "browser": true,
    "node": true
  }
}

Преобразование:

  • env раскладывается в набор глобальных переменных и параметров окружения;
  • часть настроек заменяется на languageOptions.

globals

{
  "globals": {
    $: "readonly"
  }
}

В flat config:

export default [
  {
    languageOptions: {
      globals: {
        $: "readonly"
      }
    }
  }
];

Генерация flat config структуры

Итоговый результат работы утилиты — файл eslint.config.js, который содержит массив конфигурационных объектов.

Пример структуры:

import js from "@eslint/js";
import react from "eslint-plugin-react";

export default [
  js.configs.recommended,
  {
    files: ["**/*.js"],
    plugins: {
      react
    },
    rules: {
      "react/react-in-jsx-scope": "off"
    }
  },
  {
    files: ["**/*.test.js"],
    rules: {
      "no-unused-expressions": "off"
    }
  }
];

Утилита старается разделять конфигурацию на логические блоки:

  • базовые рекомендации;
  • настройки плагинов;
  • специфичные правила для файловых групп.

Обработка сложных случаев

Наследование с overrides

Старый формат:

{
  "overrides": [
    {
      "files": ["*.test.js"],
      "rules": {
        "no-undef": "off"
      }
    }
  ]
}

В flat config:

export default [
  {
    files: ["*.test.js"],
    rules: {
      "no-undef": "off"
    }
  }
];

Каждый override становится отдельным объектом конфигурации.


Конфликты правил

При миграции возможны ситуации, когда одно и то же правило определяется в разных слоях конфигурации.

Механизм разрешения:

  • более специфичные files перекрывают глобальные настройки;
  • порядок объектов в массиве влияет на итоговое значение;
  • утилита старается сохранить исходную семантику ESLint.

Ограничения автоматической миграции

Несмотря на высокую степень автоматизации, существуют сценарии, требующие ручной доработки.

Динамические конфигурации

Если .eslintrc.js использует функции:

module.exports = {
  rules: process.env.NODE_ENV === "production" ? prodRules : devRules
};

автоматическое преобразование не всегда может корректно интерпретировать условную логику.


Кастомные плагины

Плагины, не имеющие явного ESM-эквивалента или использующие нестандартную структуру экспорта, могут потребовать ручного подключения.


Неявные extends

Некоторые пакеты используют цепочки расширений, которые сложно полностью развернуть в статическую структуру.


Типичный процесс миграции проекта

  1. запуск @eslint/migrate-config в корне проекта;
  2. генерация предварительного eslint.config.js;
  3. установка недостающих плагинов;
  4. проверка сопоставления правил;
  5. устранение несовместимостей вручную;
  6. запуск ESLint в новом режиме flat config.

Диагностика результатов миграции

После генерации конфигурации важно анализировать:

  • корректность применения правил к файлам;
  • отсутствие дублирующихся конфигурационных блоков;
  • правильность подключения плагинов;
  • соответствие поведения ESLint до и после миграции.

Особое внимание требуется к проектам с большим количеством overrides и сложной иерархией extends, так как именно в них чаще всего проявляются расхождения поведения линтера.


Поведение в монорепозиториях

В монорепозиториях утилита обрабатывает несколько конфигураций, но итоговая структура может требовать разделения по пакетам.

Характерные особенности:

  • разные ESLint-конфиги в подпакетах;
  • локальные overrides для каждого workspace;
  • общие базовые правила на верхнем уровне.

Миграция часто приводит к необходимости централизовать часть правил в shared-конфигурацию и распределить специфичные настройки по пакетам.


Совместимость с ESLint 9+

Утилита ориентирована на версии ESLint, где flat config становится основным форматом. В таких версиях:

  • .eslintrc считается устаревшим;
  • приоритет отдаётся eslint.config.js;
  • плагины переходят на модульную систему.

@eslint/migrate-config выступает как промежуточный инструмент, уменьшающий разрыв между поколениями конфигураций и обеспечивающий детерминированный переход без ручного переписывания всей структуры правил.