Миграция Puppeteer на TypeScript

Мы в команде DevTools большие поклонники TypeScript — настолько, что новый код в DevTools создается с его помощью, и мы находимся в процессе большой миграции всей кодовой базы на проверку типов с помощью TypeScript. Подробнее об этой миграции вы можете узнать из нашего выступления на Chrome Dev Summit 2020 . Поэтому имело смысл рассмотреть возможность переноса кодовой базы Puppeteer на TypeScript.

Планирование миграции

Планируя миграцию, мы хотели добиться прогресса небольшими шагами . Это снижает накладные расходы на миграцию (вы одновременно работаете только над небольшой частью кода) и снижает риск. Если на одном из шагов что-то пойдет не так, вы можете легко его отменить. У Puppeteer много пользователей, и сломанная версия может вызвать проблемы у многих из них, поэтому было жизненно важно свести риск внесения критических изменений к минимуму.

Нам также повезло, что у Puppeteer есть надежный набор модульных тестов , охватывающий все его функциональные возможности. Это означало, что мы могли быть уверены, что не нарушаем код при миграции, но также и не вносим изменения в наш API. Целью миграции было завершить ее так, чтобы пользователи Puppeteer даже не осознали, что мы перешли, и тесты были жизненно важной частью этой стратегии. Если бы у нас не было хорошего тестового покрытия, мы бы добавили его, прежде чем продолжить миграцию.

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

Одна вещь, на которую мы потратили время заранее, — это настройка непрерывной интеграции (CI) . Мы заметили, что запуски CI против запросов на включение были нестабильными и часто завершались неудачно. Это случалось так часто, что мы привыкли игнорировать наш CI и все равно объединять запросы на включение, предполагая, что сбой был разовой проблемой в CI, а не проблемой в Puppeteer.

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

Выберите и поместите один файл

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

Кроме того, работа файл за файлом (и с регулярными выпусками Puppeteer, поэтому все изменения не входили в одну и ту же версию npm) снижала риск. В качестве первого файла мы выбрали DeviceDescriptors.js , поскольку это был один из самых простых файлов в базе кода. Выполнение всей этой подготовительной работы и внесение такого небольшого изменения может показаться немного неприятным, но цель состоит не в том, чтобы немедленно внести огромные изменения, а в том, чтобы действовать осторожно и методично, файл за файлом. Время, потраченное на проверку подхода, определенно экономит время на последующих этапах миграции, когда вы столкнетесь с более сложными файлами.

Докажите закономерность и повторите

К счастью, изменение в DeviceDescriptors.js успешно вошло в кодовую базу, и план сработал так, как мы надеялись! На этом этапе вы готовы взяться за дело и продолжить, что мы и сделали . Использование метки GitHub — действительно хороший способ сгруппировать все запросы на включение вместе, и мы обнаружили, что это полезно для отслеживания прогресса.

Перенесите его и улучшите позже

Для каждого отдельного файла JavaScript наш процесс был следующим:

  1. Переименуйте файл с .js на .ts .
  2. Запустите компилятор TypeScript.
  3. Исправьте любые проблемы.
  4. Создайте запрос на включение .

Большая часть работы в этих первоначальных запросах на включение заключалась в извлечении интерфейсов TypeScript для существующих структур данных. В случае первого запроса на вытягивание , который переносил DeviceDescriptors.js , который мы обсуждали ранее, код был следующим:

module.exports = [
  { 
    name: 'Pixel 4',
    … // Other fields omitted to save space
  }, 
  …
]

И стал:

interface Device {
  name: string,
  …
}

const devices: Device[] = [{name: 'Pixel 4', …}, …]

module.exports = devices;

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

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

Перенос тестов для проверки наших определений типов

Как только мы перенесем весь исходный код на TypeScript, мы сможем сосредоточиться на тестах . Наши тесты имели широкий охват, но все они были написаны на JavaScript. Это означало, что они не проверяли наши определения типов. Одна из долгосрочных целей проекта ( над которой мы все еще работаем ) — поставлять высококачественные определения типов «из коробки» с Puppeteer, но в нашей кодовой базе не было никаких проверок определений типов.

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

Мы уже получили огромную пользу от TypeScript как инженеры, работающие над кодовой базой Puppeteer. В сочетании с нашей значительно улучшенной средой CI это позволило нам стать более продуктивными при работе над Puppeteer и выявить ошибки TypeScript, которые в противном случае попали бы в выпуск npm. Мы очень рады получить высококачественные определения TypeScript, которые позволят всем разработчикам, использующим Puppeteer, также извлечь выгоду из этой работы.

Загрузите предварительный просмотр каналов

Рассмотрите возможность использования Chrome Canary , Dev или Beta в качестве браузера для разработки по умолчанию. Эти каналы предварительного просмотра дают вам доступ к новейшим функциям DevTools, тестируют передовые API-интерфейсы веб-платформы и находят проблемы на вашем сайте раньше, чем это сделают ваши пользователи!

Связь с командой Chrome DevTools

Используйте следующие параметры, чтобы обсудить новые функции и изменения в публикации или что-либо еще, связанное с DevTools.