Spec-Zone.ru › ESLint

Настраиваемые правила

Вы можете создавать настраиваемые правила для использования с 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.
  • docs: (object) Свойства, часто используемые для генерации документации и инструментов. Обязательны для стандартных правил и необязательны для настраиваемых правил. Настраиваемые правила могут включать дополнительные свойства по мере необходимости.

    • description: (string) Предоставляет краткое описание правила. Для стандартных правил это используется в списке правил.
    • recommended: (boolean) Для стандартных правил это определяет, включено ли правило конфигурацией recommended из @eslint/js.
    • url: (string) Указывает URL, где можно получить полную документацию. Редакторы кода часто используют это для предоставления полезной ссылки на выявленные нарушения правил.
  • fixable: (string) Либо "code" либо "whitespace" , если опция --fix на командной строке автоматически исправляет проблемы, обнаруженные правилом.

    Важно: свойство fixable обязательно для исправляемых правил. Если это свойство не указано, ESLint будет генерировать ошибку всякий раз, когда правило пытается произвести исправление. Опустите свойство fixable если правило не исправляемое.

  • hasSuggestions: (boolean) Указывает, могут ли правила возвращать предложения (по умолчанию false если не указано).

    Важно: свойство hasSuggestions обязательно для правил, которые предлагают исправления. Если это свойство не установлено в true, ESLint будет генерировать ошибку всякий раз, когда правило пытается предложить исправление. Опустите свойство hasSuggestions если правило не предлагает исправления.

  • schema: (object | array | false) Указывает опции, чтобы ESLint мог предотвратить неверные конфигурации правил. Обязательно, когда у правила есть опции.

  • defaultOptions: (array) Указывает значения по умолчанию для опций правила. Если указаны, все опции, предоставленные пользователем в конфигурации, будут объединены поверх них рекурсивно.

  • deprecated: (boolean) Указывает, устарело ли правило. Можно опустить свойство deprecated если правило не устарело.

  • replacedBy: (array) В случае устаревшего правила, укажите заменяющее(ие) правило(а).

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
            }
        };
    }
};
Подсказка

Вы можете просмотреть полное AST для любого кода JavaScript с помощью Code Explorer.

Объект контекста

Объект 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 , переданная в линтер. Это путь к каталогу, который следует рассматривать как текущую рабочую директорию.
  • options: (array) Массив настроенных опций для этого правила. Этот массив не включает степень серьезности правила (см. специальный раздел).
  • sourceCode: (object) Объект SourceCode, который вы можете использовать для работы с исходным кодом, переданным в ESLint (см. Доступ к исходному коду).
  • settings: (object) Общие настройки из конфигурации.
  • languageOptions: (object) Более подробные сведения о каждом свойстве здесь
    • sourceType: ('script' | 'module' | 'commonjs') Режим для текущего файла.
    • ecmaVersion: (number) Версия ECMAScript, используемая для разбора текущего файла.
    • parser: (object): Парсер, используемый для разбора текущего файла.
    • parserOptions: (object) Параметры парсера, настроенные для этого файла.
    • globals: (object) Указанные глобальные переменные.
  • parserPath: (string, Удалено Используйте context.languageOptions.parser вместо этого.) Имя parser из конфигурации.
  • parserOptions: (Устарело Используйте context.languageOptions.parserOptions вместо этого.) Параметры парсера, настроенные для этого выполнения (более подробная информация здесь).

Кроме того, объект context имеет следующие методы:

  • getCwd(): (Устарело: Используйте context.cwd вместо этого.) Возвращает опцию cwd , переданную в линтер. Это путь к каталогу, который следует рассматривать как текущую рабочую директорию.
  • getFilename(): (Устарело: Используйте context.filename вместо этого.) Возвращает имя файла, связанное с исходным кодом.
  • getPhysicalFilename(): (Устарело: Используйте context.physicalFilename вместо этого.) При проверке файла, возвращает полный путь к файлу на диске без информации о блоке кода. При проверке текста, возвращает значение, переданное —stdin-filename или <text> если не указано.
  • getSourceCode(): (Устарело: Используйте context.sourceCode вместо этого.) Возвращает объект SourceCode, который вы можете использовать для работы с исходным кодом, переданным в ESLint (см. Доступ к исходному коду).
  • report(descriptor). Сообщает о проблеме в коде (см. соответствующий раздел).

