Оригинал доступен по ссылке humanwhocodes.com

История о том, как один баг на работе породил подключаемый линтер.

Если вы похожи на меня, то, вероятно, вы каждый день используете кучу инструментов с открытым исходным кодом, не задумываясь о том, откуда они взялись. Немного найдется проектов, которые делятся опытом относительно своих «почему» в плане собственного создания: животрепещущей проблемой, которую они пытались решить, а также тем, когда они впервые столкнулись с данной проблемой. Несомненно, вы можете извлечь пользу из проектов с открытым исходным кодом, не разбираясь в истории их происхождения, но мне всегда интересно узнать, как все начиналось.

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

Баг

Я все еще был относительно новичком в Box, когда коллега работал над необычным багом. Клиент сообщил о проблемах в использовании веб-приложения в Internet Explorer 7 (вероятно, мы были одной из последних компаний, поддерживавших тогда IE7). Разработчик, очевидно, использовал в каком-то коде на JavaScript встроенный объект XMLHttpRequest вместо нашей собственной функции-обертки. Это не вызывало затруднения у какого-либо другого браузера; проблем с внутренним тестированием IE7 тоже не было. Сложности возникли из-за того, что у клиента имелась внутренняя политика безопасности, которая отключила ActiveX в Internet Explorer, а так как в IE7 встроенный объект XMLHttpRequest являлся попросту функцией-оберткой для объекта ActiveX, то он также оказался заблокирован.

Решение было достаточно простым: следовало просто позаботиться, чтобы все использовали собственную функцию-оболочку Ajax вместо встроенного объекта XMLHttpRequest. Но как мы могли бы такое осуществить? Оказалось, что у Box есть «линтер» для JavaScript в виде части системы сборки. Я поместил слово «линтер» в кавычки, так как по факту это была просто серия регулярных выражений, которые работали с кодом JavaScript. В данном случае мой коллега добавил регулярное выражение для «XMLHttpRequest» – таковым было решение. Сборка прервалась бы, если бы кто-то попытался зафиксировать файл JavaScript, соответствующий данному варианту.

Исходя из моего опыта, использование регулярных выражений в исходном коде никогда не приводило ни к чему хорошему. Хотелось бы, чтобы для проведения подобных проверок в ходе сборки существовал способ получше. Я подумал, что кто-то уже наверняка решил данный вопрос, так что я приступил к поиску решений.

Мог ли это быть JSHint?

Первое что я сделал – отправил электронное письмо тогдашнему администратору JSHint Антону Ковалеву[1]. Я вспомнил, как читал пост в блоге[2], где речь шла о том, что JSHint планирует поддерживать плагины, но не смог найти никакой информации о том, что это было реализовано. Из прошлого опыта содействия JSHint и внесения изменений в JSLint для проекта в Yahoo я знал, что JSHint с самого начала не был настроен для поддержки плагинов, а без соответствующей поддержки не существовало бы простого способа изменить JSHint для того, чтобы он совершал то, что хотел я.

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

Вдохновение

Поковырявшись в системе сборки в Box, я обнаружил, что в придачу к самодельному линтеру для JavaScript, там вообще-то работает еще и линтер для PHP. Правда, линтер для PHP был намного запутаннее, чем линтер JavaScript. Вместо использования регулярных выражений линтер для PHP парсил код в абстрактное синтаксическое дерево (AST), а затем проверял AST, чтобы сообщить о наличии шаблонов.

Весьма вероятно, что я кивал головой «да», пока читал тот код. Я моментально понял, что это именно то, что было необходимо сделать мне для JavaScript. Если бы только был какой-либо способ пропарсить код JavaScript в AST, а затем проверить AST на наличие проблем.

Подготовительная работа

Со всем тем, что творилось у меня в голове, я пригласил Ария Хидаят[3] сделать доклад в Box на тему, которая ему нравилась. Так сложилось, что он выступил с докладом об Esprima[4] – парсере JavaScript, который он написал на JavaScript. В процессе этого доклада Ария затронул вопрос, почему полезно располагать кодом JavaScript в AST, и сослался на несколько уже существующих инструментов, созданных на основе Esprima. Среди указанных инструментов имелись estraverse для обхода AST и escope для анализа области видимости, которые написал Юсуке Сузуки.

Пока Ария продолжал рассказывать и приводить примеры задач, которые может решить AST, в моей голове образовалась идея нового инструмента. Я видел смысл в том, чтобы существовал один инструмент, который был бы в состоянии выполнять все виды анализа, каковые указал Ария. В конце концов, они все используют AST, но просто с разной целью. Почему бы не располагать одним AST, который смогут использовать они все?

Начало

В немалой степени благодаря доступности Esprima, estraverse и escope я смог собрать первый прототип ESLint в течение пары уикендов. Для меня эти три утилиты соответствовали всему, что мне было необходимо для создания нового инструмента, который мог бы с легкостью находить проблемные шаблоны в коде JavaScript. Если бы мне довелось создавать их с нуля, то я не сомневаюсь, что ESLint сегодня бы не существовало. Отталкиваясь от указанных инструментов, я смог быстро выполнить итерацию, и в один прекрасный момент появился инструмент, известный вам сегодня как ESLint.

(По-моему важно отметить, что я тогда был не единственным, кто стремился создать линтер на основе AST. Где-то в это же время разрабатывался JSCS[5], а текущий специалист по обслуживанию ESLint – Илья Володин – работал над собственным проектом еще до разработки ESLint. Если бы я не придумал что-то типа ESLint, то, без сомнений, кто-то другой это бы сделал. Благодаря Ария и Юсуке все пазлы уже имелись в наличии и кому-то просто надо было собрать их наиболее эффективным способом).

Ссылки

  1. Антон Ковалев (antonkovalyov.com)
  2. Планы JSHint 3 (jshint.com)
  3. Ария Хидаят (ariya.io)
  4. Анализ кода JavaScript (speakerdeck.com)
  5. JSCS (jscs.info)