На код-ревью зачастую тратят слишком много времени и энергии. Перфекционизм часто заставляет размениваться на мелочи вместо сути, а холивары разрушают команды. Хотя есть специальные инструменты, задача которых автоматизировать процесс и убрать лишние споры, это часто не работает. Ведь всё взаимодействие с ESLint сводится к extend’у от популярных конфигурации типа airbnb. Проблема в том, что эти конфигурации не покрывают даже малую часть того, что на самом деле может ESLint.
Чтобы исправить ситуацию, нужно собрать мощный, оптимизированный, а главное, реюзабельный ESLint.
Это статья написана на основе доклада Дениса Красновского для FronendConf 2022. Денис, руководитель направления разработки и фронтэнд-лид в компании Домклик, в телеграм @jquery_dly_slabih.
Экскурс в историю ESLint
ESLint — это ПО, которое проводит статический анализ кода, чтобы найти баги, неиспользуемый код, излишнюю логику, ошибки синтаксиса, а также расхождения в code style.
Изначально Lint это ПО, которое проводило статический анализ кода для языка С. В свою очередь, статический анализ кода — это анализ программы без её реального выполнения. Но мы называем линтинг линтингом потому, что в начале XXI века этот термин стал нарицательным для всех программ такого типа, а именно статических анализаторов. Уже в 2002 Дуглас Крокфорд явил миру первый инструмент для линтинга JavaScript под названием JSLint, своего рода self opinionated инструмент, без возможности настройки под свои вкусовые предпочтения.
Спустя много лет в 2011 году Антон Ковалев сделал форк от JSLint и назвал его JSHint. Он дал разработчикам возможность конфигурировать его под свои потребности. Одним из контрибьюторов JS Hint был Николас Закас. В 2013 году, Николас сделал новый инструмент для линтинга под названием ESLint. Ключевой мотивацией была необходимость писать свои кастомные правила и использовать их в конфигурациях. Ещё говорят, что изначально его называли JSCheck, но уже через месяц переименовали в ESLint.
Как собрать сильный Config
Только на первом этапе конфигурирования нужно потратить время на чтение документации, настройку и тестирование порядка 260 правил. Трудно назвать это лёгкой прогулкой. Для начала нам потребуется проект с настроенным Lint, будет здорово если этот проект очень большой, так будет легче отловить все граничные случаи. Если же в проекте уже используется популярный extend от условного airbnb, то стоит распечатать текущий конфиг. Но зачем? На это есть две причины. Возможно некоторые настройки вы захотите перенести и сэкономить тонну времени. А вторая причина кроется в том, что некоторые правила в ESLint описаны недостаточно очевидно, и было бы неплохо увидеть понятный пример как настроить правило.
Распечатать конфиг можно с помощью команды --print-config, пример: eslint --print-config .eslintrc.json > result.json
eslintrc.json ваш конфигурационный файл ESLint, а result.json файлик который нужно создать и заполнить подкапотной конфигурацией.
В результате из конфига на следующей картинке, мы получим читабельный принт.
Теперь можно смело перейти к настройке eslint.
Сперва обязательно читаем документацию, чтобы принять решение, нужно ли правило. Если не нужно, его можно выключить и пропустить. Если нужно, настраиваем, тестируем по примеру и повторяем, пока не закончатся все 260 правил.
В результате получим только базовую конфигурацию, а это лишь малая часть того, что можно сделать. Ведь основной мотивацией к появлению ESLint было желание добавлять свои кастомные правила.
Опенсорс-сообщество начало пользоваться этим, и появилось много крутых плагинов. Найти их можно на странице Awesome ESLint на GitHub. В первую очередь, стоит использовать те плагины, с которыми вы уже работали и знаете их потенциал.
Например, если вы использовали eslint-config-airbnb, то, скорее всего, знаете, что под капотом есть такие плагины, как react, jsx-a11y и import. Но если вы ещё не погружались в данную тематику, то далее я расскажу какие плагины стоит установить в первую очередь, по моему скромному мнению.
Перечисленные ниже плагины касаются стека, с которым работает Домклик. Если он не совпадает с вашим, на Awesome ESLint вы найдете аналоги и для Angular, Vue и так далее.
eslint-plugin-react (специфичен для react)
eslint-plugin-jsx-a11y (специфичен для react)
typescript-eslint (специфичен для typescript)
eslint-plugin-jest (специфичен для jest)
eslint-plugin-jest-dom (специфичен для jest)
eslint-plugin-testing-library (специфичен для testing-library)
Про пользу каждого из этих правил можно почитать перейдя на них по ссылке.
Новые плагины настраиваются точно по такой же логике, как и базовые правила eslint. А именно:
Важно не просто взять двадцать плагинов и бездумно закинуть их в конфигурации, а сделать это осознанно.
Оптимизация скорости
Чем больше плагинов мы устанавливаем, тем больше правил в конфиге появляется, а для ESLint это означает, что работать придётся больше и дольше, а разработчику и пользователям придется — ждать. Что с этим делать?
Можем например найти самые медленные правила, сделать это можно с помощью команды: TIMING=1 npm run lint. Так получим топ-10 правил, которые работают медленнее всего. Если хотим посмотреть всё, то можно заменить 1 на all. Если видите что какое-то правило или даже несколько работают аномально долго по сравнению с другими правилами, то можно уже начинать уже технические изыски.
Алгоритм предельно прост:
Обновить библиотеку до последней версии. Возможно, это баг, который уже исправлен.
Если установлена последняя версия библиотеки, то нужно проверить issue на Github. Часто бывает, что в библиотеке нет проблемы, зато есть в локальном окружении у разработчика или билд-машине.
Отключить правило.
Использовать поле overrides. По глобпаттерну нужно настроить либо список правил, либо список плагинов на определённый шаблон. Так вы снизите количество анализируемых файлов без реальной необходимости. Пример на картинке ниже.
overrides: [
{
files: '**/?(*.)+(spec|test).[jt]s?(x)',
extends: [
'plugin: jest-dom/recommended' ,
'plugin: jest/all'
],
rules: {
/* turn off or enable and configure rules */
'jest/no-hooks': 'off',
'jest/prefer-expect-assertions': ['error', { onlyFunctionsWithAsyncKeyword: true }],
'max-lines': 'off'
/* and so on till the end... */
}
}
]
ESLint VS Prettier
Есть популярное мнение, что использовать ESLint не нужно, ведь есть Prettier. Соглашусь лишь в том, что если настраивать ESLint спустя рукава, он не сильно будет отличаться от Prettier и действительно смысла будет немного. Но в действительности ESLint нужен, а Prettier не является заменой, они прекрасно могут дополнить друг друга.
Достатки Prettier
Скорость. Он действительно быстрый и если вы правильно интегрируете специальный плагин Prettier с ESLint, то получите оптимизацию к скорости проверки кода, приблизительно 10-15%. Для больших проектов это имеет смысл.
Многогранность. Киллер-фича Prettier в том, что он правит не только js(x) и ts(x) файлы, но и многие другие.
Недостатки Prettier
По аналогии с JSLint, это такой же self opinionated инструмент. Если у вас аллергия на код-стайл, который предлагает Prettier, то, не стоит его использовать.
В целом ESLint тоже отвечает за код-стайл, и по команде fix делает исправления. Все это можно настроить и в ci-pipeline и в вашей IDE.
Как сделать конфиг реюзабельным
Вариант «копипастить» Config от проекта к проекту, не рассматриваем, ведь это не масштабируемое решение. Конечно же мы будем делать npm-модуль.
"name": "eslint-config-domclick",
"extends": "domclick",
"name": "@domclick/eslint-config",
"extends": "@domclick",
Есть два аспекта, специфичных для ESLint.
Конвенция имен. Нужны префиксы либо постфиксы как на картинке выше, и когда разработчик будет устанавливать его, он сможет в своем .eslintrc-файле написать extends domclick. Для скоупа модулей всё то же самое, только добавляется @.
Не нужно никаких сборок. Это понижает порог входа для заворачивания вашего eslint-config’а в npm модуль. Конфиг будет использоваться в dev-среде, поэтому его можно не минифицировать, что упрощает работу.
Пример можно посмотреть на github.
Тестируем npm
В силу своей модульности и изолированности тестировать npm-модули не очень удобно. Но решение конечно же есть. Можно использовать например npm link или аналог в yarn. Лично я пользуюсь yalc. По причинам того, что в свою бытность, нативные команды link работали посредственно.
Порядок работы:
используем команду yalc publish в консоли вашего npm-модуля
переходим в проект где будем тестировать npm-модуль
используем команду yalc add + название вашего npm-модуля которое написано у него в package.json
пишем там же npm i
готово, приступаем к тестированию
Если что-то не работает или хочется протестировать что-то ещё, возвращаемся в npm-модуль и меняем код, а далее:
используем команду yalc publish в консоли вашего npm-модуля
переходим в проект где будем тестировать npm-модуль
используем команду yalc update
в случае если менялись зависимости, используем npm i
продолжаем тестировать
Удалить перелинковку зависимости после проведения теста так же легко, в проекте, где происходило тестирование используем команду yalc remove --all.
Интегрируем наш конфиг в старые проекты
Настанет тот момент, когда готовый eslint-config после локального тестирования на каком-нибудь эталонном проекте, понадобится завести на реальный проект с legacy. Это будет выглядеть примерно так:
Объем работы может быть довольно большим, а ещё можно нарваться на мердж-конфликты с коллегами. Ниже предоставляю планы «капканы», как этого избежать:
для команды до пяти человек со средним проектом
для команды с 5+ людьми, где проект очень большой
Маленькая команда. Первое, что стоит сделать — договориться с коллегами о codefreeze. Если такой вариант не подходит, придется встать пораньше, либо уйти с работы позже. Создайте новую ветку и используйте в ней команду fix. Таким образом вы избавитесь от большинства ошибок. Что сделать с оставшимися? Если есть возможность быстро пофиксить - то просто сделай это, если нет - то отключи временно правило, не забыв создать себе задачи в таск-трекере. И в рамках технических задач включайте правила и правьте ошибки.
Большая команда. В этом случае тоже придется договариваться с коллегами о codefreeze. Делаем команду fix, но не отключаем правила, а переводим их в warning. Это не помешает поставке фич на production, ведь обычно CI настраивают так, чтобы reject сборки отрабатывал только на ошибках. Но вернемся к нашим warning. К сожалению, нужно будет перенести всю настройку правила, чтобы сделать этот подход корректным. Перенос мы можем осуществить или используя исходники вашего eslint-конфига, или же командной «print-config» о которой уже слышали ранее. Делаем pull request и договариваемся на уровне команды о том, что выполняя задачу в каком-нибудь файле и видя что рядышком лежит warning на какое-то правило, необходимо не пройти мимо, а поправить файлик. Рано или поздно все ошибки уйдет и можно будет удалить все переопределение из локального файла конфигурации eslint.
В завершение
Выключение правил в любой непонятной ситуации — плохое решение, вы просто теряете всю прелесть автоматизированных решений которые будут экономить вам тонну времени. Лучше почитайте документацию по правилу и поймите чего от вас хочет ESLint, в крайнем случае перенастройте правило под свои нужды и в перспективе сохраните много времени и нервов.
cakkypamucm
Смотрел другую версию этого доклада, которая больше понравилась.
Вот, что есть в том видео и нет в статье по этой версии:
для eslint-plugin-import нужно сразу задавать resolver, иначе боль с путями
полезный плагин eslint-plugin-unicorn упоминается, хоть и не применяется
В обеих версиях:
упоминается прирост на проценты после распараллеливания, но не говорится, как можно легко ускориться на порядок через
--cache
ожидал, но не увидел упоминания очень полезных встроенных no-restricted-imports и no-restricted-syntax (+связанных с этим eslint selectors)
было бы здорово добавить информацию про flat config и грядущую перепись с нуля
Популярное среди не прошедших собес в Домклик?) Или кто там еще не отличает linter от formatter?
От себя отмечу полезный для аспектов сортировки eslint-plugin-perfectionist
jquery_dlya_slabih
Бесконечно с вами согласен, но есть нюанс, доклад был с уклоном на CI (если я не смог донести эту мысль, то извините), по этой причине такие опции как кэш, гит-хуки не упоминались. В целом, если правильно настроить IDE, то запускать локально линтинг не придется, тогда и кэш не будет нужен.