ESLint — отличный инструмент для поощрения лучших практик в кодовой базе JS/TS. Он превращает то, что иногда существует только в документе на слиянии, в нечто, что можно автоматически проверять при каждом изменении.

Автоматизация этих проверок означает, что рецензенты или соавторы могут сосредоточиться на вопросах более высокого уровня, а не на комментариях, сосредоточенных на том, чтобы указать на одну и ту же проблему, которая повторялась много раз или была пропущена в предыдущих коммитах.

ESLint поставляется с отличным набором встроенных правил и несколькими отличными библиотеками сообщества (подробнее о настройке этого для проекта мы расскажем в следующем посте), но где-то они должны подводить черту. Некоторые кодовые базы или компании могут иметь более специфические потребности или предпочтения. Например, для поддержки перехода от вызова конкретного библиотечного метода или отказа от подхода, который имеет известные проблемы.

ESLint можно расширить двумя способами — с помощью правил «без ограничений» или путем создания собственного плагина.

Настройка «неограниченных» правил

Есть 3 встроенных правила, которые мы можем использовать для настройки пользовательских ограничений:

  • https://eslint.org/docs/rules/no-restricted-syntax — ограничить использование определенных элементов синтаксиса (например, вызовы методов).
  • https://eslint.org/docs/rules/no-restricted-globals — ограничить использование определенных глобальных переменных. Это может быть полезно, если есть глобальные переменные, определенные для гидратации приложения, но к этому глобальному состоянию не следует обращаться напрямую за пределами этого варианта использования.
  • https://eslint.org/docs/rules/no-restricted-properties — ограничить использование именованного свойства на свойстве с определенным именем. Полезно, например, если мы хотим ограничить использование screen.getByTestId.

Эти правила упрощают расширение ESLint без необходимости реализации реального плагина.

Пример

Чтобы продемонстрировать это, давайте рассмотрим пример.

Просматривая изменения кода, мы заметили несколько примеров следующего подхода:

async function someFunction() {
  const result = await asyncFunction()
    .then(x => { /* do something */});
  return result;
}

У нас в команде был разговор о том, почему не идеально смешивать цепочки обещаний с синтаксисом async/await в одном вызове, подобном этому, поэтому мы бы предпочли, чтобы тот или другой использовался в конкретном блоке кода. В идеале, однако, мы бы автоматизировали эту проверку, чтобы в будущем await и then не использовались вместе.

Мы можем сделать это с помощью правила no-restricted-syntax. Однако сначала нам нужно понять, какой синтаксис должен быть ограничен.

Ограниченный синтаксис может принимать селектор абстрактного синтаксического дерева (AST). AST представляют собой представление структуры кода и используются различными инструментами статического анализа и генераторами кода. https://www.twilio.com/blog/abstract-syntax-trees — хорошее введение в AST, если вы хотите узнать больше. В документах ESLint также есть некоторые примеры использования селекторов AST для ограничения синтаксиса.

Откуда вы знаете, какой селектор AST использовать для вашего варианта использования? Что ж, первый шаг, который я предлагаю, — это взять фрагмент кода, синтаксис которого нужно ограничить, и скопировать его в https://astexplorer.net/. Этот инструмент анализирует и отображает AST для предоставленного вами кода. Если вы нажмете на конкретный вызов метода или параметр, вы увидите, что этот конкретный узел в дереве будет выделен, как показано ниже.

Выбрав ключевое слово await и увидев, что сфокусировано в AST, мы можем увидеть, что AwaitExpression — это имя для типа узла, который мы ищем. Но мы не хотим ограничивать использование всех вызовов await, поэтому нам нужно выяснить, какие свойства этого узла использовать, чтобы сделать наш селектор более конкретным.

Подобно селекторам атрибутов CSS, мы можем специально искать AwaitExpression узлов, которые имеют определенное значение вложенного свойства. Если мы расширим свойства этого узла, мы в конце концов найдем то, что ищем — имя свойства тогда. Если мы используем ключи для создания пути к ключевому слову тогда, мы можем объединить приведенный ниже селектор атрибутов AST:

AwaitExpression[argument.callee.property.name="then"]

Чтобы настроить правило no-restricted-syntax для генерации ошибки с помощью нашего селектора с более полезным сообщением об ошибке, мы можем добавить следующее в раздел правил нашего .eslintrc (или где-либо еще, где хранится конфигурация ESLint):

"no-restricted-syntax": [
  "error",
  {
    "message": "promise.then is unnecessary when using async/await",
    "selector": "AwaitExpression[argument.callee.property.name=\" then\"]"
  }
]

(Обратите внимание, что кавычки вокруг "тогда" экранированы).

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

Пользовательский плагин

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

Посетите https://eslint.org/docs/developer-guide/working-with-plugins#create-a-plugin для получения информации о том, как начать работу, а также https://dev.to/spukas/ как написать свой первый плагин eslint-145, в котором рассказывается о создании простого плагина.

Вот и все! В следующий раз, когда вы увидите что-то, что, по вашему мнению, было бы хорошо автоматически проверять в проекте, над которым вы работаете, подумайте, как это можно сделать с помощью правила ESLint.

Дальнейшее чтение

https://www.twilio.com/blog/abstract-syntax-trees https://eslint.org/docs/developer-guide/selectors https://dev.to/spukas/how-to -write-your-first-eslint-plugin-145 https://astexplorer.net/