@rovik/eslint-config

Shared ESLint flat configs (Node.js / Vue + TypeScript) with a set of custom rules
385
Install
npm install @rovik/eslint-config
Latest Version:0.1.1
License:MIT
Last Updated:Sep 29, 2026
Links: npm
Maintainer: rovik

@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

Как добавить правило

  1. Создайте src/plugin/rules/my-rule.ts через createRule из src/utils/create-rule.ts.
  2. Зарегистрируйте правило в src/plugin/index.ts.
  3. Включите его в src/configs/rules/custom.ts.
  4. Добавьте тесты в tests/rules/.

Публикация

npm login
npm version patch        # или minor / major
npm publish              # prepublishOnly запускает typecheck, тесты и сборку

Подробная инструкция и решение типичных ошибок: RELEASING.md.

Лицензия

MIT

Related Packages

@deviltea/eslint-config

DevilTea's opinionated ESLint flat config for JavaScript, TypeScript, and Vue.

21,263 0
@nitra/eslint-config

An ESLint shareable config for projects using Vue and Node

30,246
eslint-config-loderunner

Reusable ESLint flat configs for TypeScript, React, Vitest, Vue, Tailwind CSS, a...

10,279 1
@dsb-norge/eslint-config-dsb-vue3-ts

ESLint standard config for Vue.js version 3 projects with Typescript support

3,371 0
slidev-md-lint

Slidev の .md に書いた Vue(HTML)部分を ESLint でチェック・自動整形する共有設定...

407 0

Version History

Version Released License
0.1.1 MIT
0.1.0 MIT