Рефакторинг за допомогою codemods для автоматизації змін API

Codemods

Уявіть, що вам потрібно здійснити масштабну зміну API у великому проєкті: перейменувати методи, змінити назви параметрів чи навіть перевпорядкувати аргументи у сотнях файлів. Зробити все вручну вкрай важко і ризиковано. Тут на допомогу приходять codemods — спеціальні скрипти, які можуть автоматизувати рефакторинг, використовуючи абстрактне синтаксичне дерево (AST). У цій статті поговоримо, навіщо потрібні codemods, як вони працюють і як вони допомагають у рефакторингу API.

Що таке codemods

Codemod — це скрипт, який аналізує вихідний код на рівні синтаксичного дерева, а потім перетворює його за певними правилами. В англомовній спільноті це часто називають AST transform, бо основна робота виконується через розбір коду в AST, внесення змін і генерацію коду назад.

Основні цілі codemods:

  • Масштабний рефакторинг: заміна застарілого API, зміна назв змінних чи функцій.
  • Міграція з однієї бібліотеки на іншу: автоматичне переписування імпортів і викликів.
  • Усунення технічного боргу: оновлення коду відповідно до нових правил ESLint або стилю.

Навіщо потрібні codemods при зміні API

Коли велика бібліотека чи фреймворк випускає нову версію з оновленим API, розробникам часто потрібно:

  1. Змінити сигнатури функцій
  2. Перейменувати об’єкти чи методи
  3. Переписати імпорти (наприклад, @angular/common/http замість @angular/http)
  4. Видалити застарілий (deprecated) код

Зробити це вручну у великому кодовому базисі майже нереально. Codemods дають змогу виконати зміни автоматично за допомогою одного скрипта.

Приклад інструментів

jscodeshift

jscodeshift — це популярна утиліта від Facebook (Meta), яка використовує Babel або recast для роботи з кодом. Вона написана на Node.js і може швидко обходити всі файли у вашому проєкті.

Встановлення:

npm install -g jscodeshift

Приклад запуску:

jscodeshift -t renameFunction.js src/

Де renameFunction.js — це ваш скрипт-трансформатор.

Babel transformations

Babel теж дає можливість писати власні трансформери (плагіни), але jscodeshift вважають більш спеціалізованим рішенням для codemods.

TS-morph або Typescript Compiler API

Якщо вам треба більш типобезпечний підхід або якщо код на TypeScript, можна використати ts-morph чи офіційний TypeScript Compiler API.

Приклад: перейменування методу в API

Припустімо, що у вас є функція getUser() в десятках файлів, яку потрібно перейменувати на fetchUser(). За допомогою jscodeshift це можна робити так:

  1. Створити файл renameFunction.js:
module.exports = function(file, api) {
  const j = api.jscodeshift
  const root = j(file.source)

  // Шукаємо всі виклики getUser(...)
  root.find(j.CallExpression, {
    callee: {
      type: 'Identifier',
      name: 'getUser'
    }
  })
  .forEach(path => {
    // Замінюємо назву функції на fetchUser
    path.value.callee.name = 'fetchUser'
  })

  return root.toSource()
}
  1. Запустити:
jscodeshift -t renameFunction.js src/

Скрипт пройдеться по всіх файлах у папці src/ і замінить виклики getUser(...) на fetchUser(...).

Приклад: міграція іменованих імпортів

Уявімо, що раніше ми імпортували метод parseData з import { parseData } from './utils', а тепер він називається processData.

  1. Скрипт migrateImports.js:
module.exports = function(file, api) {
  const j = api.jscodeshift
  const root = j(file.source)

  // Знаходимо імпорт з './utils' і замінюємо parseData на processData
  root.find(j.ImportDeclaration, { source: { value: './utils' } })
    .forEach(path => {
      path.value.specifiers.forEach(specifier => {
        if (specifier.imported.name === 'parseData') {
          specifier.imported.name = 'processData'
        }
      })
    })

  return root.toSource()
}
  1. Запуск:
jscodeshift -t migrateImports.js src/

Поради для успішного використання codemods

  • Створіть резервну копію коду або використовуйте систему контролю версій, щоб завжди можна було відкотити зміни.
  • Пиши маленькі, інкрементальні скрипти: краще кілька маленьких трансформерів, ніж один великий, що робить все.
  • Тестуйте скрипт на невеликій ділянці коду перед тим, як застосовувати до всього проєкту.
  • Використовуйте типи (для TypeScript) якщо це можливо, щоб більш точно знаходити потрібні місця.
  • Підключайте linter і форматер (наприклад, ESLint і Prettier) до фінального кроку, щоб отриманий код був читабельним.

Коли codemods не допоможуть

  • Якщо логіка зміни складна і залежить від конкретного контексту, який важко відстежити в коді. Наприклад, якщо функцію викликають з різними аргументами залежно від умов, і ваша трансформація залежить від того, якими саме аргументами.
  • Якщо API має дуже різні сценарії використання, які не вкладаються у шаблони.

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

Висновок

Рефакторинг великого проєкту, в якому потрібно внести глобальні зміни в API, може забрати багато часу, якщо робити його вручну. Codemods дають змогу автоматизувати більшу частину роботи, зменшують ризик людських помилок і роблять процес масштабного оновлення коду більш прозорим.

Завдяки jscodeshift або аналогічним інструментам розробники можуть швидко оновлювати імпорти, перейменовувати функції та навіть змінювати структуру коду за кілька хвилин, а не днів. Головне — проєкт має бути підконтрольний системі версій, а самі скрипти краще писати й тестувати покроково.

Якщо у вашій команді часто змінюється API або ви готуєтеся до великого оновлення фреймворку чи бібліотек, варто розглянути можливості codemods як частини процесу рефакторингу. Це інвестиція, що швидко окупиться у вигляді зекономленого часу і покращення якості коду.