ESLint построен вокруг набора правил (rules), каждое из которых отвечает за проверку определённого аспекта исходного кода. Одни правила контролируют стиль оформления, другие выявляют потенциальные ошибки, третьи обеспечивают соблюдение архитектурных соглашений проекта.
Механизм регистрации правил позволяет подключать собственные проверки и расширять стандартные возможности ESLint. Благодаря этому инструмент способен анализировать не только синтаксис JavaScript, но и учитывать требования конкретного проекта, команды или компании.
Регистрация правила представляет собой процесс объявления правила внутри плагина ESLint и предоставления его линтеру для дальнейшего использования в конфигурации.
Каждое правило ESLint состоит из двух основных частей:
meta).create).Типичная структура выглядит следующим образом:
module.exports = {
meta: {
type: "problem",
docs: {
description: "Запрещает использование console.log"
},
schema: []
},
create(context) {
return {
CallEx * pression(node) {
// логика проверки
}
};
}
};
После регистрации правило становится доступным для использования в конфигурации ESLint.
Секция meta содержит служебную информацию о правиле.
Пример:
meta: {
type: "problem",
docs: {
description: "Запрет console.log",
recommended: true
},
schema: [],
messages: {
forbiddenConsole: "Использование console.log запрещено."
}
}
Наиболее важные поля:
| Поле | Назначение |
|---|---|
type |
Тип правила |
docs |
Документация |
schema |
Описание параметров |
messages |
Шаблоны сообщений |
fixable |
Поддержка автоисправления |
hasSuggestions |
Поддержка подсказок |
ESLint выделяет три категории:
type: "problem"
Поиск потенциальных ошибок.
type: "suggestion"
Рекомендации по улучшению кода.
type: "layout"
Контроль форматирования.
Пример:
meta: {
type: "layout"
}
Такое правило относится исключительно к оформлению кода.
Метод create() вызывается при запуске анализа файла.
Он получает объект контекста:
create(context) {
return {};
}
Через context правило взаимодействует с ESLint:
create(context) {
console.log(context.filename);
}
На практике используется набор методов объекта контекста.
Например:
context.report(...)
или
context.getSourceCode()
ESLint анализирует код через абстрактное синтаксическое дерево (AST).
Каждому типу узла соответствует обработчик:
create(context) {
return {
VariableDeclaration(node) {
},
FunctionDeclaration(node) {
},
CallEx * pression(node) {
}
};
}
При обходе дерева ESLint автоматически вызывает нужный обработчик.
Код:
const user = "Alex";
console.log(user);
Создаёт узлы:
VariableDeclarationVariableDeclaratorIdentifierCallExpressionMemberExpressionКаждый из них может быть обработан пользовательским правилом.
Рассмотрим правило, запрещающее использование
console.log.
module.exports = {
meta: {
type: "problem",
schema: [],
messages: {
forbiddenConsole:
"Использование console.log запрещено."
}
},
create(context) {
return {
CallEx * pression(node) {
if (
node.callee.type === "MemberExpression" &&
node.callee.object.name === "console" &&
node.callee.property.name === "log"
) {
context.report({
node,
messageId: "forbiddenConsole"
});
}
}
};
}
};
Теперь правило способно обнаруживать:
console.log("debug");
и выдавать диагностическое сообщение.
В плагине ESLint все правила обычно собираются в одном месте.
Структура:
eslint-plugin-company/
│
├── rules/
│ ├── no-console-log.js
│ ├── no-debugger.js
│ └── no-var.js
│
└── index.js
Файл регистрации:
module.exports = {
rules: {
"no-console-log":
require("./rules/no-console-log"),
"no-debugger":
require("./rules/no-debugger"),
"no-var":
require("./rules/no-var")
}
};
После этого ESLint получает доступ ко всем правилам плагина.
Каждое зарегистрированное правило используется через имя плагина.
Плагин:
eslint-plugin-company
Правило:
no-console-log
Полное имя:
company/no-console-log
Конфигурация:
module.exports = {
rules: {
"company/no-console-log": "error"
}
};
Плагин может предоставлять не только отдельные правила, но и готовые конфигурации.
Пример:
module.exports = {
rules: {
"no-console-log":
require("./rules/no-console-log")
},
configs: {
recommended: {
rules: {
"company/no-console-log": "error"
}
}
}
};
Подключение:
extends: [
"plugin:company/recommended"
]
После подключения все зарегистрированные правила активируются автоматически.
Многие правила принимают настройки.
Например:
{
"company/max-function-lines": [
"error",
50
]
}
Для описания параметров используется schema.
Пример:
meta: {
schema: [
{
type: "integer",
minimum: 1
}
]
}
Теперь ESLint сможет валидировать настройки до выполнения правила.
Некорректная конфигурация:
{
"company/max-function-lines": [
"error",
"many"
]
}
будет отклонена ещё на этапе загрузки конфигурации.
Сообщения рекомендуется хранить отдельно.
Плохо:
context.report({
node,
message:
"Использование console.log запрещено"
});
Лучше:
meta: {
messages: {
forbiddenConsole:
"Использование console.log запрещено"
}
}
Использование:
context.report({
node,
messageId: "forbiddenConsole"
});
Преимущества:
Сообщение может содержать переменные.
Описание:
messages: {
forbiddenMethod:
"Метод '{{name}}' запрещён."
}
Использование:
context.report({
node,
messageId: "forbiddenMethod",
data: {
name: "alert"
}
});
Результат:
Метод 'alert' запрещён.
ESLint поддерживает автоматическое исправление ошибок.
Для этого необходимо указать:
meta: {
fixable: "code"
}
или
meta: {
fixable: "whitespace"
}
Пример:
module.exports = {
meta: {
fixable: "code"
},
create(context) {
return {
Identifier(node) {
if (node.name === "foo") {
context.report({
node,
message: "foo запрещён",
fix(fixer) {
return fixer.replaceText(
node,
"bar"
);
}
});
}
}
};
}
};
Запуск:
eslint . --fix
выполнит исправление автоматически.
Иногда автоматическая замена неоднозначна.
Для таких случаев используются suggestions.
Регистрация:
meta: {
hasSuggestions: true
}
Использование:
context.report({
node,
message: "Использование var нежелательно",
suggest: [
{
desc: "Заменить на let",
fix(fixer) {
return fixer.replaceText(
node,
"let"
);
}
}
]
});
Редактор может предложить исправление без автоматического применения.
Во многих правилах требуется доступ к тексту программы.
Для этого используется:
const sourceCode =
context.getSourceCode();
Получение фрагмента:
sourceCode.getText(node);
Пример:
const text =
sourceCode.getText(node);
Результат:
user.profile.name
Подобные возможности активно используются при регистрации сложных правил.
Одно правило может анализировать множество типов узлов.
Пример:
create(context) {
return {
FunctionDeclaration(node) {
},
ArrowFunctionEx * pression(node) {
},
FunctionEx * pression(node) {
}
};
}
Такой подход позволяет реализовать единую проверку для всех вариантов объявления функций.
ESLint поддерживает регистрацию обработчиков через селекторы.
Пример:
create(context) {
return {
"CallExpression[callee.name='eval']"(node) {
context.report({
node,
message: "eval запрещён"
});
}
};
}
Вместо ручных проверок используется декларативный фильтр.
Это делает код правила значительно компактнее.
Для некоторых задач требуется отслеживать момент входа и выхода из узла.
Синтаксис:
create(context) {
return {
FunctionDeclaration(node) {
},
"FunctionDeclaration:exit"(node) {
}
};
}
Вход выполняется до обработки дочерних элементов.
Выход выполняется после завершения обхода поддерева.
Подобный механизм часто используется для анализа областей видимости и стеков состояний.
Полный пример структуры:
eslint-plugin-company/
│
├── index.js
│
└── rules/
└── no-console-log.js
module.exports = {
meta: {
type: "problem",
schema: [],
messages: {
forbidden:
"Использование console.log запрещено."
}
},
create(context) {
return {
CallEx * pression(node) {
if (
node.callee.type === "MemberExpression" &&
node.callee.object.name === "console" &&
node.callee.property.name === "log"
) {
context.report({
node,
messageId: "forbidden"
});
}
}
};
}
};
module.exports = {
rules: {
"no-console-log":
require("./rules/no-console-log")
}
};
module.exports = {
plugins: [
"company"
],
rules: {
"company/no-console-log":
"error"
}
};
После загрузки ESLint регистрирует правило, подключает его к процессу анализа и начинает применять ко всем проверяемым файлам.
Неправильно:
meta: {}
Правильно:
meta: {
schema: []
}
Даже если правило не принимает параметров, схема должна быть определена явно.
Неправильно:
context.report({
node,
message: "Ошибка"
});
Предпочтительно:
messages: {
error: "Ошибка"
}
Регистрация:
rules: {
"no-console-log": rule
}
Использование:
"company/no-console": "error"
ESLint не сможет найти правило из-за несовпадения имени.
Неправильно:
const rule = {};
Правильно:
module.exports = rule;
Без экспорта правило не будет зарегистрировано внутри плагина.
После подключения ESLint выполняет последовательность действий:
meta.schema.create.Именно регистрация связывает реализацию правила с инфраструктурой ESLint, превращая обычный JavaScript-модуль в полноценный механизм статического анализа кода.