Примечание: Более ранние версии ESLint поддерживали дополнительные методы в объекте context. Эти методы были удалены в новом формате и не должны использоваться.

Сообщения о проблемах

Основной метод, который вы будете использовать при написании настраиваемых правил, — context.report(), который публикует предупреждение или ошибку (в зависимости от используемой конфигурации). Этот метод принимает один аргумент, который является объектом, содержащим следующие свойства:

  • messageId: (string) Идентификатор сообщения (см. messageIds) (рекомендуется вместо message).
  • message: (string) Сообщение об ошибке (альтернатива messageId).
  • node: (необязательно object) Узел AST, связанный с проблемой. Если указан и loc не указан, то начальная позиция узла используется в качестве позиции проблемы.
  • loc: (необязательно object) Указывает позицию проблемы. Если указаны как loc, так и node, тогда позиция берется из loc, а не из node.
    • 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
    }
});

Обратите внимание, что ведущие и хвостовые пробелы в параметрах сообщений необязательны.

Узел содержит всю необходимую информацию для определения строки и столбца проблемного текста, а также исходный текст, представляющий узел.

messageId

messageId — рекомендуемый подход к сообщению об ошибках в вызовах 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 не должны перекрываться.

Рекомендации по исправлению:

  1. Избегайте исправлений, которые могут изменить поведение кода во время выполнения и привести к его остановке.
  2. Делайте исправления как можно меньше. Необоснованно большие исправления могут конфликтовать с другими исправлениями и предотвратить их применение.
  3. Делайте только одно исправление на сообщение. Это ограничение связано с тем, что вы должны вернуть результат операции исправления из fix().
  4. Поскольку все правила выполняются снова после первоначального цикла применения исправлений, правилу не нужно проверять, вызовет ли стиль исправления ошибку, которая будет сообщена другим правилом.
    • Например, предположим, что корректор хочет заключить ключ объекта в кавычки, но не уверен, одинарные или двойные кавычки предпочтительнее.

      ({ foo : 1 })
      
      // should get fixed to either
      
      ({ 'foo': 1 })
      
      // or
      
      ({ "foo": 1 })
      
    • Этот корректор может просто произвольно выбрать тип кавычек.

Примечание: Сокращение исправлений — это лучшая практика, но в некоторых случаях может быть правильным расширить диапазон исправления, чтобы намеренно предотвратить применение других правил к окружающему диапазону в одном проходе. Например, если текст замены объявляет новую переменную, может быть полезно предотвратить другие изменения в области действия переменной, так как они могут привести к конфликтам имён.

Следующий пример заменяет 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 выдаст ошибку, если правило попытается сгенерировать предложение, но не экспортирует это свойство.

Примечание: Предложения применяются как самостоятельные изменения без запуска исправлений с несколькими проходами. Каждое предложение должно фокусироваться на единственном изменении в коде и не должно пытаться соответствовать стилям, определенным пользователем. Например, если предложение добавляет новую инструкцию в базу кода, оно не должно пытаться соответствовать правильному отступу или соответствовать пользовательским предпочтениям по наличию/отсутствию точек с запятой. Все это может быть исправлено автоматическим исправление с несколькими проходами, когда пользователь запускает его.

Рекомендации по предложениям:

  1. Не пытайтесь сделать слишком много и предлагать большие рефакторинги, которые могут привести к многочисленным изменениям.
  2. Как указано выше, не пытайтесь соответствовать стилям, определенным пользователем.

Предложения предназначены для предоставления исправлений. ESLint автоматически удалит всё предложение из вывода проверки, если функция fix предложения вернула null или пустой массив/последовательность.

Предложения messageId

Вместо использования ключа desc для предложений можно использовать messageId. Это работает так же, как и messageId для общей ошибки (см. 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;

        // ...
    }
};

Устаревшее: метод context.getSourceCode() устарел; используйте свойство context.sourceCode вместо него.

