В крупных проектах правила линтинга редко ограничиваются несколькими настройками. Обычно формируется единый набор требований к стилю кода, качеству архитектуры, использованию современных возможностей JavaScript и особенностям конкретного стека технологий. Когда одинаковые настройки необходимо применять в нескольких проектах, возникает проблема дублирования конфигурации.
Shareable Config — это механизм ESLint, позволяющий
вынести набор правил в отдельный пакет и подключать его в разных
проектах через директиву extends.
Основные задачи Shareable Config:
Без Shareable Config каждый проект вынужден хранить собственный набор правил:
module.exports = {
rules: {
semi: ["error", "always"],
quotes: ["error", "single"],
eqeqeq: "error",
no-var: "error",
prefer-const: "error"
}
};
При изменении стандартов такую конфигурацию пришлось бы обновлять во всех репозиториях вручную.
Shareable Config решает эту проблему через публикацию общего пакета настроек.
Предположим, компания использует единый стандарт JavaScript-разработки.
Создаётся пакет:
eslint-config-company
Внутри него хранится конфигурация:
module.exports = {
rules: {
semi: ["error", "always"],
quotes: ["error", "single"],
eqeqeq: "error"
}
};
После публикации пакет устанавливается в любой проект:
npm install --save-dev eslint-config-company
Подключение производится через extends:
module.exports = {
extends: ["company"]
};
Теперь весь набор правил автоматически применяется к проекту.
Механизм Shareable Config полностью основан на свойстве
extends.
Пример:
module.exports = {
extends: ["airbnb"]
};
Во время запуска ESLint:
Локальные настройки имеют приоритет над унаследованными.
Например:
module.exports = {
extends: ["company"],
rules: {
quotes: ["error", "double"]
}
};
Если в Shareable Config используются одинарные кавычки, а локально указаны двойные, итоговой станет локальная настройка.
Для Shareable Config ESLint использует специальную схему именования.
Полное имя пакета:
eslint-config-myconfig
Подключение:
extends: ["myconfig"]
Префикс eslint-config- можно не указывать.
ESLint автоматически добавляет его при поиске пакета.
Соответствие выглядит следующим образом:
| Пакет | Подключение |
|---|---|
| eslint-config-company | company |
| eslint-config-team | team |
| eslint-config-standard | standard |
В организациях часто используются scoped-пакеты npm.
Название:
@company/eslint-config
или
@company/eslint-config-base
Подключение:
extends: ["@company"]
либо:
extends: ["@company/base"]
ESLint автоматически определяет необходимый пакет по имени scope.
Минимальная структура пакета:
eslint-config-company/
├── package.json
└── index.js
Файл index.js:
module.exports = {
rules: {
semi: ["error", "always"],
quotes: ["error", "single"]
}
};
Файл package.json:
{
"name": "eslint-config-company",
"version": "1.0.0",
"main": "index.js"
}
После публикации пакет готов к использованию в любых проектах.
Начиная с современных версий ESLint основным способом настройки становится Flat Config.
Для Shareable Config в таком формате создаётся конфигурация:
export default [
{
rules: {
semi: ["error", "always"],
quotes: ["error", "single"]
}
}
];
Структура пакета:
eslint-config-company/
├── package.json
└── eslint.config.js
Подключение:
import companyConfig from "eslint-config-company";
export default [
...companyConfig
];
В экосистеме ESLint постепенно происходит переход именно к этому подходу.
Часто требуется несколько вариантов правил:
Структура пакета:
eslint-config-company/
├── index.js
├── react.js
├── node.js
└── typescript.js
Базовая конфигурация:
module.exports = {
rules: {
semi: ["error", "always"]
}
};
React-конфигурация:
module.exports = {
extends: ["./index"],
rules: {
"react/jsx-uses-react": "error"
}
};
Подключение:
extends: ["company/react"]
или
extends: ["company/typescript"]
Такой подход позволяет поддерживать разные наборы правил внутри одного npm-пакета.
Shareable Config может использовать другие Shareable Config.
Пример:
module.exports = {
extends: [
"eslint:recommended",
"standard"
]
};
Здесь конфигурация объединяет:
После этого поверх них можно добавлять собственные настройки:
module.exports = {
extends: [
"eslint:recommended",
"standard"
],
rules: {
semi: ["error", "always"]
}
};
Такой способ значительно сокращает объём конфигурации.
Часто конфигурация зависит от ESLint-плагинов.
Пример:
module.exports = {
plugins: ["import"],
rules: {
"import/order": "error"
}
};
Для корректной работы пакет должен объявлять зависимости.
Пример:
{
"name": "eslint-config-company",
"peerDependencies": {
"eslint": "^9.0.0",
"eslint-plugin-import": "^2.30.0"
}
}
Или:
{
"dependencies": {
"eslint-plugin-import": "^2.30.0"
}
}
Выбор подхода зависит от стратегии распространения конфигурации.
Исторически для Shareable Config часто использовались
peerDependencies.
Пример:
{
"peerDependencies": {
"eslint": "^9.0.0",
"eslint-plugin-react": "^7.37.0"
}
}
Преимущества:
Недостаток заключается в необходимости дополнительной установки зависимостей в каждом проекте.
ESLint содержит несколько встроенных конфигураций.
Наиболее известная:
extends: ["eslint:recommended"]
Shareable Config может использовать её как основу:
module.exports = {
extends: ["eslint:recommended"]
};
Затем поверх неё добавляются собственные требования:
module.exports = {
extends: ["eslint:recommended"],
rules: {
eqeqeq: "error",
no-var: "error",
prefer-const: "error"
}
};
Это позволяет избежать ручного перечисления десятков базовых правил.
ESLint позволяет подключать несколько конфигураций одновременно.
Пример:
module.exports = {
extends: [
"company",
"company/react",
"prettier"
]
};
Конфигурации применяются последовательно.
Последняя конфигурация получает наивысший приоритет.
Порядок подключения играет важную роль:
extends: [
"company",
"prettier"
]
Если правило определено в обеих конфигурациях, итоговым станет
вариант из prettier.
Shareable Config часто используется вместе с TypeScript.
Пример:
module.exports = {
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
parser: "@typescript-eslint/parser"
};
После публикации все TypeScript-проекты могут использовать единый набор правил:
extends: ["company/typescript"]
Это особенно важно для монорепозиториев и крупных корпоративных систем.
Отдельная React-конфигурация может выглядеть следующим образом:
module.exports = {
extends: [
"plugin:react/recommended"
],
settings: {
react: {
version: "detect"
}
}
};
Подключение:
extends: ["company/react"]
В результате все React-проекты получают одинаковые требования к JSX, компонентам и хукам.
Shareable Config может учитывать особенности серверной среды.
Пример:
module.exports = {
env: {
node: true
},
rules: {
"no-console": "off"
}
};
Подключение:
extends: ["company/node"]
Такой подход отделяет браузерные правила от серверных.
Подготовка пакета обычно включает следующие шаги:
Публикация:
npm publish
После публикации конфигурация становится доступной:
npm install --save-dev eslint-config-company
Поскольку Shareable Config влияет на качество и стиль кода, изменение правил может приводить к большому количеству новых ошибок.
Поэтому рекомендуется использовать семантическое версионирование.
Пример:
1.2.0
Добавление новых необязательных возможностей.
2.0.0
Изменение существующих правил или ужесточение требований.
Для корпоративных конфигураций особенно важно документировать изменения между версиями.
В монорепозитории может существовать единая конфигурация:
packages/
├── eslint-config-company
├── frontend
├── backend
└── shared
Каждый пакет подключает общий набор правил:
extends: ["company"]
Преимущества:
Минимизировать количество обязательных зависимостей
Каждый дополнительный плагин усложняет поддержку конфигурации.
Разделять конфигурации по назначению
Хорошая структура:
company
company/react
company/node
company/typescript
Использовать наследование
Вместо дублирования:
extends: ["./index"]
Документировать правила
Каждое нестандартное правило должно иметь объяснение причины его существования.
Соблюдать обратную совместимость
Неожиданное ужесточение правил может нарушить процесс разработки десятков проектов одновременно.
Выделять базовую конфигурацию
Базовый пакет должен содержать только универсальные правила:
module.exports = {
rules: {
eqeqeq: "error",
no-var: "error",
prefer-const: "error"
}
};
Технологически специфичные настройки рекомендуется выносить в отдельные конфигурации.
В зрелой инфраструктуре линтинга формируется следующая иерархия:
eslint-config-company
│
├── company/base
├── company/node
├── company/react
├── company/vue
├── company/typescript
└── company/testing
Проекты подключают только необходимые части:
extends: [
"company/typescript",
"company/react"
]
В результате достигается высокая степень переиспользования настроек, централизованное управление правилами и единый стандарт качества кода во всей экосистеме проектов.