Чуть меньше двух лет назад моя команда готовилась к первому рефактору производительности нашей системы проектирования. Наши планы по максимизации прироста производительности основывались на удалении устаревших API, которые медленно накапливались годами и теперь представляли собой значительное количество раздутого и дублирующегося кода. Беспокоит то, что это означало бы беспрецедентное количество ломающих изменений в то время, когда принятие и стабильность были большой болью для наших потребителей. Автоматизированная миграция казалась единственным реальным способом продвижения вперед…
Как и большинство популярных библиотек, например, React, Next.js и другие, которые предоставляют кодмоды для перемещения огромной пользовательской базы между версиями, нам нужна была специальная и довольно простая CLI-обертка jscodeshift, которая обеспечивала бы публикацию, загрузку и запуск кодмодов. Поэтому мы создали собственный ‘codemod-cli’, нашу первую часть внутренней инфраструктуры codemod.
Мы уже написали несколько кодмодов для помощи в некоторых внутренних миграциях. Они были в виде отдельных файлов преобразований, расположенных вместе с пакетами, которые они обслуживали, но были одноразовыми, которые были написаны, запущены и забыты, поскольку работа была сделана и больше не требовалась. Это не похоже на то, что предоставляют React и Next.js, — неструктурированные, но порой нечеткие указания на то, когда и почему следует использовать тот или иной кодмод. Именно здесь мы увидели нашу первую возможность:
Что если каждое изменение будет сопровождаться версионным кодовым модулем?
В этой модели каждое изменение сопровождалось бы кодмодом, а название кодмода указывало бы на версию, на которую он нацелен: button-v2.0.0, чтобы его смысл был понятен пользователю. Более того, когда мы накопим кодмоды для нескольких основных версий, пользователи, отстающие на много версий, смогут переходить на новейший код, запуская последовательно все доступные кодмоды.
С этой парадигмой и функциональностью, реализованной в codemod-cli, мы оказались в точке, где нам нужно было отложить инструменты. Нам нужно было вернуться к проекту, который мы изначально собирались завершить, — повышению производительности. Теперь мы должны были начать тестировать его, написав и опубликовав кодмоды для наших изменений. Но теперь с возможностью реально изменить API, которые сдерживали нас в течение многих лет. Чтобы не затягивать, я пропущу следующий год или около того, просто сказав: Это сработало! Мы написали кучу кодмодов, они работали и, конечно же, делали то, что мы изначально планировали — Ура!
Однако я ушел из этого проекта с большим количеством незавершенных дел, я чувствовал, что в этом пространстве есть еще много неиспользованных возможностей. Поэтому я сделал то, что до меня не делал ни один инженер — я начал побочный проект… 😅. С появлением все большего количества систем проектирования и сближением к многопакетным репозиториям, я чувствую, что сейчас самое время поделиться тем, чем я занимаюсь, в надежде, что это также может помочь людям, находящимся в похожей с нами ситуации.
🚚 CodeshiftCommunity
Прежде всего, если бы проект был ничем иным, он был бы просто сайтом документации, представляющим наши коллективные знания по созданию codemod. Очевидно, что это огромный пробел и барьер для новичков в jscodeshift. Ввод в курс дела довольно пугающий, собранный из различных примеров и постов в блогах.
Во-вторых, стратегия codemods, нацеленных на конкретные версии пакета, показалась мне поразительно похожей на стратегию DefinitelyTyped, версионные артефакты (определения типов ts), поддерживающие пакет на протяжении всего его жизненного цикла. Что, если бы мы могли предоставить аналогичные возможности в качестве единого места, где кодомоды могли бы распространяться, документироваться и поддерживаться контролируемым и структурированным образом?
В-третьих, все, кто использует jscodeshift, включая нас самих, скорее всего, также будут писать/поддерживать специальный CLI для решения этой проблемы. Нам нужно будет связать все вместе в единый и знакомый инструмент CLI.
Наконец, моя главная цель и то, над чем я сейчас работаю, заключается в следующем: Предоставить бота, похожего на renovate, который может не только сбивать версии, но и автоматически переносить код между основными версиями и предлагать сопровождающим либо слиться на зеленом CI, либо дать им четкие указания для вмешательства в случае, если codemod не может перенести их до конца.
Как это работает
Вообще говоря, библиотека работает путем публикации каждого кодмода на npm в виде собственного пакета или на основе существующего пакета NPM. Затем наш CLI может загрузить и запустить их из любого места. Скрытым преимуществом является возможность публиковать кодмоды с зависимостями, что в настоящее время невозможно в ванильных реализациях jscodeshift CLI.
Использование NPM значительно снижает планку внедрения, поскольку все, что нужно сделать, это опубликовать свой существующий пакет в паре с codemod.config.js
, который представляет собой файл, содержащий имена и расположение кодмодов. В существующих пакетах npm простое добавление этого файла — это все, что необходимо для принятия Codeshift, никаких зависимостей не требуется.
Например:
export.module = {
transforms: { // Versioned transforms
'12.0.0': require.resolve('./18.0.0/transform'),
'13.0.0': require.resolve('./19.0.0/transform'),
},
presets: { // Generic utility transforms
'format-imports': require.resolve('./format-imports/transform')
}
};
Запуск приведенного выше codemod теперь является простым вопросом обращения к имени пакета и его версии.
Допустим, этот конфиг существует в пакете @chakra/button
. Если предположить, что конфиг и кодмоды опубликованы в NPM, можно выполнить команду:
$ codeshift -p @chakra/button@12.0.0 path/to/src
Codeshift загрузит последнюю версию @chakra/button
локально, гарантируя, что у нас всегда будут самые актуальные кодовые модули. CLI прочитает конфиг и передаст его в jscodeshift, где произойдет обычное преобразование.
Передача флага --sequence
вызовет запуск v12 и v13, один за другим.
Конфиг предустановок — это место для «общих» или «не зависящих от версии» кодмодов, которые относятся к @chakra/button
. Потенциально здесь можно разместить такие кодовые модули, как format-imports
, которые, например, будут форматировать импорт кнопок в определенном порядке. Запуск одного из них выглядит следующим образом:
$ codeshift -p @chakra/button#format-imports path/to/src
Как вы будете использовать Codeshift, зависит от вас.
Вы можете внести свой вклад в публичный реестр
Дополнить существующие пакеты кодомодами, чтобы сделать их доступными через @codeshift/cli
.
Или создать свой собственный частный реестр codemod, используя ту же документацию, лучшие практики и структурированный API, при этом полностью совместимый с сообществом.
Более подробную информацию можно найти в руководстве по созданию.
Что дальше?
Главная цель — снизить барьер для входа, чтобы экосистема JS в целом могла использовать эти ресурсы и сообщество и, в свою очередь, генерировать покрытие codemod для библиотек, от которых мы зависим, точно так же, как мы используем определения типов из DefinitelyTyped. Идея заключается в том, что разработчики в конечном итоге смогут использовать codemods, которые сообщество коллективно предоставляет, упрощая миграцию для основных зависимостей (react, redux, next, emotion, jest и т.д.). Это высокая цель, но подумайте, если бы существующие кодовые модули экосистемы были интегрированы с библиотекой.
Единственным барьером для них будет предоставление конфигурационного файла и использование @codeshift/cli
, что может быть безопасно сделано вместе с существующей инфраструктурой. После публикации в NPM наши инструменты сборки и потребители могут запускать эти кодмоды из любого места.
В конечном итоге, что более важно, консолидация усилий в этой области в структурированную библиотеку служит более широкой экосистеме JS.
Если вы заинтересовались или хотите узнать больше, пожалуйста, ознакомьтесь с документацией: CodeshiftCommunity.