После получения экземпляра SourceCode, вы можете использовать следующие методы для работы с кодом:

  • getText(node): Возвращает исходный код для данного узла. Опустите node для получения всего исходного текста (см. посвящённый раздел).
  • getAllComments(): Возвращает массив всех комментариев в исходном тексте (см. посвящённый раздел).
  • getCommentsBefore(nodeOrToken): Возвращает массив маркеров комментариев, которые встречаются непосредственно перед заданным узлом или маркером (см. посвящённый раздел).
  • getCommentsAfter(nodeOrToken): Возвращает массив маркеров комментариев, которые встречаются непосредственно после заданного узла или маркера (см. посвящённый раздел).
  • getCommentsInside(node): Возвращает массив всех маркеров комментариев внутри данного узла (см. посвящённый раздел).
  • isSpaceBetween(nodeOrToken, nodeOrToken): Возвращает true, если между двумя маркерами или, если задан узел, между последним маркером первого узла и первым маркером второго узла есть пробел.
  • 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 — объект с 1-основанным ключом line и 0-основанным ключом column.
  • 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 — это объект, который имеет 1 свойство, includeComments. По умолчанию {includeComments: false}.

  • includeComments: (boolean) Флаг включения маркеров комментариев в результат.

Также доступны некоторые свойства:

  • hasBOM: (boolean) Флаг, указывающий, содержит ли исходный код BOM Unicode.
  • text: (string) Полный текст проверяемого кода. BOM Unicode был удален из этого текста.
  • ast: (object) Узел Program AST для проверяемого кода.
  • 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 в формате JSON Schema опций правила. Это используется ESLint для проверки конфигурационных опций и предотвращения неверных или непредвиденных вводов, прежде чем они будут переданы правилу в context.options.

Если у вашей правила есть опции, настоятельно рекомендуется указать схему для проверки валидности опций. Однако можно отказаться от проверки опций, установив schema: false, но это не рекомендуется, так как это увеличивает вероятность ошибок и проблем.

Для правил, которые не указывают свойство meta.schema, ESLint выдает ошибки, когда передаются какие-либо опции. Если у вашего правила нет опций, не устанавливайте schema: false, а просто опустите свойство schema или используйте schema: [], что предотвращает передачу любых опций.

При проверке конфигурации правила выполняется пять шагов:

  1. Если конфигурация правила не является массивом, то значение оборачивается в массив (например, "off" становится ["off"]); если конфигурация правила является массивом, она используется напрямую.
  2. ESLint проверяет первый элемент массива конфигурации правила как уровень серьезности ("off", "warn", "error", 0, 1, 2).
  3. Если уровень серьезности off или 0, то правило отключено, и проверка останавливается, игнорируя любые другие элементы массива конфигурации правила.
  4. Если правило включено, то все элементы массива после уровня серьезности копируются в массив context.options (например, конфигурация ["warn", "never", { someOption: 5 }] приводит к context.options = ["never", { someOption: 5 }]).
  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 или прочитать бесплатный электронный учебник Understanding 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 во время выполнения, если пользователь не укажет другое значение, например, с помощью ["error", { alias: "complex" }].

Каждый элемент массива опций объединяется в соответствии со следующими правилами:

  • Любое отсутствующее значение или явно заданное пользователем undefined будет возвращаться к значению по умолчанию.
  • Массивы и примитивные значения, кроме undefined, переданные пользователем, будут переопределять значение по умолчанию.
  • Объекты, переданные пользователем, будут сливаться с объектом по умолчанию и заменят его, если он не является объектом.

Значения по умолчанию опций также будут проверены на соответствие схеме meta.schema правила.

Примечание: ESLint в своей работе использует Ajv для проверки схем со включенной опцией useDefaults. Как значения, переданные пользователем, так и meta.defaultOptions значения опций переопределят любые значения по умолчанию, заданные в схеме правила. В будущей основной версии ESLint может отключить useDefaults Ajv.

Доступ к Shebang

