Пользовательские правила
Вы можете создавать пользовательские правила для использования с ESLint. Возможно, вы захотите создать пользовательское правило, если основные правила не охватывают ваш сценарий использования.
Вот базовая структура пользовательского правила:
// customRule.js
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "Description of the rule",
},
fixable: "code",
schema: [], // no options
},
create: function (context) {
return {
// callback functions
};
},
};
Структура правила
Исходный файл правила экспортирует объект со следующими свойствами. И пользовательские, и основные правила имеют эту структуру.
meta: (object) Содержит метаданные правила:
-
type: (string) Указывает тип правила:"problem","suggestion"или"layout":-
"problem": Правило выявляет код, который приведёт к ошибке или может вызвать неоднозначное поведение. Разработчикам следует считать устранение таких проблем приоритетной задачей. -
"suggestion": Правило выявляет то, что можно сделать лучше, но если код не изменять, ошибок не возникнет. -
"layout": Правило касается главным образом пробелов, точек с запятой, запятых и скобок — всех тех частей программы, которые определяют внешний вид кода, а не его выполнение. Эти правила работают с частями кода, не представленными в AST.
Тип правила можно использовать с параметром CLI
--fix-type, чтобы указать тип исправлений для применения. -
-
docs: (object) Свойства, часто используемые для генерации документации и инструментов. Обязательны для основных правил и необязательны для пользовательских. При необходимости пользовательские правила могут содержать здесь дополнительные свойства.-
description: (string) Краткое описание правила. Для основных правил используется в списке правил. -
dialects: (string[]) Диалекты языков, для которых предназначено это правило. Например,["JavaScript", "TypeScript"]. -
frozen: (boolean) Для основных правил указывает, является ли правило замороженным и больше не принимает запросы на добавление функций. -
recommended: (unknown) Для основных правил это логическое значение, указывающее, включено ли правило конфигурациейrecommendedиз@eslint/js. -
url: (string) Указывает URL-адрес полной документации. Редакторы кода часто используют его, чтобы предоставлять полезную ссылку на подсвеченные нарушения правил.
-
-
messages: (object) Объект, содержащий сообщения о нарушениях и рекомендации для правила. Обязателен для основных правил и необязателен для пользовательских. На сообщения можно ссылаться по ключам (messageIds) в вызовахcontext.report(). Подробнее см. в разделеmessageIds. -
fixable: (string) Значение"code"или"whitespace", если параметр--fixв командной строке автоматически исправляет проблемы, о которых сообщает правило.Важно: свойство
fixableобязательно для правил с возможностью исправления. Если это свойство не указано, ESLint выдаст ошибку при любой попытке правила выполнить исправление. Не указывайте свойствоfixable, если правило не может выполнять исправления. -
hasSuggestions: (boolean) Указывает, могут ли правила возвращать рекомендации (по умолчанию используетсяfalse, если значение не указано).Важно: свойство
hasSuggestionsобязательно для правил, предоставляющих рекомендации. Если для этого свойства не задано значениеtrue, ESLint выдаст ошибку при любой попытке правила предоставить рекомендацию. Не указывайте свойствоhasSuggestions, если правило не предоставляет рекомендации. -
schema: (object | array | false) Задает параметры, чтобы ESLint мог предотвращать некорректные конфигурации правил. Обязательно, если у правила есть параметры. -
defaultOptions: (array) Задает параметры по умолчанию для правила. Если свойство указано, любые параметры, заданные пользователем в конфигурации, будут рекурсивно объединены с ними. -
languages: (array) Указывает языки, для работы с которыми предназначено правило. Каждый элемент — это строка в формате"pluginName/languageName"(например,"js/js","markdown/gfm"). Специальные значения:-
"*"— правило работает с любым языком. -
"pluginName/*"— правило работает с любым языком, предоставляемым указанным плагином.
Если
languagesне указано, предполагается, что правило работает со всеми языками. Если указаноlanguagesи ни один из элементов не соответствует активному языку, ESLint выдаёт ошибку.// Rule only runs when the active language is the built-in JavaScript language meta: { languages: ["js/js"], } // Rule runs with any language from the "markdown" plugin meta: { languages: ["markdown/*"], } // Rule runs with any language meta: { languages: ["*"], }Идентификаторы языков сопоставляются в следующем порядке:
-
Шаблон
"*"— соответствует любому языку. -
Точное совпадение строки — элемент в точности совпадает со строкой активного языка (например,
"test/lang"). -
Шаблон
"plugin/*"— соответствует любому языку, имя плагина которого (илиmeta.namespace) совпадает с частью элемента, обозначающей плагин. -
Совпадение пространства имён — плагин, зарегистрированный под другим именем, может совпасть, если его
meta.namespaceсовпадает с частью элемента, обозначающей плагин.
// Rule runs with a specific language from a plugin whose meta.namespace is "markdown" // (e.g., the plugin is registered as "md" but declares meta.namespace = "markdown") meta: { languages: ["markdown/gfm"], } -
-
deprecated: (boolean | DeprecatedInfo) Указывает, устарело ли правило. Если правило не устарело, свойствоdeprecatedможно не указывать. Для DeprecatedInfo предусмотрена отдельная страница. -
replacedBy: (array, Устарело Используйте вместо негоmeta.deprecated.replacedBy.) Для устаревшего правила укажите правило (или правила), заменяющее его.
create(): Возвращает объект с методами, которые ESLint вызывает для «посещения» узлов при обходе абстрактного синтаксического дерева (AST, определённого в ESTree) кода JavaScript:
- Если ключ — это тип узла или селектор, ESLint вызывает соответствующую функцию обработчика посещения при движении вниз по дереву.
- Если ключ — это тип узла или селектор с
:exit, ESLint вызывает соответствующую функцию обработчика посещения при движении вверх по дереву. - Если ключ — это имя события, ESLint вызывает соответствующую функцию обработчика для анализа пути выполнения кода.
Правило может использовать текущий узел и окружающее его дерево, чтобы сообщать о проблемах или исправлять их.
Вот методы для правила array-callback-return:
function checkLastSegment (node) {
// report problem for function if last code path segment is reachable
}
module.exports = {
meta: { ... },
create: function(context) {
// declare the state of the rule
return {
ReturnStatement: function(node) {
// at a ReturnStatement node while going down
},
// at a function expression node while going up:
"FunctionExpression:exit": checkLastSegment,
"ArrowFunctionExpression:exit": checkLastSegment,
onCodePathStart: function (codePath, node) {
// at the start of analyzing a code path
},
onCodePathEnd: function(codePath, node) {
// at the end of analyzing a code path
}
};
}
};
Объект контекста
Объект context — единственный аргумент метода create в правиле. Например:
// customRule.js
module.exports = {
meta: { ... },
// `context` object is the argument
create(context) {
// ...
}
};
Как следует из названия, объект context содержит информацию, относящуюся к контексту правила.
Объект context имеет следующие свойства:
-
id: (string) Идентификатор правила. -
filename: (string) Имя файла, связанного с исходным кодом. -
physicalFilename: (string) При проверке файла возвращает полный путь к файлу на диске без сведений о блоках кода. При проверке текста возвращает значение, переданное в—stdin-filename, или<text>, если оно не указано. -
cwd: (string) Параметрcwd, переданный в Linter. Это путь к каталогу, который следует считать текущим рабочим каталогом. -
options: (array) Массив настроенных параметров этого правила. Массив не включает уровень серьёзности правила (см. соответствующий раздел). -
sourceCode: (object) ОбъектSourceCode, который можно использовать для работы с исходным кодом, переданным ESLint (см. раздел Доступ к исходному коду). -
settings: (object) Общие настройки из конфигурации. -
languageOptions: (object) Подробнее о каждом свойстве-
sourceType: ('script' | 'module' | 'commonjs') Режим для текущего файла. -
ecmaVersion: (number) Версия ECMA, используемая для разбора текущего файла. -
parser: (object): Парсер, используемый для разбора текущего файла. -
parserOptions: (object) Параметры парсера, настроенные для этого файла. -
globals: (object) Указанные глобальные переменные.
-
Кроме того, объект context имеет следующие методы:
-
report(descriptor). Сообщает о проблеме в коде (см. соответствующий раздел).
Примечание: В более ранних версиях ESLint объект context поддерживал дополнительные методы. Они были удалены в новом формате, и полагаться на них не следует.
Сообщение о проблемах
Основной метод, который используется при написании пользовательских правил, — context.report(). Он выводит предупреждение или ошибку (в зависимости от используемой конфигурации). Этот метод принимает один аргумент — объект со следующими свойствами:
-
messageId: (string) Идентификатор сообщения (см. messageIds); рекомендуется использовать вместоmessage. -
message: (string) Текст сообщения о проблеме (альтернативаmessageId). -
node: (необязательныйobject) Это может быть узел AST, токен или комментарий, связанный с проблемой. Если он указан, аlocне задано, начальная позиция узла используется в качестве позиции проблемы. -
loc: (необязательныйobject) Задаёт позицию проблемы. Если указаны иloc, иnode, вместоnodeиспользуется позиция изloc.-
start: Объект начальной позиции.-
line: (number) Номер строки, в которой возникла проблема (нумерация начинается с 1). -
column: (number) Номер столбца, в котором возникла проблема (нумерация начинается с 0).
-
-
end: Объект конечной позиции.-
line: (number) Номер строки, в которой возникла проблема (нумерация начинается с 1). -
column: (number) Номер столбца, в котором возникла проблема (нумерация начинается с 0).
-
-
-
data: (необязательныйobject) Данные заполнителей дляmessage. -
fix(fixer): (необязательныйfunction) Применяет исправление для устранения проблемы.
Обратите внимание: необходимо указать хотя бы одно из свойств — node или loc.
Самый простой пример — использовать только node и message:
context.report({
node: node,
message: "Unexpected identifier",
});
Узел содержит всю информацию, необходимую для определения номера строки и столбца проблемного текста, а также исходный текст, соответствующий узлу.
Использование заполнителей в сообщениях
В сообщении также можно использовать заполнители и передавать data:
context.report({
node: node,
message: "Unexpected identifier: {{ identifier }}",
data: {
identifier: node.name
}
});
Обратите внимание, что пробелы в начале и конце параметров сообщения необязательны.
Узел содержит всю информацию, необходимую для определения номера строки и столбца проблемного текста, а также исходный текст, соответствующий узлу.
messageIds
messageIds рекомендуется использовать для передачи сообщений в вызовах context.report() по следующим причинам:
- Сообщения о нарушениях правил можно хранить в централизованном объекте
meta.messagesдля удобства управления. - Сообщения о нарушениях правил не нужно дублировать и в файле правила, и в файле его тестов.
- В результате упрощается изменение сообщений о нарушениях правил, что способствует более частому вкладу в их улучшение и оптимизацию для максимальной ясности и полезности.
Файл правила:
// avoid-name.js
module.exports = {
meta: {
messages: {
avoidName: "Avoid using variables named '{{ name }}'"
}
},
create(context) {
return {
Identifier(node) {
if (node.name === "foo") {
context.report({
node,
messageId: "avoidName",
data: {
name: "foo",
}
});
}
}
};
}
};
Файл для проверки:
// someFile.js
var foo = 2;
// ^ error: Avoid using variables named 'foo'
Тесты:
// avoid-name.test.js
var rule = require("../../../lib/rules/avoid-name");
var RuleTester = require("eslint").RuleTester;
var ruleTester = new RuleTester();
ruleTester.run("avoid-name", rule, {
valid: ["bar", "baz"],
invalid: [
{
code: "foo",
errors: [
{
messageId: "avoidName",
},
],
},
],
});
Применение исправлений
Чтобы ESLint попытался исправить проблему, о которой вы сообщаете, укажите функцию fix при использовании context.report(). Функция fix получает один аргумент — объект fixer, который можно использовать для применения исправления. Например:
context.report({
node: node,
message: "Missing semicolon",
fix(fixer) {
return fixer.insertTextAfter(node, ";");
},
});
Здесь функция fix() используется для вставки точки с запятой после узла. Обратите внимание, что исправление не применяется немедленно и может вообще не примениться, если возникнет конфликт с другими исправлениями. После применения исправлений ESLint повторно запускает все включённые правила для исправленного кода и, возможно, применяет дополнительные исправления. Этот процесс повторяется до 10 раз или до тех пор, пока не останется проблем, которые можно исправить. После этого обо всех оставшихся проблемах будет сообщено как обычно.
Важно: свойство meta.fixable обязательно для правил с возможностью исправления. ESLint выдаст ошибку, если правило, реализующее функции fix, не экспортирует свойство meta.fixable.
Объект fixer имеет следующие методы:
-
insertTextAfter(nodeOrToken, text): Вставляет текст после указанного узла или токена. -
insertTextAfterRange(range, text): Вставляет текст после указанного диапазона. -
insertTextBefore(nodeOrToken, text): Вставляет текст перед указанным узлом или токеном. -
insertTextBeforeRange(range, text): Вставляет текст перед указанным диапазоном. -
remove(nodeOrToken): Удаляет указанный узел или токен. -
removeRange(range): Удаляет текст в указанном диапазоне. -
replaceText(nodeOrToken, text): Заменяет текст в указанном узле или токене. -
replaceTextRange(range, text): Заменяет текст в указанном диапазоне.
range — это массив из двух элементов, содержащий индексы символов в исходном коде. Первый элемент — начало диапазона (включительно), второй — конец диапазона (не включительно). Каждый узел и токен имеет свойство range, указывающее на соответствующий диапазон исходного кода.
Описанные выше методы возвращают объект fixing. Функция fix() может возвращать следующие значения:
- Объект
fixing. - Массив, содержащий объекты
fixing. - Итерируемый объект, перечисляющий объекты
fixing. В частности, функцияfix()может быть генератором.
Если вы создаёте функцию fix(), возвращающую несколько объектов fixing, эти объекты fixing не должны пересекаться.
Рекомендации по исправлениям:
- Избегайте исправлений, которые могут изменить поведение кода во время выполнения и привести к его неработоспособности.
- Делайте исправления как можно меньше. Неоправданно крупные исправления могут конфликтовать с другими исправлениями и препятствовать их применению.
- Выполняйте только одно исправление для каждого сообщения. Это требование обеспечивается тем, что из
fix()необходимо вернуть результат операции исправления. - Поскольку после первого раунда исправлений все правила запускаются повторно, правилу не нужно проверять, приведёт ли стиль кода после исправления к сообщениям об ошибках от других правил.
-
Например, предположим, что функция исправления хочет заключить ключ объекта в кавычки, но не знает, предпочитает ли пользователь одинарные или двойные кавычки.
{ foo: 1 } // should get fixed to either { 'foo': 1 } // or { "foo": 1 } -
Функция исправления может просто выбрать тип кавычек произвольно. Если выбор окажется неверным, полученный код будет автоматически обнаружен и исправлен правилом
quotes.
-
Примечание: минимальный размер исправления — рекомендуемая практика, однако в некоторых случаях может быть правильно расширить диапазон исправления, чтобы намеренно не допустить применения другими правилами исправлений в окружающем диапазоне за тот же проход. Например, если заменяющий текст объявляет новую переменную, полезно предотвратить другие изменения в области видимости переменной, поскольку они могут привести к конфликтам имён.
В следующем примере заменяется node, а также гарантируется, что за тот же проход не будут применены другие исправления в диапазоне node.parent:
context.report({
node,
message,
*fix(fixer) {
yield fixer.replaceText(node, replacementText);
// extend range of the fix to the range of `node.parent`
yield fixer.insertTextBefore(node.parent, "");
yield fixer.insertTextAfter(node.parent, "");
},
});
Конфликтующие исправления
Конфликтующие исправления — это исправления, которые вносят разные изменения в одну и ту же часть исходного кода. Невозможно указать, какое из конфликтующих исправлений будет применено.
Например, если два исправления изменяют символы с 0-го по 5-й, будет применено только одно из них.
Предоставление рекомендаций
В некоторых случаях автоматическое применение исправлений нежелательно — например, если исправление может изменить функциональность или если существует несколько корректных способов исправить правило в зависимости от замысла реализации (см. приведённые выше рекомендации по применению исправлений). В таких случаях можно использовать альтернативный параметр suggest для context.report(), позволяющий другим инструментам, например редакторам, предоставлять пользователям средства для ручного применения рекомендаций.
Чтобы предоставить рекомендации, используйте ключ suggest в аргументе отчёта и передайте массив объектов рекомендаций. Эти объекты представляют отдельные возможные исправления. Каждый из них должен содержать строковый ключ desc с описанием результата применения рекомендации или ключ messageId (см. ниже), а также ключ fix с функцией, определяющей результат рекомендации. Эта функция fix использует тот же API, что и обычные исправления (описанные выше в разделе Применение исправлений).
context.report({
node: node,
message: "Unnecessary escape character: \\{{character}}.",
data: { character },
suggest: [
{
desc: "Remove the `\\`. This maintains the current functionality.",
fix: function(fixer) {
return fixer.removeRange(range);
}
},
{
desc: "Replace the `\\` with `\\\\` to include the actual backslash character.",
fix: function(fixer) {
return fixer.insertTextBeforeRange(range, "\\");
}
}
]
});
Важно: свойство meta.hasSuggestions обязательно для правил, предоставляющих рекомендации. ESLint выдаст ошибку, если правило попытается предоставить рекомендацию, но не экспортирует это свойство.
Примечание: рекомендации применяются как отдельные изменения, без многоэтапного исправления. Каждая рекомендация должна касаться одного конкретного изменения в коде и не должна пытаться подстроиться под стили, заданные пользователем. Например, если рекомендация добавляет в код новую инструкцию, ей не следует пытаться соблюдать правильные отступы или учитывать предпочтения пользователя относительно наличия точек с запятой. Всё это можно исправить с помощью многоэтапного автоисправления, запущенного пользователем.
Рекомендации по созданию предложений:
- Не пытайтесь сделать слишком много и предлагать крупные рефакторинги, которые могут привести к множеству несовместимых изменений.
- Как отмечено выше, не пытайтесь подстраиваться под стили, заданные пользователем.
Рекомендации предназначены для предоставления исправлений. ESLint автоматически удалит рекомендацию целиком из вывода проверки, если функция fix этой рекомендации вернула null или пустой массив/последовательность.
messageIds рекомендаций
Вместо ключа desc для рекомендаций можно использовать messageId. Это работает так же, как messageIds для общей ошибки (см. messageIds). Вот пример использования messageId рекомендации в правиле:
module.exports = {
meta: {
messages: {
unnecessaryEscape: "Unnecessary escape character: \\{{character}}.",
removeEscape: "Remove the `\\`. This maintains the current functionality.",
escapeBackslash: "Replace the `\\` with `\\\\` to include the actual backslash character."
},
hasSuggestions: true
},
create: function(context) {
// ...
context.report({
node: node,
messageId: 'unnecessaryEscape',
data: { character },
suggest: [
{
messageId: "removeEscape", // suggestion messageId
fix: function(fixer) {
return fixer.removeRange(range);
}
},
{
messageId: "escapeBackslash", // suggestion messageId
fix: function(fixer) {
return fixer.insertTextBeforeRange(range, "\\");
}
}
]
});
}
};
Заполнители в сообщениях рекомендаций
В сообщении рекомендации также можно использовать заполнители. Это работает так же, как использование заполнителей для общей ошибки (см. раздел Использование заполнителей в сообщениях).
Обратите внимание, что для объекта рекомендации необходимо указать data. В сообщениях рекомендаций нельзя использовать свойства из data общей ошибки.
module.exports = {
meta: {
messages: {
unnecessaryEscape: "Unnecessary escape character: \\{{character}}.",
removeEscape: "Remove `\\` before {{character}}.",
},
hasSuggestions: true
},
create: function(context) {
// ...
context.report({
node: node,
messageId: "unnecessaryEscape",
data: { character }, // data for the unnecessaryEscape overall message
suggest: [
{
messageId: "removeEscape",
data: { character }, // data for the removeEscape suggestion message
fix: function(fixer) {
return fixer.removeRange(range);
}
}
]
});
}
};
Доступ к параметрам, переданным правилу
Для правильной работы некоторым правилам требуются параметры. Эти параметры указываются в конфигурации (.eslintrc, интерфейсе командной строки или комментариях). Например:
{
"quotes": ["error", "double"]
}
У правила quotes в этом примере есть один параметр — "double" (error — это уровень ошибки). Получить параметры правила можно с помощью context.options — массива, содержащего все настроенные параметры правила. В этом случае context.options[0] будет содержать "double":
module.exports = {
meta: {
schema: [
{
enum: ["single", "double", "backtick"],
},
],
},
create: function (context) {
var isDouble = context.options[0] === "double";
// ...
},
};
Поскольку context.options — это обычный массив, его можно использовать для определения количества переданных параметров, а также для получения самих параметров. Учтите, что уровень ошибки не входит в context.options, поскольку его нельзя узнать или изменить изнутри правила.
Используя параметры, предусмотрите разумные значения по умолчанию на случай, если параметры не переданы.
Для правил с параметрами необходимо указать схему.
Доступ к исходному коду
Объект SourceCode — основной объект для получения дополнительной информации об анализируемом исходном коде. Получить объект SourceCode можно в любой момент с помощью свойства context.sourceCode:
module.exports = {
create: function (context) {
var sourceCode = context.sourceCode;
// ...
},
};
Получив экземпляр SourceCode, вы можете использовать следующие методы для работы с кодом:
-
getText(node): Возвращает исходный код указанного узла. Не указывайтеnode, чтобы получить весь исходный код (см. соответствующий раздел). -
getAllComments(): Возвращает массив всех комментариев в исходном коде (см. соответствующий раздел). -
getCommentsBefore(nodeOrToken): Возвращает массив токенов комментариев, расположенных непосредственно перед указанным узлом или токеном (см. соответствующий раздел). -
getCommentsAfter(nodeOrToken): Возвращает массив токенов комментариев, расположенных непосредственно после указанного узла или токена (см. соответствующий раздел). -
getCommentsInside(node): Возвращает массив всех токенов комментариев внутри указанного узла (см. соответствующий раздел). -
isSpaceBetween(nodeOrToken, nodeOrToken): Возвращает true, если между двумя токенами есть пробельный символ или, если переданы узлы, между последним токеном первого узла и первым токеном второго узла. -
isGlobalReference(node): Возвращает true, если идентификатор ссылается на глобальную переменную, настроенную черезlanguageOptions.globals, комментарии/* global */илиecmaVersion, и не объявлен локальной привязкой. -
getFirstToken(node, skipOptions): Возвращает первый токен, представляющий указанный узел. -
getFirstTokens(node, countOptions): Возвращает первыеcountтокенов, представляющих указанный узел. -
getLastToken(node, skipOptions): Возвращает последний токен, представляющий указанный узел. -
getLastTokens(node, countOptions): Возвращает последниеcountтокенов, представляющих указанный узел. -
getTokenAfter(nodeOrToken, skipOptions): Возвращает первый токен после указанного узла или токена. -
getTokensAfter(nodeOrToken, countOptions): Возвращаетcountтокенов после указанного узла или токена. -
getTokenBefore(nodeOrToken, skipOptions): Возвращает первый токен перед указанным узлом или токеном. -
getTokensBefore(nodeOrToken, countOptions): Возвращаетcountтокенов перед указанным узлом или токеном. -
getFirstTokenBetween(nodeOrToken1, nodeOrToken2, skipOptions): Возвращает первый токен между двумя узлами или токенами. -
getFirstTokensBetween(nodeOrToken1, nodeOrToken2, countOptions): Возвращает первыеcountтокенов между двумя узлами или токенами. -
getLastTokenBetween(nodeOrToken1, nodeOrToken2, skipOptions): Возвращает последний токен между двумя узлами или токенами. -
getLastTokensBetween(nodeOrToken1, nodeOrToken2, countOptions): Возвращает последниеcountтокенов между двумя узлами или токенами. -
getTokens(node): Возвращает все токены указанного узла. -
getTokensBetween(nodeOrToken1, nodeOrToken2): Возвращает все токены между двумя узлами. -
getTokenByRangeStart(index, rangeOptions): Возвращает токен, диапазон которого начинается с указанного индекса в исходном коде. -
getNodeByRangeIndex(index): Возвращает самый глубокий узел AST, содержащий указанный индекс исходного кода. -
getLocFromIndex(index): Возвращает объект со свойствамиlineиcolumn, соответствующими позиции указанного индекса исходного кода.lineнумеруется с 1, аcolumn— с 0. -
getIndexFromLoc(loc): Возвращает индекс указанной позиции в исходном коде, гдеloc— объект с ключомline, нумеруемым с 1, и ключомcolumn, нумеруемым с 0. -
commentsExistBetween(nodeOrToken1, nodeOrToken2): Возвращаетtrue, если между двумя узлами есть комментарии. -
getAncestors(node): Возвращает массив предков указанного узла, начиная с корня AST и заканчивая его непосредственным родителем. Сам указанный узел в массив не входит. -
getDeclaredVariables(node): Возвращает список переменных, объявленных указанным узлом. Эту информацию можно использовать для отслеживания ссылок на переменные.- Если узел — это
VariableDeclaration, возвращаются все переменные, объявленные в объявлении. - Если узел — это
VariableDeclarator, возвращаются все переменные, объявленные в декларативном элементе. - Если узел — это
FunctionDeclarationилиFunctionExpression, возвращается переменная имени функции, а также переменные параметров функции. - Если узел — это
ArrowFunctionExpression, возвращаются переменные параметров. - Если узел — это
ClassDeclarationилиClassExpression, возвращается переменная имени класса. - Если узел — это
CatchClause, возвращается переменная исключения. - Если узел — это
ImportDeclaration, возвращаются переменные всех его спецификаторов. - Если узел — это
ImportSpecifier,ImportDefaultSpecifierилиImportNamespaceSpecifier, возвращается объявленная переменная. - В противном случае, если узел не объявляет переменных, возвращается пустой массив.
- Если узел — это
-
getScope(node): Возвращает область видимости указанного узла. Эту информацию можно использовать для отслеживания ссылок на переменные. -
markVariableAsUsed(name, refNode): Помечает переменную с указанным именем как используемую в области видимости, определяемой указанным узлом-ссылкой. Это влияет на правило no-unused-vars. Возвращаетtrue, если переменная с указанным именем найдена и помечена как используемая, в противном случае возвращаетfalse.
skipOptions — это объект с 3 свойствами: skip, includeComments и filter. По умолчанию — {skip: 0, includeComments: false, filter: null}.
-
skip: (number) Положительное целое число — количество пропускаемых токенов. Если одновременно задан параметрfilter, отфильтрованные токены не учитываются как пропущенные. -
includeComments: (boolean) Флаг, указывающий, нужно ли включать токены комментариев в результат. -
filter(token): Функция, получающая токен в качестве первого аргумента. Если функция возвращаетfalse, токен исключается из результата.
countOptions — это объект с 3 свойствами: count, includeComments и filter. По умолчанию — {count: 0, includeComments: false, filter: null}.
-
count: (number) Положительное целое число — максимальное количество возвращаемых токенов. -
includeComments: (boolean) Флаг, указывающий, нужно ли включать токены комментариев в результат. -
filter(token): Функция, получающая токен в качестве первого аргумента. Если функция возвращаетfalse, токен исключается из результата.
rangeOptions — это объект с одним свойством — includeComments. По умолчанию — {includeComments: false}.
-
includeComments: (boolean) Флаг, указывающий, нужно ли включать токены комментариев в результат.
Также доступны следующие свойства:
-
hasBOM: (boolean) Флаг, указывающий, содержит ли исходный код BOM Unicode. -
text: (string) Полный текст проверяемого кода. BOM Unicode удалён из этого текста. -
ast: (object) УзелProgramAST проверяемого кода. -
scopeManager: объект ScopeManager кода. -
visitorKeys: (object) Ключи посетителей для обхода этого AST. -
parserServices: (object) Содержит предоставленные парсером службы для правил. Парсер по умолчанию не предоставляет никаких служб. Однако если предполагается, что правило будет использоваться с пользовательским парсером, оно может использоватьparserServicesдля доступа к любым данным, предоставляемым этим парсером. (Например, парсер TypeScript может предоставлять возможность получать вычисленный тип заданного узла.) -
lines: (array) Массив строк, разделённых в соответствии с определением переводов строк в спецификации.
Используйте объект SourceCode всякий раз, когда требуется получить дополнительную информацию о проверяемом коде.
Доступ к исходному тексту
Если для работы правилу требуется исходный код JavaScript, используйте метод sourceCode.getText(). Этот метод работает следующим образом:
// get all source
var source = sourceCode.getText();
// get source for just this AST node
var nodeSource = sourceCode.getText(node);
// get source for AST node plus previous two characters
var nodeSourceWithPrev = sourceCode.getText(node, 2);
// get source for AST node plus following two characters
var nodeSourceWithFollowing = sourceCode.getText(node, 0, 2);
Так можно искать шаблоны непосредственно в тексте JavaScript, когда AST не предоставляет нужные данные (например, расположение запятых, точек с запятой, скобок и т. д.).
Доступ к комментариям
Хотя комментарии технически не являются частью AST, ESLint предоставляет методы sourceCode.getAllComments(), sourceCode.getCommentsBefore(), sourceCode.getCommentsAfter() и sourceCode.getCommentsInside() для доступа к ним.
Методы sourceCode.getCommentsBefore(), sourceCode.getCommentsAfter() и sourceCode.getCommentsInside() полезны для правил, которым необходимо проверять комментарии относительно заданного узла или токена.
Имейте в виду, что результаты этих методов вычисляются по запросу.
Вы также можете получать доступ к комментариям с помощью многих методов sourceCode, используя параметр includeComments.
Схемы параметров
Правила с параметрами должны указывать свойство meta.schema — описание параметров правила в формате JSON Schema. ESLint использует его для проверки параметров конфигурации и предотвращения передачи правилу в context.options недопустимых или неожиданных значений.
Если у правила есть параметры, настоятельно рекомендуется указать схему для их проверки. Однако проверку параметров можно отключить, задав schema: false, но это не рекомендуется, поскольку увеличивает вероятность ошибок и недочётов.
Если правило не задаёт свойство meta.schema, ESLint выдаёт ошибки при передаче любых параметров. Если у правила нет параметров, не задавайте schema: false, а просто опустите свойство схемы или используйте schema: [] — оба варианта запрещают передачу параметров.
Проверка конфигурации правила включает пять этапов:
- Если конфигурация правила не является массивом, значение оборачивается в массив (например,
"off"становится["off"]); если конфигурация правила является массивом, она используется напрямую. - ESLint проверяет первый элемент массива конфигурации правила как уровень важности (
"off","warn","error",0,1,2). - Если уровень важности равен
offили0, правило отключается и проверка завершается; остальные элементы массива конфигурации правила игнорируются. - Если правило включено, все элементы массива после уровня важности копируются в массив
context.options(например, конфигурация["warn", "never", { someOption: 5 }]даёт результатcontext.options = ["never", { someOption: 5 }]). - Проверка схемы правила выполняется для массива
context.options.
Примечание: это означает, что схема правила не может проверять уровень важности. Схема правила проверяет только элементы массива, расположенные после уровня важности в конфигурации правила. Правило не может узнать, с каким уровнем важности оно настроено.
Для schema правила предусмотрено два формата:
- Массив объектов JSON Schema
- Каждый элемент проверяется по соответствующей позиции в массиве
context.options. - Если в массиве
context.optionsэлементов меньше, чем схем, несопоставленные схемы игнорируются. - Если в массиве
context.optionsэлементов больше, чем схем, проверка завершается неудачно. - У использования этого формата есть два важных следствия:
- Пользователь всегда может не передавать параметры правилу (помимо уровня важности).
- Если указать пустой массив, передача пользователем любых параметров правилу (помимо уровня важности) всегда приведёт к ошибке.
- Каждый элемент проверяется по соответствующей позиции в массиве
- Полный объект JSON Schema, проверяющий массив
context.options- Схема должна предполагать наличие массива параметров для проверки, даже если правило принимает только один параметр.
- Схема может иметь произвольную сложность, поэтому с помощью
oneOf,anyOfи т. д. можно проверять совершенно разные наборы возможных параметров. - Поддерживается версия JSON Schema Draft-04, поэтому некоторые более новые возможности, например
ifили$data, недоступны.- В настоящее время явно планируется не обновлять поддержку схем до версии выше этой из-за проблем совместимости экосистемы. Дополнительный контекст см. в этом комментарии.
Например, правило yoda принимает в качестве аргумента основной режим "always" или "never", а также дополнительный объект параметров с необязательным свойством exceptRange:
// Valid configuration:
// "yoda": "warn"
// "yoda": ["error"]
// "yoda": ["error", "always"]
// "yoda": ["error", "never", { "exceptRange": true }]
// Invalid configuration:
// "yoda": ["warn", "never", { "exceptRange": true }, 5]
// "yoda": ["error", { "exceptRange": true }, "never"]
module.exports = {
meta: {
schema: [
{
enum: ["always", "never"],
},
{
type: "object",
properties: {
exceptRange: { type: "boolean" },
},
additionalProperties: false,
},
],
},
};
А вот эквивалентная схема на основе объекта:
// Valid configuration:
// "yoda": "warn"
// "yoda": ["error"]
// "yoda": ["error", "always"]
// "yoda": ["error", "never", { "exceptRange": true }]
// Invalid configuration:
// "yoda": ["warn", "never", { "exceptRange": true }, 5]
// "yoda": ["error", { "exceptRange": true }, "never"]
module.exports = {
meta: {
schema: {
type: "array",
minItems: 0,
maxItems: 2,
items: [
{
enum: ["always", "never"],
},
{
type: "object",
properties: {
exceptRange: { type: "boolean" },
},
additionalProperties: false,
},
],
},
},
};
Схемы объектов позволяют точнее и строже определять допустимые значения. Например, приведённая ниже схема всегда требует указать первый параметр (число от 0 до 10), а второй параметр необязателен и может быть либо объектом с явно заданными параметрами, либо "off" или "strict".
// Valid configuration:
// "someRule": ["error", 6]
// "someRule": ["error", 5, "strict"]
// "someRule": ["warn", 10, { someNonOptionalProperty: true }]
// Invalid configuration:
// "someRule": "warn"
// "someRule": ["error"]
// "someRule": ["warn", 15]
// "someRule": ["warn", 7, { }]
// "someRule": ["error", 3, "on"]
// "someRule": ["warn", 7, { someOtherProperty: 5 }]
// "someRule": ["warn", 7, { someNonOptionalProperty: false, someOtherProperty: 5 }]
module.exports = {
meta: {
schema: {
type: "array",
minItems: 1, // Can't specify only severity!
maxItems: 2,
items: [
{
type: "number",
minimum: 0,
maximum: 10,
},
{
anyOf: [
{
type: "object",
properties: {
someNonOptionalProperty: { type: "boolean" },
},
required: ["someNonOptionalProperty"],
additionalProperties: false,
},
{
enum: ["off", "strict"],
},
],
},
],
},
},
};
Помните: параметры правила всегда представляют собой массив, поэтому не указывайте на верхнем уровне схему для типа, отличного от массива. Если схема не задаёт массив на верхнем уровне, пользователи никогда не смогут включить правило, поскольку при включённом правиле их конфигурация всегда будет недействительной.
Вот пример схемы, которая всегда приводит к ошибке проверки:
// Possibly trying to validate ["error", { someOptionalProperty: true }]
// but when the rule is enabled, config will always fail validation because the options are an array which doesn't match "object"
module.exports = {
meta: {
schema: {
type: "object",
properties: {
someOptionalProperty: {
type: "boolean",
},
},
additionalProperties: false,
},
},
};
Примечание: если в схеме правила используются свойства JSON Schema $ref, необходимо использовать полный объект JSON Schema, а не массив позиционных схем свойств. Это связано с тем, что ESLint преобразует сокращённую запись массива в единую схему, не обновляя ссылки, из-за чего они становятся неверными (и игнорируются).
Чтобы узнать больше о JSON Schema, рекомендуем посмотреть примеры на сайте JSON Schema или прочитать бесплатную электронную книгу «Понимание JSON Schema».
Значения параметров по умолчанию
Правила могут задавать массив meta.defaultOptions со значениями по умолчанию для любых параметров. Когда правило включается в пользовательской конфигурации, ESLint рекурсивно объединяет предоставленные пользователем элементы параметров со значениями по умолчанию.
Например, если заданы следующие значения по умолчанию:
export default {
meta: {
defaultOptions: [
{
alias: "basic",
},
],
schema: [
{
type: "object",
properties: {
alias: {
type: "string",
},
},
additionalProperties: false,
},
],
},
create(context) {
const [{ alias }] = context.options;
return {
/* ... */
};
},
};
Во время выполнения значение alias правила будет равно "basic", если только в пользовательской конфигурации не указано другое значение, например ["error", { alias: "complex" }].
Каждый элемент массива параметров объединяется по следующим правилам:
- Отсутствующее значение или явно заданное пользователем значение
undefinedприводит к использованию параметра по умолчанию. - Массивы и примитивные значения, предоставленные пользователем, за исключением
undefined, заменяют параметр по умолчанию. - Объекты, предоставленные пользователем, объединяются с объектом параметра по умолчанию; если значение по умолчанию не является объектом, оно заменяется.
Значения параметров по умолчанию также проверяются по meta.schema правила.
Примечание: для проверки схем ESLint использует библиотеку Ajv с включённым параметром useDefaults. И пользовательские параметры, и параметры meta.defaultOptions переопределяют все значения по умолчанию, заданные в схеме правила. В будущей основной версии ESLint может отключить useDefaults Ajv.
Доступ к шебангам
Шебанги (#!) представлены уникальными токенами типа "Shebang". Они рассматриваются как комментарии, и к ним можно обращаться методами, описанными в разделе Доступ к комментариям, например sourceCode.getAllComments().
Доступ к областям видимости переменных
Метод SourceCode#getScope(node) возвращает область видимости заданного узла. Этот метод полезен для получения информации о переменных в заданной области видимости и об их использовании в других областях видимости.
Типы областей видимости
В следующей таблице перечислены типы узлов AST и соответствующие им типы областей видимости. Дополнительную информацию о типах областей видимости см. в документации по объекту Scope.
| Тип узла AST | Тип области видимости |
|---|---|
Program | global |
FunctionDeclaration | function |
FunctionExpression | function |
ArrowFunctionExpression | function |
ClassDeclaration | class |
ClassExpression | class |
StaticBlock | class-static-block |
BlockStatement ※1 | block |
SwitchStatement ※1 | switch |
ForStatement ※2 | for |
ForInStatement ※2 | for |
ForOfStatement ※2 | for |
WithStatement | with |
CatchClause | catch |
| другие | ※3 ※4 |
※1 Только если настроенный парсер поддерживает блочную область видимости. Парсер по умолчанию поддерживает блочную область видимости, если languageOptions.ecmaVersion не меньше 6.
※2 Только если инструкция for определяет переменную итерации как переменную с блочной областью видимости (например, for (let i = 0;;) {}).
※3 Область видимости ближайшего родительского узла, имеющего собственную область видимости. Если у ближайшего родительского узла несколько областей видимости, выбирается самая внутренняя (например, узел Program имеет область видимости global и область видимости module, если Program#sourceType равно "module". Самой внутренней является область видимости module.).
※4 У каждого узла PropertyDefinition#value (это может быть узел любого типа выражения) есть область видимости class-field-initializer. Например, в class C { field = 1 } узел Literal, представляющий 1, имеет область видимости class-field-initializer. Если у узла есть другие области видимости, область видимости class-field-initializer будет самой внешней. Например, в class C { field = () => {} } узел ArrowFunctionExpression имеет две области видимости: class-field-initializer и function.
Переменные области видимости
Свойство Scope#variables содержит массив объектов Variable. Это переменные, объявленные в текущей области видимости. С помощью этих объектов Variable можно отслеживать ссылки на переменную во всём модуле.
У каждого объекта Variable свойство Variable#references содержит массив объектов Reference. Массив Reference содержит все места, где на переменную ссылаются в исходном коде модуля.
Кроме того, у каждого объекта Variable свойство Variable#defs содержит массив объектов Definition. С помощью Definitions можно найти место, где была определена переменная.
У глобальных переменных есть следующие дополнительные свойства:
-
Variable#writeable(boolean | undefined) … Если значение равноtrue, этой глобальной переменной можно присвоить произвольное значение. Если значение равноfalse, глобальная переменная доступна только для чтения. -
Variable#eslintExplicitGlobal(boolean | undefined) … Если значение равноtrue, эта глобальная переменная определена директивным комментарием/* globals */в файле исходного кода. -
Variable#eslintExplicitGlobalComments(Comment[] | undefined) … Массив директивных комментариев/* globals */, определивших эту глобальную переменную в файле исходного кода. Если директивных комментариев/* globals */нет, это свойство имеет значениеundefined. -
Variable#eslintImplicitGlobalSetting("readonly" | "writable" | undefined) … Настроенное значение из файлов конфигурации. Оно может отличаться отvariable.writeable, если присутствуют директивные комментарии/* globals */.
Примеры использования SourceCode#getScope() для отслеживания переменных см. в исходном коде следующих встроенных правил:
-
no-shadow: вызывает
sourceCode.getScope()для узлаProgramи проверяет все дочерние области видимости, чтобы убедиться, что имя переменной не используется повторно во вложенной области. (Документация правила no-shadow) -
no-redeclare: вызывает
sourceCode.getScope()для каждой области видимости, чтобы убедиться, что переменная не объявлена дважды в одной и той же области. (Документация правила no-redeclare)
Отметка переменных как использованных
Некоторые правила ESLint, например no-unused-vars, проверяют, использовалась ли переменная. ESLint распознаёт только стандартные способы доступа к переменным, поэтому нестандартные способы обращения к ним могут не считаться использованием.
Для этого можно использовать метод sourceCode.markVariableAsUsed(). Он принимает два аргумента: имя переменной, которую нужно отметить как использованную, и необязательный узел-ссылку, указывающий область видимости, с которой вы работаете. Пример:
module.exports = {
create: function (context) {
var sourceCode = context.sourceCode;
return {
ReturnStatement(node) {
// look in the scope of the function for myCustomVar and mark as used
sourceCode.markVariableAsUsed("myCustomVar", node);
// or: look in the global scope for myCustomVar and mark as used
sourceCode.markVariableAsUsed("myCustomVar");
},
};
// ...
},
};
В этом примере переменная myCustomVar отмечается как использованная относительно узла ReturnStatement, то есть ESLint начнёт поиск с ближайшей к этому узлу области видимости. Если опустить второй аргумент, будет использоваться область видимости верхнего уровня. (Для файлов ESM областью видимости верхнего уровня является область модуля; для файлов CommonJS — первая область видимости функции.)
Доступ к путям выполнения кода
ESLint анализирует пути выполнения кода при обходе AST. Доступ к объектам путей выполнения кода можно получить с помощью семи событий, связанных с этими путями. Дополнительную информацию см. в разделе Анализ путей выполнения кода.
Модульные тесты правил
ESLint предоставляет утилиту RuleTester, упрощающую написание тестов для правил.
Соглашения об именовании правил
Пользовательское правило можно назвать как угодно, однако для основных правил действуют соглашения об именовании. Возможно, будет удобнее применять эти же соглашения к своим пользовательским правилам. Дополнительную информацию см. в документации «Соглашения об именовании основных правил».
Правила времени выполнения
Возможность определять пользовательские правила во время выполнения — это то, что отличает ESLint от других линтеров. Это идеально подходит для правил, специфичных для вашего проекта или компании, которые нет смысла включать в ESLint или плагин. Просто напишите правила и подключите их во время выполнения.
Правила времени выполнения записываются в том же формате, что и все остальные правила. Создайте правило обычным способом, а затем выполните следующие действия:
- Поместите все правила времени выполнения в один каталог (например,
eslint_rules). - Создайте файл конфигурации и укажите уровень ошибки для идентификатора правила в ключе
rules. Правило будет выполняться, только если в файле конфигурации задано значение"warn"или"error". - Запустите интерфейс командной строки, используя параметр
--rulesdirдля указания расположения правил времени выполнения.
Профилирование производительности правил
В ESLint встроен метод отслеживания производительности отдельных правил. Если задать переменную среды TIMING, после завершения проверки будут показаны десять самых медленных правил, время выполнения каждого из них (создание правила + выполнение правила) и относительное влияние на производительность в процентах от общего времени обработки правил (создание правил + выполнение правил).
$ TIMING=1 eslint lib
Rule | Time (ms) | Relative
:-----------------------|----------:|--------:
no-multi-spaces | 52.472 | 6.1%
camelcase | 48.684 | 5.7%
no-irregular-whitespace | 43.847 | 5.1%
valid-jsdoc | 40.346 | 4.7%
handle-callback-err | 39.153 | 4.6%
space-infix-ops | 35.444 | 4.1%
no-undefined | 25.693 | 3.0%
no-shadow | 22.759 | 2.7%
no-empty-class | 21.976 | 2.6%
semi | 19.359 | 2.3%
Чтобы проверить одно конкретное правило, объедините параметры --no-config-lookup и --rule:
$ TIMING=1 eslint --no-config-lookup --rule "quotes: [2, 'double']" lib
Rule | Time (ms) | Relative
:------|----------:|--------:
quotes | 18.066 | 100.0%
Чтобы просмотреть более длинный список результатов (более 10), задайте переменной среды другое значение, например TIMING=50 или TIMING=all.
Для получения более подробной информации о времени выполнения (для каждого файла и правила) используйте параметр stats.
© OpenJS Foundation and other contributors
Licensed under the MIT License.
https://eslint.org/docs/latest/extend/custom-rules