@rovik/eslint-config
@rovik/eslint-config
Общий ESLint-конфиг (flat config, ESLint 10) для TypeScript-проектов на Node.js и Vue 3. Включает набор собственных правил custom-rules/*.
defineEslintNode()— для Node.js / NestJS проектов (*.ts,*.mts,*.cts)defineEslintVue()— для Vue 3 + TypeScript проектов с FSD-архитектурой (*.ts,*.vue)- Полная поддержка TypeScript: пакет написан на TS, поставляется с типами, конфиг можно писать в
eslint.config.ts - Опциональный type-aware режим (
tsconfigRootDir) - Опциональная поддержка Tailwind CSS v4 через
eslint-plugin-tailwindcss(tailwind)
Требования
| Зависимость | Версия |
|---|---|
| Node.js | ^20.19.0 || ^22.13.0 || >=24 |
| eslint | ^10.0.0 (peer) |
| typescript | >=5.0.0 <6.1.0 (peer) |
| eslint-plugin-tailwindcss, tailwindcss | ^4.0.0 (optional peer, только для опции tailwind) |
Установка
pnpm add -D @rovik/eslint-config eslint typescript jiti
# или
npm i -D @rovik/eslint-config eslint typescript jiti
jiti нужен ESLint, чтобы читать конфиг в формате eslint.config.ts. Если конфиг пишете в eslint.config.js / .mjs, jiti не нужен.
Все плагины (typescript-eslint, @stylistic, eslint-plugin-vue, import-x, simple-import-sort, unused-imports, fsd-import) ставятся вместе с пакетом, отдельно их устанавливать не нужно.
Исключение — Tailwind CSS. Он нужен не во всех проектах, поэтому ставится отдельно и только при использовании опции tailwind:
pnpm add -D eslint-plugin-tailwindcss tailwindcss
Использование
Создайте eslint.config.ts в корне проекта.
Node.js / NestJS
import { defineEslintNode } from '@rovik/eslint-config';
export default defineEslintNode();
Vue 3
import { defineEslintVue } from '@rovik/eslint-config';
export default defineEslintVue();
Подключать можно и через отдельные точки входа, тогда плагины для другой платформы не загружаются:
import { defineEslintNode } from '@rovik/eslint-config/node';
import { defineEslintVue } from '@rovik/eslint-config/vue';
Скрипты в package.json
{
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix"
}
}
Опции
Обе функции принимают один объект опций (тип DefineEslintOptions). Все опции необязательные.
| Опция | Тип | Описание |
|---|---|---|
ignores |
string[] |
Дополнительные игнорируемые пути |
overrides |
Linter.Config[] |
Свои flat-config объекты поверх общего конфига |
tsconfigRootDir |
string |
Включает type-aware линтинг |
tailwind |
TailwindOptions |
Включает проверку классов Tailwind CSS v4 |
Пример со всеми опциями:
import { defineEslintVue } from '@rovik/eslint-config';
export default defineEslintVue({
ignores: ['src/shared/api/generated/**'],
tsconfigRootDir: import.meta.dirname,
tailwind: {
cssConfigPath: './src/app/styles/main.css',
},
overrides: [
{
files: ['**/*.spec.ts'],
rules: {
'max-lines-per-function': 'off',
},
},
],
});
ignores?: string[]
Дополнительные glob-паттерны для игнорирования. Всегда игнорируются: **/coverage, **/dist, **/node_modules, vite.config.ts, vitest.config.ts, **/*.timestamp-*.
export default defineEslintVue({
ignores: ['src/legacy/**', '**/*.generated.ts'],
});
overrides?: Linter.Config[]
Flat-config объекты, которые добавляются в конец общего конфига. Через них переопределяются и отключаются правила.
export default defineEslintNode({
overrides: [
{
rules: {
'custom-rules/no-comments-except-todo': 'off',
},
},
{
files: ['**/*.spec.ts'],
rules: {
'max-lines-per-function': 'off',
'max-nested-callbacks': 'off',
},
},
],
});
tsconfigRootDir?: string
Включает type-aware линтинг (projectService из typescript-eslint). С ним custom-rules/variable-naming-convention определяет примитивность значения по типу, а не по синтаксису. Линтинг при этом становится медленнее.
export default defineEslintNode({
tsconfigRootDir: import.meta.dirname,
});
В TypeScript 6
import.meta.dirnameтребует"types": ["node"]вtsconfig.json, который покрываетeslint.config.ts.
tailwind?: TailwindOptions
Подключает eslint-plugin-tailwindcss для проектов на Tailwind CSS v4. По умолчанию выключено. Плагин нужно установить в проект отдельно:
pnpm add -D eslint-plugin-tailwindcss tailwindcss
Единственная обязательная настройка — cssConfigPath: путь к главному CSS-файлу Tailwind, в котором есть @import "tailwindcss";.
export default defineEslintVue({
tailwind: {
cssConfigPath: './src/app/styles/main.css',
},
});
Проверяются классы в class, :class (строка, массив, объект), className, @apply и в вызовах clsx(), cn(), cva(), tv(), twMerge() и т.п. Классы внутри тернарного оператора (:class="ok ? 'a' : 'b'") плагин не проверяет.
Включённые правила:
| Правило | Уровень | Автофикс | Описание |
|---|---|---|---|
tailwindcss/classnames-order |
error | ✅ | Порядок классов как у компилятора Tailwind |
tailwindcss/enforces-canonical-classname |
error | ✅ | Каноническое написание класса |
tailwindcss/enforces-negative-arbitrary-values |
error | ✅ | - не ставится перед произвольными значениями |
tailwindcss/enforces-shorthand |
error | ✅ | px-4 py-4 заменяется на p-4 |
tailwindcss/important-modifier-suffix |
error | ✅ | ! ставится в конце класса (v4) |
tailwindcss/no-contradicting-classname |
error | Противоречащие классы (p-2 p-4) |
|
tailwindcss/no-unnecessary-arbitrary-value |
error | ✅ | w-[16px] заменяется на w-4 |
tailwindcss/no-custom-classname |
off | Классы, которых нет в Tailwind | |
tailwindcss/no-arbitrary-value |
off | Запрет произвольных значений |
no-custom-classname выключено, потому что в Vue-компонентах обычно есть собственные классы для <style scoped>. Остальные настройки плагина (attributes, functions, parseKeyFunctions, ignoredKeys) и уровни правил задаются в той же опции:
export default defineEslintVue({
tailwind: {
cssConfigPath: './src/app/styles/main.css',
functions: ['cn', 'clsx', 'tw'],
rules: {
'tailwindcss/no-custom-classname': ['error', {
whitelist: ['user-card', 'user-card__.*'],
}],
},
},
});
Относительный cssConfigPath считается от корня проекта: плагин ищет ближайшую к проверяемому файлу папку с eslint.config.* или package.json. В монорепозитории надёжнее передать абсолютный путь: path.join(import.meta.dirname, 'src/app/styles/main.css').
Что входит в конфиг
| Группа | Node | Vue |
|---|---|---|
@eslint/js recommended |
✅ | ✅ |
typescript-eslint recommended + доп. правила |
✅ | ✅ |
eslint-plugin-vue flat/recommended + доп. правила |
— | ✅ |
@stylistic (форматирование, max-len: 85) |
✅ | ✅ |
simple-import-sort (группы под NestJS / FSD) |
✅ | ✅ |
unused-imports |
✅ | ✅ |
import (через eslint-plugin-import-x) |
✅ | ✅ |
fsd-import (Feature-Sliced Design) |
— | ✅ |
tailwindcss (опция tailwind) |
опционально | опционально |
custom-rules/* |
✅ | ✅ |
Длина строки (max-len)
Максимальная длина строки — 85 символов (@stylistic/max-len). Не проверяются строки:
- с атрибутом класса:
class="...",:class="...",v-bind:class="...",className=.... Длинные наборы утилитарных классов (Tailwind и т.п.) не нужно переносить; - с
import ... from '...';иexport ... from '...';, а во Vue также строки вида} from '...';; - с атрибутом
d="..."у SVG-путей.
Исключение действует построчно. Если значение :class разбито на несколько строк, строки внутри значения проверяются как обычно.
Этот же лимит используют правила short-if-statement, emits-params-line-breaks и vue-single-line-tag-with-one-attribute: они решают, помещается ли код в одну строку.
Переопределения по типам файлов
*.d.ts: отключены правила про namespace*.types.ts(Node): отключеныno-undefи@typescript-eslint/no-namespace*.dto.ts,*.entity.ts(Node): вместо@stylistic/indentработаетcustom-rules/decorated-property-indent*.controller.ts(Node): вместоcustom-rules/max-params-with-constructorработаетmax-params: 5
Собственные правила (custom-rules/*)
| Правило | Node | Vue | Автофикс | Описание |
|---|---|---|---|---|
class-inheritance-layout |
✅ | ✅ | extends / implements не с новой строки |
|
composable-function-naming-convention |
✅ | export default function в use*.ts называется как файл |
||
decorated-property-indent |
*.dto/entity.ts |
✅ | Отступ +2 для свойства после декораторов | |
emits-params-line-breaks |
✅ | ✅ | Формат сигнатур в *Emits, без : void |
|
file-naming-convention |
✅ | .vue в PascalCase, use* в camelCase, остальные файлы в kebab-case |
||
max-params-with-constructor |
✅ | Максимум 3 параметра у функций, 5 у конструкторов | ||
multiple-properties-destructuring |
✅ | ✅ | ✅ | При деструктуризации 2+ свойств каждое на отдельной строке |
namespace-class-padding |
✅ | ✅ | ✅ | Без пустых строк на краях namespace, с пустыми строками на краях класса |
namespace-declaration-order |
✅ | ✅ | Порядок внутри namespace: enum, interface, type | |
namespace-name-match-file-name |
✅ | ✅ | ✅ | Имя namespace совпадает с именем файла |
namespace-requires-types-file |
✅ | ✅ | namespace объявляется только в *.types.ts |
|
no-comments-between-imports |
✅ | ✅ | ✅ | Без комментариев между импортами |
no-comments-except-todo |
✅ | ✅ | Разрешены только TODO-комментарии и директивы eslint-* |
|
no-empty-file |
✅ | ✅ | Запрет пустых файлов | |
no-empty-first-line |
✅ | ✅ | ✅ | Файл не начинается с пустой строки |
no-explicit-undefined-return |
✅ | ✅ | ✅ | return; вместо return undefined; |
no-i18n-global-in-use-files |
✅ | ✅ | В composables вместо i18n.global используется useI18n() |
|
no-multiple-namespaces |
✅ | ✅ | Один namespace на файл | |
no-padding-between-variables |
✅ | ✅ | ✅ | Правила пустых строк между переменными, вызовами, return, if / switch / for |
no-raw-colors |
✅ | Запрет HEX / RGB / HSL цветов в коде | ||
no-self-import |
✅ | ✅ | Модуль не импортирует сам себя | |
no-types-outside-namespace |
✅ | ✅ | enum / interface / type объявляются только внутри namespace | |
no-unknown-type-assertion |
✅ | ✅ | Запрет as unknown |
|
no-vue-import-outside-allowed-paths |
✅ | .vue импортируется только в index.ts |
||
props-name-casing |
✅ | Поля интерфейса Props в camelCase |
||
require-emits-keyword |
✅ | В *Emits параметры называются event, value / key |
||
require-proper-imports-for-components |
✅ | ✅ | Внешние компоненты в kebab-case, свои в PascalCase, все импортированы | |
return-type-same-line |
✅ | ✅ | ), тип возврата и { на одной строке |
|
short-if-statement |
✅ | ✅ | ✅ | Короткий if в одну строку без скобок, длинный со скобками |
variable-naming-convention |
✅ | ✅ | Переменные в camelCase, примитивные константы в UPPER_CASE | |
vue-block-spacing |
✅ | ✅ | Ровно одна пустая строка между <script>, <template>, <style> |
|
vue-single-line-tag-with-one-attribute |
✅ | ✅ | Тег с одним атрибутом в одну строку, если помещается | |
vue-template-object-curly-spacing |
✅ | ✅ | { a: 1 } с пробелами внутри шаблона |
Правила с опциями
defineEslintVue({
overrides: [
{
rules: {
'custom-rules/no-raw-colors': ['error', {
allowedFiles: ['palette.ts'],
hint: 'Use colors from "@/shared/config/palette".',
}],
'custom-rules/require-proper-imports-for-components': ['error', {
externalPackages: ['my-ui-kit'],
externalPatterns: ['^el-'],
}],
},
},
],
});
defineEslintNode({
overrides: [
{
rules: {
'custom-rules/max-params-with-constructor': ['error', {
max: 4,
maxConstructor: 8,
}],
'custom-rules/multiple-properties-destructuring': ['error', {
minProperties: 3,
}],
},
},
],
});
Плагин отдельно
Собственные правила можно подключить в любой flat-config без общего конфига:
import customRules from '@rovik/eslint-config/plugin';
export default [
{
plugins: {
'custom-rules': customRules,
},
rules: {
'custom-rules/no-unknown-type-assertion': 'error',
},
},
];
Настройка редактора
VS Code / Cursor (.vscode/settings.json):
{
"editor.tabSize": 2,
"editor.rulers": [85],
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
}
WebStorm: Settings → Languages & Frameworks → JavaScript → Code Quality Tools → ESLint → Automatic ESLint configuration, включить Run eslint --fix on save.
Разработка
pnpm install
pnpm typecheck # проверка типов
pnpm test # тесты правил (RuleTester) и конфигов
pnpm build # сборка в dist/ (ESM + .d.ts)
Структура:
src/
├── index.ts # публичный API
├── node.ts / vue.ts # defineEslintNode / defineEslintVue
├── types.ts # DefineEslintOptions, TailwindOptions
├── constants/ # max-len, регулярки, списки
├── configs/
│ ├── core/ # сборка конфигов node / vue
│ ├── rules/ # наборы правил по группам
│ ├── overrides/ # переопределения по типам файлов
│ ├── ignores.ts
│ ├── tailwind.ts # опция tailwind (eslint-plugin-tailwindcss)
│ └── type-aware.ts # опция tsconfigRootDir
├── plugin/
│ ├── index.ts # плагин custom-rules
│ └── rules/ # по файлу на правило
└── utils/
tests/
├── rules/ # RuleTester-тесты правил
├── fixtures/ # тестовые проекты node / vue
├── configs.test.ts # интеграционные тесты конфигов
├── max-len.test.ts # исключения max-len
└── tailwind.test.ts # опция tailwind
Как добавить правило
- Создайте
src/plugin/rules/my-rule.tsчерезcreateRuleизsrc/utils/create-rule.ts. - Зарегистрируйте правило в
src/plugin/index.ts. - Включите его в
src/configs/rules/custom.ts. - Добавьте тесты в
tests/rules/.
Публикация
npm login
npm version patch # или minor / major
npm publish # prepublishOnly запускает typecheck, тесты и сборку
Подробная инструкция и решение типичных ошибок: RELEASING.md.
Лицензия
MIT
Related Packages
DevilTea's opinionated ESLint flat config for JavaScript, TypeScript, and Vue.
Reusable ESLint flat configs for TypeScript, React, Vitest, Vue, Tailwind CSS, a...
ESLint standard config for Vue.js version 3 projects with Typescript support
Version History
| Version | Released | License |
|---|---|---|
| 0.1.1 | MIT | |
| 0.1.0 | MIT |