Shebang (#!) представлены уникальными маркерами типа "Shebang". Они обрабатываются как комментарии и могут быть обработаны методами, описанными в разделе Доступ к комментариям, например, sourceCode.getAllComments().

Доступ к областям видимости переменных

Метод SourceCode#getScope(node) возвращает область видимости заданного узла. Это полезный метод для получения информации о переменных в данной области видимости и о том, как они используются в других областях видимости.

Подсказка

Вы можете просмотреть информацию о области видимости любого кода JavaScript, используя Code Explorer.

Типы областей видимости

В следующей таблице представлен список типов узлов AST и соответствующих типов областей видимости. Для получения дополнительной информации о типах областей видимости обратитесь к документации объекта Scope.

Тип узла AST Тип области видимости
Program global
FunctionDeclaration function
FunctionExpression function
ArrowFunctionExpression function
ClassDeclaration class
ClassExpression class
BlockStatement ※1 block
SwitchStatement ※1 switch
ForStatement ※2 for
ForInStatement ※2 for
ForOfStatement ※2 for
WithStatement with
CatchClause catch
другие ※3

※1 Только если конфигурированный парсер предоставил функцию области видимости блока. По умолчанию парсер предоставляет функцию области видимости блока, если parserOptions.ecmaVersion не меньше 6.
※2 Только если оператор for определяет переменную итерации как переменную с областью видимости блока (например, for (let i = 0;;) {}).
※3 Область видимости ближайшего родительского узла, имеющего собственную область видимости. Если у ближайшего родительского узла несколько областей видимости, выбирается внутренняя область видимости (например, узел Program имеет область видимости global и область видимости module если Program#sourceType равно "module"). Внутренней областью видимости является область видимости module.)

Переменные области видимости

Свойство Scope#variables содержит массив объектов Variable. Это переменные, объявленные в текущей области видимости. Вы можете использовать эти объекты Variable для отслеживания ссылок на переменную во всем модуле.

Внутри каждого объекта Variable, свойство Variable#references содержит массив объектов Reference. Массив Reference содержит все места, где переменная ссылается в исходном коде модуля.

END_OF_DOCUMENT_MARKER

Также внутри каждого Variable, свойство Variable#defs содержит массив объектов Definition. Вы можете использовать Definitions для определения места определения переменной.

Глобальные переменные имеют следующие дополнительные свойства:

  • Variable#writeable (boolean | undefined) … Если true, этой глобальной переменной можно присвоить произвольное значение. Если false, эта глобальная переменная является только для чтения.
  • Variable#eslintExplicitGlobal (boolean | undefined) … Если true, эта глобальная переменная была определена директивой комментария /* globals */ в файле исходного кода.
  • Variable#eslintExplicitGlobalComments (Comment[] | undefined) … Массив директивы комментария /* globals */, которые определяли эту глобальную переменную в файле исходного кода. Это свойство undefined если нет комментариев директивы /* globals */.
  • 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. Вы можете получить доступ к объектам пути кода с помощью семи событий, связанных с путями кода. Для получения дополнительной информации обратитесь к Анализу путей кода.

Устаревшие SourceCode методы

Обратите внимание, что следующие SourceCode методы устарели и будут удалены в будущей версии ESLint:

  • getTokenOrCommentBefore(): Заменён на SourceCode#getTokenBefore() с опцией { includeComments: true }.
  • getTokenOrCommentAfter(): Заменён на SourceCode#getTokenAfter() с опцией { includeComments: true }.
  • isSpaceBetweenTokens(): Заменён на SourceCode#isSpaceBetween()
  • getJSDocComment()

Тесты правил

ESLint предоставляет утилиту RuleTester для упрощения написания тестов для правил.

Конвенции именования правил

Вы можете присвоить своему пользовательскому правилу любое имя, но ядро правил использует конвенции именования. Может быть проще применить эти же конвенции к вашему пользовательскому правилу. Для получения дополнительной информации обратитесь к документации Конвенции именования правил ядра.

Правила выполнения

Отличительной особенностью ESLint от других анализаторов кода является возможность определения пользовательских правил во время выполнения. Это идеально подходит для правил, специфичных для вашего проекта или компании, которые нецелесообразно включать в состав ESLint или плагина.

Правила выполнения имеют тот же формат, что и все другие правила. Создайте правило как обычно, а затем выполните следующие действия:

  1. Поместите все свои правила выполнения в один каталог (например, eslint_rules).
  2. Создайте файл конфигурации конфигурации и укажите уровень ошибки вашего правила ID в ключе rules. Правило не будет выполняться, если в файле конфигурации ему не будет присвоено значение "warn" или "error".
  3. Запустите интерфейс командной строки с опцией --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-eslintrc, и --rule:

$ TIMING=1 eslint --no-eslintrc --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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API