Отладка трансформированного кода

Трансформация исходного JavaScript- или TypeScript-кода является одной из ключевых задач SWC. После прохождения через цепочку преобразований код может существенно отличаться от исходной версии: меняются конструкции языка, добавляются вспомогательные функции, модифицируются импорты, удаляются типы TypeScript, внедряются полифилы и оптимизации.

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


Почему возникают сложности при отладке

Во время трансформации SWC может выполнять множество операций:

  • преобразование современного синтаксиса в более старые стандарты ECMAScript;
  • удаление типов TypeScript;
  • преобразование JSX в вызовы функций;
  • внедрение вспомогательных хелперов;
  • оптимизацию импортов;
  • минификацию;
  • удаление неиспользуемого кода.

Рассмотрим пример.

Исходный код:

class User {
    name = "John";

    greet() {
        console.log(this.name);
    }
}

После трансформации в ES5 результат может выглядеть значительно сложнее:

function User() {
    this.name = "John";
}

User.prototype.greet = function() {
    console.log(this.name);
};

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


Source Maps как основной инструмент отладки

Для решения этой проблемы используются карты исходников (Source Maps).

Source Map представляет собой специальный файл, содержащий информацию о соответствии между:

  • исходным кодом;
  • трансформированным кодом.

Благодаря этому браузер или отладчик способны показывать оригинальный код даже тогда, когда фактически выполняется его преобразованная версия.

Схематически процесс выглядит так:

TypeScript
      │
      ▼
     SWC
      │
      ├── transformed.js
      └── transformed.js.map

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


Включение Source Maps

В конфигурации SWC генерация карт исходников включается через параметр sourceMaps.

Пример:

{
  "jsc": {
    "target": "es2018"
  },
  "sourceMaps": true
}

Также допускается использование строковых значений:

{
  "sourceMaps": "inline"
}

В этом случае карта внедряется непосредственно в итоговый JavaScript-файл.


Внешние и встроенные карты

SWC поддерживает два основных варианта.

Внешняя карта

{
  "sourceMaps": true
}

Результат:

app.js
app.js.map

Преимущества:

  • меньший размер JavaScript-файла;
  • удобство публикации;
  • возможность исключить карты из production-сборки.

Недостаток:

  • требуется отдельный файл.

Inline Source Maps

{
  "sourceMaps": "inline"
}

Результат:

//

Преимущества:

  • удобство локальной разработки;
  • отсутствие дополнительных файлов.

Недостатки:

  • увеличение размера выходного файла;
  • замедление загрузки крупных бандлов.

Проверка корректности генерации карт

После сборки рекомендуется убедиться, что SWC действительно создал карту.

Например:

npx swc src -d dist --source-maps

Структура результата:

dist/
 ├─ index.js
 └─ index.js.map

Дополнительно в конце JavaScript-файла должна присутствовать ссылка:

//# sourceMappingURL=index.js.map

Отсутствие этой строки делает карту недоступной для большинства инструментов отладки.


Отладка в браузере

Наиболее распространённый сценарий связан с фронтенд-разработкой.

Пусть имеется исходный TypeScript:

const getName = (user?: string) => {
    return user ?? "Anonymous";
};

console.log(getName());

После трансформации в старую версию ECMAScript код становится более объёмным.

Если Source Maps настроены правильно, в инструментах разработчика браузера будут отображаться:

  • оригинальный TypeScript;
  • исходные номера строк;
  • реальные имена переменных.

Это позволяет устанавливать точки останова непосредственно в исходном коде.


Использование Breakpoints

Без Source Maps точка останова устанавливается в преобразованном коде:

var getName = function(user) {
    return user !== null && user !== void 0
        ? user
        : "Anonymous";
};

С Source Maps отладчик показывает исходную версию:

const getName = (user?: string) => {
    return user ?? "Anonymous";
};

Отладка становится значительно проще, поскольку разработчик работает с привычным кодом.


Анализ стека вызовов

Одной из наиболее ценных возможностей Source Maps является восстановление корректного stack trace.

Рассмотрим пример.

Исходный код:

function divide(a: number, b: number) {
    if (b === 0) {
        throw new Error("Division by zero");
    }

    return a / b;
}

divide(10, 0);

После трансформации номер строки может измениться.

Без карты исходников стек может выглядеть так:

Error: Division by zero
    at divide (app.js:187:13)

С корректной картой:

Error: Division by zero
    at divide (math.ts:3:15)

Поиск ошибки становится существенно быстрее.


Отладка Node.js-приложений

Node.js также поддерживает работу с Source Maps.

Современные версии платформы позволяют использовать флаг:

node --enable-source-maps app.js

Теперь ошибки будут отображаться относительно оригинальных файлов.

Пример:

TypeError: Cannot read properties of undefined
    at src/services/user.ts:24:18

Вместо:

TypeError: Cannot read properties of undefined
    at dist/services/user.js:173:45

Использование SWC совместно с Node.js

Типичный сценарий:

