Плагины являются одним из ключевых механизмов расширения ESLint. Базовая поставка линтера содержит ограниченный набор правил, тогда как плагины позволяют добавлять новые проверки, поддерживать сторонние технологии, реализовывать корпоративные стандарты кодирования и автоматизировать контроль качества исходного кода.
Плагин представляет собой обычный npm-пакет, содержащий набор объектов, которые ESLint способен загрузить и использовать во время анализа файлов. Внутри одного плагина могут находиться:
Благодаря такой архитектуре один пакет может предоставлять полноценную экосистему для работы с конкретным фреймворком, языковым расширением или набором внутренних стандартов разработки.
Минимальная структура проекта плагина обычно выглядит следующим образом:
eslint-plugin-example/
│
├── package.json
├── index.js
│
├── rules/
│ ├── no-console-log.js
│ └── prefer-const-name.js
│
├── configs/
│ ├── recommended.js
│ └── strict.js
│
└── processors/
└── markdown.js
Каждый элемент отвечает за определённую часть функциональности.
Файл описывает npm-пакет и позволяет ESLint обнаруживать плагин.
Пример:
{
"name": "eslint-plugin-example",
"version": "1.0.0",
"main": "index.js"
}
По соглашению название плагина начинается с префикса:
eslint-plugin-
Например:
eslint-plugin-react
eslint-plugin-vue
eslint-plugin-import
eslint-plugin-unicorn
При подключении префикс обычно опускается:
{
plugins: ["react"]
}
ESLint автоматически понимает, что необходимо загрузить пакет:
eslint-plugin-react
Главным файлом обычно является index.js, экспортирующий
объект плагина.
Пример:
module.exports = {
rules: {},
configs: {},
processors: {}
};
Современный вариант для Flat Config:
export default {
rules: {},
configs: {},
processors: {}
};
Этот объект становится основным интерфейсом взаимодействия ESLint с плагином.
Наиболее важной частью большинства плагинов является раздел
rules.
Каждое правило представляет собой отдельный модуль.
Структура:
rules/
└── no-console-log.js
Экспорт правила:
module.exports = {
meta: {},
create(context) {
return {};
}
};
Подключение в основном файле:
module.exports = {
rules: {
"no-console-log": require("./rules/no-console-log")
}
};
После публикации правило будет доступно под именем:
example/no-console-log
Использование:
{
rules: {
"example/no-console-log": "error"
}
}
Каждое правило состоит из двух основных частей:
module.exports = {
meta: {},
create(context) {}
};
Содержит метаданные.
Пример:
meta: {
type: "problem",
docs: {
description: "Запрещает использование console.log"
},
fixable: "code",
schema: []
}
Основные свойства:
| Свойство | Назначение |
|---|---|
| type | Тип правила |
| docs | Документация |
| schema | Описание параметров |
| fixable | Поддержка автоисправления |
| messages | Шаблоны сообщений |
Поле type помогает классифицировать нарушение.
type: "problem"
Возможные значения:
problem
suggestion
layout
Ошибки, влияющие на корректность программы.
Пример:
if (value = 10) {
}
Рекомендации по улучшению кода.
Пример:
let name = "John";
Вместо:
const name = "John";
Правила форматирования.
Пример:
const a=1;
Вместо:
const a = 1;
Используется для хранения информации о правиле.
docs: {
description: "Запрещает console.log",
recommended: true
}
Обычно применяется генераторами документации и инструментами автоматического анализа.
Позволяет описывать параметры правила через JSON Schema.
Без параметров:
schema: []
С одним параметром:
schema: [
{
type: "object",
properties: {
allowWarn: {
type: "boolean"
}
},
additionalProperties: false
}
]
Использование:
{
rules: {
"example/no-console-log": [
"error",
{
allowWarn: true
}
]
}
}
Позволяет хранить шаблоны ошибок централизованно.
messages: {
forbidden: "Использование console.log запрещено."
}
Вызов сообщения:
context.report({
node,
messageId: "forbidden"
});
Такой подход упрощает поддержку и локализацию правил.
Метод create() является сердцем любого правила.
Сигнатура:
create(context)
Параметр context содержит информацию о текущем
анализе:
create(context) {
return {};
}
Возвращаемый объект определяет обработчики AST-узлов.
ESLint строит AST (Abstract Syntax Tree) и проходит по каждому узлу.
Пример:
create(context) {
return {
Identifier(node) {
}
};
}
Обработчик будет вызван для каждого идентификатора программы.
Пример AST:
const userName = "John";
Содержит узел:
Identifier
с именем:
userName
Запрет переменной с именем foo.
module.exports = {
meta: {
type: "problem",
schema: []
},
create(context) {
return {
Identifier(node) {
if (node.name === "foo") {
context.report({
node,
message: "Имя foo запрещено."
});
}
}
};
}
};
Для регистрации ошибки применяется метод:
context.report()
Пример:
context.report({
node,
message: "Ошибка."
});
Либо через идентификатор сообщения:
context.report({
node,
messageId: "forbidden"
});
Возможна передача дополнительных данных:
context.report({
node,
messageId: "invalidName",
data: {
name: node.name
}
});
Шаблон:
messages: {
invalidName: "Имя '{{name}}' запрещено."
}
Если правило может исправлять код автоматически, используется свойство:
fixable: "code"
В отчёте необходимо вернуть исправление.
Пример:
context.report({
node,
message: "Используйте let вместо var.",
fix(fixer) {
return fixer.replaceText(node.kind, "let");
}
});
Объект fixer предоставляет множество методов:
replaceText()
replaceTextRange()
insertTextAfter()
insertTextBefore()
remove()
removeRange()
Плагин может содержать готовые наборы настроек.
Структура:
configs/
└── recommended.js
Пример конфигурации:
module.exports = {
rules: {
"example/no-console-log": "error",
"example/prefer-const-name": "warn"
}
};
Экспорт:
module.exports = {
configs: {
recommended: require("./configs/recommended")
}
};
Подключение:
extends: [
"plugin:example/recommended"
]
Часто создаются разные уровни строгости.
Пример:
recommended
strict
all
Структура:
configs: {
recommended,
strict,
all
}
Назначение:
| Конфигурация | Особенности |
|---|---|
| recommended | Базовые проверки |
| strict | Усиленные требования |
| all | Все правила плагина |
Процессоры позволяют анализировать не только JavaScript-файлы.
Примеры:
Структура:
module.exports = {
preprocess(text, filename) {
return [text];
},
postprocess(messages) {
return messages.flat();
}
};
Экспорт:
module.exports = {
processors: {
markdown: require("./processors/markdown")
}
};
Вызывается до запуска линтера.
Пример:
preprocess(source) {
return [source];
}
Можно извлечь фрагменты JavaScript из документа:
# Заголовок
```js
console.log("test");
Затем передать найденные блоки в ESLint.
---
## Метод postprocess
Выполняется после проверки.
Пример:
```js
postprocess(messages) {
return messages.flat();
}
Основная задача — объединение и корректировка сообщений об ошибках.
Некоторые плагины экспортируют дополнительные окружения.
Пример:
module.exports = {
environments: {
browserplus: {
globals: {
MyGlobal: true
}
}
}
};
Подключение:
{
env: {
"example/browserplus": true
}
}
После этого глобальная переменная станет известна линтеру.
По мере роста проекта структура становится более сложной.
Пример:
eslint-plugin-company/
│
├── src/
│ ├── rules/
│ │ ├── architecture/
│ │ ├── naming/
│ │ ├── security/
│ │ └── performance/
│ │
│ ├── configs/
│ ├── processors/
│ ├── environments/
│ └── utils/
│
├── tests/
│
└── docs/
Подход обеспечивает:
Большие плагины часто содержат общие функции.
Пример:
utils/
├── ast.js
├── selectors.js
└── messages.js
Файл:
function isConsoleLog(node) {
return (
node.object.name === "console" &&
node.property.name === "log"
);
}
module.exports = {
isConsoleLog
};
Использование:
const { isConsoleLog } = require("../utils/ast");
Такой подход уменьшает дублирование кода между правилами.
Для проверки работы правил применяется RuleTester.
Структура:
tests/
└── no-console-log.test.js
Пример:
const { RuleTester } = require("eslint");
const rule = require("../rules/no-console-log");
Создание тестера:
const tester = new RuleTester();
Проверка:
tester.run(
"no-console-log",
rule,
{
valid: [
"alert('hello')"
],
invalid: [
{
code: "console.log('hello')",
errors: 1
}
]
}
);
Тесты являются обязательной частью профессиональных ESLint-плагинов.
Итоговая структура главного файла может выглядеть следующим образом:
const noConsoleLog = require("./rules/no-console-log");
const recommended = require("./configs/recommended");
const markdown = require("./processors/markdown");
module.exports = {
rules: {
"no-console-log": noConsoleLog
},
configs: {
recommended
},
processors: {
markdown
}
};
Такой объект представляет полный контракт между ESLint и плагином. Линтер загружает экспортированный модуль, регистрирует правила, конфигурации, процессоры и дополнительные расширения, после чего они становятся доступны для использования в конфигурации проекта.