При трансформации, минификации или компиляции JavaScript- и TypeScript-кода исходные файлы изменяются. В результате код, который выполняется в браузере или среде выполнения, может существенно отличаться от оригинала. Это осложняет отладку, поскольку сообщения об ошибках, точки останова и стек вызовов начинают ссылаться на сгенерированные файлы.
Source maps решают эту проблему, сохраняя соответствие между исходным и результирующим кодом. Инструменты разработчика используют карты исходников для восстановления оригинальной структуры проекта.
SWC поддерживает несколько вариантов генерации source maps:
Выбор режима влияет на размер файлов, удобство отладки, скорость развертывания и организацию сборки.
В конфигурации SWC настройка выполняется через свойство
sourceMaps.
Пример:
{
"sourceMaps": true
}
В зависимости от значения SWC генерирует карты исходников различным способом.
Поддерживаются следующие варианты:
{
"sourceMaps": true
}
или
{
"sourceMaps": "inline"
}
или
{
"sourceMaps": "both"
}
В некоторых версиях инструментов и обёрток над SWC значение
true соответствует генерации внешней карты.
Режим external создаёт отдельный файл карты исходников рядом со скомпилированным файлом.
Исходный файл:
const message: string = "Hello";
console.log(message);
После компиляции:
//
const message = "Hello"; console.log(message);Дополнительно создаётся файл:
app.js.mapСтруктура проекта:
dist/ ├── app.js └── app.js.map
Содержимое карты
Файл карты представляет собой JSON-документ.
Пример упрощённой структуры:
{ "version": 3, "sources": ["src/app.ts"], "names": [], "mappings": "...", "file": "app.js" }Основные поля:
| Поле | Назначение |
|---|---|
| version | Версия формата source map |
| sources | Список исходных файлов |
| names | Используемые идентификаторы |
| mappings | Таблица соответствий |
| file | Имя результирующего файла |
Файл .swcrc:
{
"sourceMaps": true
}
или
{
"sourceMaps": "external"
}
Если используется программный API:
const { transform } = require("@swc/core");
const result = await transform(code, {
sourceMaps: true
});
SWC вернёт объект:
{
code: "...",
map: "..."
}
Полученную карту можно сохранить отдельно.
В карту может входить значительный объём информации.
Если она хранится отдельно, основной файл остаётся компактнее:
bundle.js
bundle.js.map
Вместо:
bundle.js (включает карту внутри)
Можно публиковать только Jav * aScript:
dist/
└── bundle.js
или вместе с картами:
dist/
├── bundle.js
└── bundle.js.map
Карты легко исключаются из production-сборок.
Браузер может отдельно кэшировать:
bundle.js
bundle.js.map
Изменение карты не обязательно приводит к повторной загрузке основного файла.
Большие проекты могут содержать множество карт:
dist/
├── app.js
├── app.js.map
├── admin.js
├── admin.js.map
├── vendor.js
└── vendor.js.map
Количество файлов увеличивается почти вдвое.
Если файл карты отсутствует:
app.js
но отсутствует:
app.js.map
инструменты разработчика не смогут восстановить исходный код.
При загрузке карт браузер выполняет отдельный запрос:
GET /app.js.map
Для крупных приложений это может слегка увеличивать сетевую активность.
В режиме inline карта исходников встраивается непосредственно в JavaScript-файл.
Отдельный файл .map не создаётся.
Пример:
console.log("Hello");
//# sourceMappingURL=dat a:application/json;base64,...
После комментария располагается длинная строка Base64, содержащая JSON-карту.
SWC создаёт обычную карту:
{
"version": 3,
"sources": ["src/index.ts"]
}
Затем:
Получается единый файл:
bundle.js
внутри которого уже содержится вся необходимая информация.
Файл .swcrc:
{
"sourceMaps": "inline"
}
Программный API:
const result = await transform(code, {
sourceMaps: "inline"
});
Исходный код:
export const sum = (a: number, b: number) => a + b;
Скомпилированный файл:
export const sum = (a, b) => a + b;
//# sourceMappingURL=dat a:application/json;base64,eyJ2ZXJzaW9uIjoz...
Весь отладочный контекст находится внутри одного файла.
Не требуется хранить:
app.js.map
или следить за корректностью путей.
Достаточно:
app.js
При запуске локального сервера отсутствует риск потерять файл карты.
Например:
src/
dist/
После сборки всё необходимое уже находится внутри JavaScript-файла.
Полезно для:
Карта может занимать объём, сопоставимый с кодом.
Например:
bundle.js 800 KB
source map 600 KB
После встраивания:
bundle.js 1.4 MB
Код и карта становятся единым объектом.
Даже небольшое изменение карты приводит к изменению всего файла:
bundle.js
Пользователи загружают вместе с кодом всю карту исходников.
Это приводит к:
Режим both объединяет два предыдущих подхода.
SWC:
Получаются одновременно:
bundle.js
bundle.js.map
и встроенная Base64-карта внутри bundle.js.
{
"sourceMaps": "both"
}
Программный API:
const result = await transform(code, {
sourceMaps: "both"
});
dist/
├── bundle.js
└── bundle.js.map
Конец файла:
//# sourceMappingURL=dat a:application/json;base64,...
При этом карта также существует отдельно:
bundle.js.map
Чаще всего он используется в сложных сборочных конвейерах.
Например:
TypeScript
↓
SWC
↓
Webpack
↓
Production bundle
Некоторые инструменты могут читать встроенную карту, тогда как другие работают с отдельными файлами.
Наличие обоих вариантов обеспечивает максимальную совместимость.
Поддерживаются сценарии, где различные инструменты ожидают разные типы карт.
Например:
Карту можно:
При нескольких этапах трансформации карты часто объединяются:
TypeScript
↓
SWC
↓
Babel
↓
Webpack
Наличие обеих версий снижает вероятность потери информации о соответствии исходников.
Фактически карта хранится дважды:
bundle.js
bundle.js.map
Часть данных дублируется.
При публикации необходимо учитывать:
bundle.js
bundle.js.map
а также наличие встроенной карты внутри файла.
| Характеристика | Inline | External | Both |
|---|---|---|---|
Отдельный .map файл
|
Нет | Да | Да |
| Карта внутри JS | Да | Нет | Да |
| Размер JS-файла | Большой | Минимальный | Большой |
| Простота локальной разработки | Высокая | Средняя | Высокая |
| Подходит для production | Редко | Да | Иногда |
| Совместимость инструментов | Средняя | Высокая | Максимальная |
| Дополнительные запросы к карте | Нет | Да | Возможны |
Source maps особенно важны при минификации.
Конфигурация:
{
"minify": true,
"sourceMaps": true
}
Исходный код:
function calculatePrice(price, tax) {
return price + tax;
}
После минификации:
function n(n,t){return n+t}
Без карты исходников отладка становится затруднительной.
Source map позволяет инструментам разработчика восстановить первоначальный вид:
function calculatePrice(price, tax) {
return price + tax;
}
несмотря на выполнение минифицированного варианта.
SWC активно применяется как быстрый компилятор TypeScript.
Конфигурация:
{
"jsc": {
"parser": {
"syntax": "typescript"
}
},
"sourceMaps": true
}
Исходный файл:
interface User {
name: string;
}
const user: User = {
name: "Alex"
};
После компиляции типы удаляются:
const user = {
name: "Alex"
};
Карта сохраняет связь между TypeScript-файлом и итоговым JavaScript, что позволяет видеть исходный код при отладке.
Наиболее распространённый вариант:
{
"sourceMaps": "inline"
}
или
{
"sourceMaps": true
}
если сборщик автоматически создаёт внешние карты.
Главная цель — максимальное удобство отладки.
Наиболее распространённая схема:
{
"sourceMaps": true
}
с публикацией:
bundle.js
bundle.js.map
или с загрузкой карт только в систему мониторинга ошибок.
Часто используется внешний вариант:
{
"sourceMaps": true
}
Потребители библиотеки получают возможность отладки без увеличения размера распространяемых файлов.
Подходящим решением может быть:
{
"sourceMaps": "both"
}
когда несколько инструментов последовательно обрабатывают один и тот же код и требуется максимальная сохранность информации об исходниках.