Исходные карты (source maps): inline, external, hidden

Исходные карты представляют собой механизм сопоставления скомпилированного или объединённого JavaScript-кода с исходными файлами, из которых он был получен. В контексте сборщика Rollup они играют ключевую роль в процессе отладки, позволяя разработчику видеть оригинальный код вместо трансформированного бандла.

При сборке модулей Rollup преобразует множество файлов в единый или несколько выходных файлов, выполняя tree-shaking, транспиляцию и другие преобразования. Без исходных карт стек вызовов, точки останова и ошибки будут указывать на итоговый файл, что значительно усложняет диагностику. Source maps решают эту проблему, сохраняя информацию о соответствиях между исходными и итоговыми фрагментами кода.

Механизм source maps основан на отдельной структуре данных, обычно в формате JSON, которая содержит сведения о:

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

Rollup поддерживает несколько стратегий генерации source maps, каждая из которых влияет на способ хранения и доставки этой информации.

Inline-формат предполагает, что карта исходников встраивается непосредственно в итоговый JavaScript-файл. Обычно она добавляется в виде специального комментария в конце файла, содержащего закодированный в base64 JSON.

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

Основная особенность inline source maps заключается в том, что они увеличивают размер итогового файла, поскольку вся метаинформация включается внутрь него. Это делает их менее подходящими для production-среды, но удобными для разработки.

Типичный пример включения inline source map в Rollup-конфигурации:

export default {
  input: 'src/index.js',
  output: {
    file: 'dist/bundle.js',
    format: 'es',
    sourcemap: 'inline'
  }
};

В результате Rollup добавляет в конец файла строку вида:

//# sourceMappingURL=dat a:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVuZGxlLmpzIiwic291cmNlcyI6WyJzcmMvaW5kZXguanMiXSwibWFwcGluZ3MiOiJ...”

Преимущества inline source maps проявляются в следующих аспектах:

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

Недостатки:

  • значительное увеличение размера бандла
  • невозможность кэшировать карту отдельно от кода
  • потенциальное раскрытие исходного кода в production

External source maps

External source maps представляют собой отдельные .map файлы, которые генерируются параллельно с основным бандлом. В итоговый JavaScript-файл добавляется только ссылка на этот файл.

Rollup формирует два артефакта:

  • bundle.js
  • bundle.js.map

Связь между ними устанавливается через директиву:

//# sourceMappingURL=bundle.js.map

Конфигурация Rollup для внешних source maps:

export default {
  input: 'src/index.js',
  output: {
    file: 'dist/bundle.js',
    format: 'es',
    sourcemap: true
  }
};

Значение true означает использование внешнего файла карты.

External source maps являются стандартным выбором для production-сборок, поскольку они позволяют:

  • отделять отладочную информацию от основного кода
  • уменьшать размер загружаемого JavaScript
  • управлять доступом к исходным картам на уровне сервера

Файл .map обычно имеет следующую структуру:

{
  "version": 3,
  "file": "bundle.js",
  "sources": ["../src/index.js"],
  "sourcesContent": ["console.log('Hello')"],
  "names": [],
  "mappings": "AAAAA,QAAQ,CAAC,GAAG,CAAC"
}

Особое значение имеет поле sourcesContent, которое может содержать оригинальный исходный код. Его наличие делает возможным отладку даже без доступа к исходным файлам на сервере.

External source maps обеспечивают более гибкую модель развертывания, поскольку файл карты может:

  • храниться отдельно на CDN
  • быть доступным только для внутренних пользователей
  • отключаться на уровне сервера в production

Hidden source maps

Hidden source maps представляют собой промежуточный вариант, при котором карта исходников генерируется, но не указывается в итоговом JavaScript-файле.

Rollup создаёт .map файл, однако строка:

//# sourceMappingURL=

не добавляется в бандл.

Конфигурация:

export default {
  input: 'src/index.js',
  output: {
    file: 'dist/bundle.js',
    format: 'es',
    sourcemap: 'hidden'
  }
};

Такой подход используется в сценариях, где необходимо:

  • сохранять возможность анализа ошибок после деплоя
  • скрывать факт наличия source maps от клиента
  • передавать карты только в систему мониторинга ошибок

Hidden source maps часто применяются в связке с инструментами логирования ошибок (Sentry, LogRocket и аналогичными системами). В этом случае .map файл загружается не браузером, а сервером анализа ошибок.

Структурно hidden source map идентичен external, разница заключается исключительно в отсутствии ссылки на него в итоговом коде.

Влияние source maps на процесс сборки Rollup

Генерация исходных карт требует дополнительной обработки на этапе бандлинга. Rollup должен:

  • сохранять соответствия между модулями
  • учитывать трансформации кода (например, при использовании плагинов Babel или TypeScript)
  • корректно объединять карты при код-сплиттинге

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

При использовании плагинов цепочка преобразований также влияет на итоговую source map. Например, порядок применения Babel и TypeScript может изменить точность сопоставлений. Rollup агрегирует эти изменения в единую структуру mapping, что позволяет сохранить корректную трассировку ошибок.

Сопоставление строк и колонок

Source maps в Rollup работают на уровне строк и колонок. Каждая точка в итоговом коде может быть связана с конкретной позицией в исходном файле. Это достигается через VLQ-кодирование (Variable Length Quantity), которое компактно хранит числовые смещения.

Система сопоставлений строится на последовательности сегментов, где каждый сегмент содержит:

  • позицию в выходном файле
  • индекс исходного файла
  • позицию в исходном файле
  • имя символа (при наличии)

Такой подход позволяет точно восстанавливать контекст выполнения даже после агрессивной оптимизации кода.

Практическое поведение в браузере

Современные браузеры автоматически интерпретируют source maps при наличии соответствующей директивы. В инструментах разработчика отображается оригинальная структура проекта, включая:

  • имена файлов
  • строки кода
  • стек вызовов

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

Если source map недоступен или повреждён, отладчик возвращается к отображению скомпилированного кода, что существенно снижает удобство диагностики.

Безопасность и утечка информации

Использование source maps в production требует осторожности. Inline и external карты с публичным доступом могут раскрывать:

  • исходную бизнес-логику
  • структуру проекта
  • комментарии и имена переменных до минификации

Hidden source maps позволяют разделить доступ: пользователи получают только бандл, а разработчики и системы мониторинга — доступ к картам.

На уровне серверной конфигурации часто применяются правила:

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

Особенности работы с плагинами Rollup

Некоторые плагины влияют на точность source maps. Например:

  • транспиляция TypeScript может изменять структуру типов
  • Babel добавляет вспомогательные функции
  • terser изменяет структуру AST при минификации

Rollup объединяет все эти изменения в единую карту, но точность зависит от корректной поддержки source map на уровне каждого плагина. При отсутствии поддержки возможны расхождения между исходным и сгенерированным кодом.

Множественные уровни карт

В сложных сборках возможна цепочка source maps, когда каждый этап преобразования создаёт свою карту. Rollup выполняет их компоновку, создавая финальную агрегированную карту.

Этот процесс называется source map flattening и включает:

  • объединение сегментов
  • пересчёт позиций
  • устранение промежуточных ссылок

Результатом становится единая карта, соответствующая финальному бандлу, но указывающая на оригинальные исходные файлы.