{
  "jsc": {
    "parser": {
      "syntax": "typescript"
    }
  },
  "sourceMaps": true
}

Сборка:

npx swc src -d dist

Запуск:

node --enable-source-maps dist/index.js

В результате весь стек вызовов будет сопоставляться с TypeScript-файлами.


Отладка JSX и React-компонентов

SWC активно применяется как замена Babel в React-проектах.

Исходный компонент:

export function Button() {
    return (
        <button>
            Click
        </button>
    );
}

После преобразования JSX:

export function Button() {
    return React.createElement(
        "button",
        null,
        "Click"
    );
}

Без Source Maps ошибки будут ссылаться на вызовы React.createElement.

С картами исходников отладчик покажет строку JSX, где возникла проблема.


Анализ трансформаций вручную

Иногда необходимо исследовать результат работы SWC без участия браузера.

Для этого удобно выполнять отдельную трансформацию:

npx swc source.js

Либо:

npx swc source.ts -o output.js

После чего сравнивать:

source.ts
output.js

Подобный подход помогает понять:

  • какие преобразования выполняются;
  • какие конструкции генерируют ошибки;
  • как меняется логика программы.

Отладка пользовательских плагинов SWC

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

Пример:

impl VisitMut for MyTransform {
    fn visit_mut_ident(&mut self, ident: &mut Ident) {
        ident.sym = "test".into();
    }
}

Если трансформация работает некорректно, полезно выводить промежуточные данные AST.

Например:

println!("{:#?}", ident);

Либо:

dbg!(ident);

Такой подход позволяет увидеть структуру узлов до и после изменений.


Проверка AST после трансформации

Для сложных преобразований полезно анализировать дерево разбора.

Схема работы:

Source Code
      │
      ▼
     AST
      │
      ▼
 Transform
      │
      ▼
 New AST
      │
      ▼
 Generated Code

Ошибка может находиться на любом этапе.

Часто код выглядит корректно, однако проблема скрывается именно в модифицированном AST.


Сохранение промежуточных результатов

При сложных цепочках трансформаций полезно сохранять промежуточные версии файлов.

Например:

input.ts
step1.js
step2.js
step3.js
final.js

Это помогает определить этап, на котором появляется ошибка.

Подход особенно полезен при использовании:

  • нескольких SWC-плагинов;
  • собственных трансформаций;
  • интеграции с Webpack;
  • интеграции с Vite;
  • интеграции с Next.js.

Диагностика проблем минификации

После включения минификации ошибки становятся сложнее для анализа.

Исходный код:

const totalPrice = calculatePrice();

Минифицированная версия:

const t=e();

Без карт исходников становится трудно понять:

  • что означает переменная t;
  • какая функция скрывается за e.

Поэтому для production-отладки часто публикуют Source Maps отдельно от клиентского кода.


Проверка сохранения имён функций

Некоторые инструменты анализа используют имена функций для диагностики.

Исходный код:

function processOrder() {
    throw new Error();
}

После агрессивной оптимизации имя может измениться:

function a() {
    throw new Error();
}

В результате стек вызовов становится менее информативным.

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


Типичные проблемы Source Maps

Карта отсутствует

Признаки:

app.js loaded
app.js.map not found

Причины:

  • параметр sourceMaps выключен;
  • карта не скопирована в каталог сборки;
  • карта удалена во время деплоя.

Некорректные номера строк

Признаки:

Ошибка отображается не в той строке

Причины:

  • дополнительная обработка файла после SWC;
  • неправильное объединение карт;
  • изменение файла без пересоздания Source Map.

Старые карты

Часто возникает ситуация:

app.js обновлён
app.js.map остался старым

В результате отладчик начинает показывать неверные позиции.

Решением является полная пересборка проекта.


Использование логирования для диагностики трансформаций

Даже при наличии Source Maps логирование остаётся важным инструментом.

Например:

console.log("before transform");

или

console.log({
    user,
    permissions,
    settings
});

При анализе трансформированного кода полезно сопоставлять:

  • место вывода сообщения;
  • позицию в исходном коде;
  • соответствующую строку в сгенерированном файле.

Это помогает локализовать участок, где трансформация изменила ожидаемое поведение.


Практика исследования проблемных трансформаций

Эффективная стратегия отладки состоит из нескольких последовательных шагов:

  1. Получить исходный код, вызывающий ошибку.
  2. Выполнить трансформацию через SWC.
  3. Сравнить исходную и преобразованную версии.
  4. Проверить корректность Source Maps.
  5. Проанализировать стек вызовов.
  6. Установить точки останова в исходном коде.
  7. При необходимости исследовать AST.
  8. Проверить влияние минификации.
  9. Изолировать конкретную трансформацию, вызывающую проблему.
  10. Подтвердить исправление повторной сборкой и проверкой карт исходников.

Такой подход позволяет эффективно диагностировать ошибки как в обычных проектах на JavaScript и TypeScript, так и при разработке собственных расширений и трансформаций для SWC.