Source maps — это мост между вашим красивым кодом и минифицированным продакшн-бандлом. Узнайте, как они работают, как их настроить и как отлаживать продакшн-ошибки, не раскрывая исходный код.
Каждое современное веб-приложение проходит через пайплайн сборки: TypeScript
транспилируется в JavaScript, JSX конвертируется в React.createElement,
стили оптимизируются, а весь код минифицируется в один или
несколько бандлов. Результат эффективен, но абсолютно нечитаем.
Когда в продакшне возникает ошибка, минифицированный бандл даёт такой стектрейс:
TypeError: Cannot read properties of undefined (reading 'id')
at e (main.a1b2c3.js:1:48732)
at t (main.a1b2c3.js:1:48901)
at Object.r (main.a1b2c3.js:1:49015)
Номера колонок указывают на позиции в минифицированном файле — бесполезно для отладки. Source maps отображают каждую позицию в минифицированном выводе обратно в исходный файл, строку и колонку. С source map та же ошибка выглядит так:
TypeError: Cannot read properties of undefined (reading 'id')
at UserProfile.render (src/components/UserProfile.tsx:42:15)
at renderWithHooks (src/react-dom/ReactFiber.ts:1560:22)
Это разница между гаданием и точным знанием, где произошла ошибка. В этом руководстве я расскажу всё, что нужно знать о source maps — от внутреннего устройства VLQ-кодирования до стратегий развёртывания в продакшн.
Прежде чем перейти к настройке, полезно понять, что содержится в .map
файле. Source maps описываются Source Map Specification v3,
изначально разработанной в Google.
Типичный файл source map выглядит так:
{
"version": 3,
"file": "main.a1b2c3.js",
"mappings": "AAAA,SAASA,EAAUC...",
"sources": [
"webpack:///src/index.ts",
"webpack:///src/components/App.tsx"
],
"sourcesContent": [
"import React from 'react';\\n...",
"export function App() {\\n..."
],
"names": ["require", "exports", "module"],
"sourceRoot": ""
}
Ключевые поля:
Строка mappings — это последовательность разделённых пробелами и запятыми
VLQ (Variable-Length Quantity) Base64 значений. Каждый сегмент
кодирует одну позицию. Один сегмент содержит до пяти полей:
sources, относительный)names, опционально, относительный)Все позиции хранятся как относительные смещения — каждый сегмент хранит разницу от предыдущего. Эта техника сжатия уменьшает размер source maps с потенциальных 10-20 МБ (с абсолютными координатами) до типичных 1-2 МБ для бандла размером 200 КБ.
Разные сборщики используют разные названия для похожих стратегий. Вот полное сравнение:
| Стратегия | Скорость сборки | Качество отладки | Безопасность | Где использовать |
|---|---|---|---|---|
| source-map | Медленно ⚠ | Полное — строки + колонки + исходники | Открывает код ⚠ | Стейдж/QА |
| hidden-source-map | Медленно ⚠ | Полное — строки + колонки + исходники | Скрыт от браузера | Продакшн с Sentry |
| nosources-source-map | Медленно ⚠ | Только строки + колонки (без исходников) | Безопасно | Продакшн, базовый дебаг |
| cheap-source-map | Быстро ★ | Только строки, без колонок | Открывает код ⚠ | Разработка |
| cheap-module-source-map | Быстро ★ | Строки + loader-source maps | Открывает код ⚠ | Разработка (рекомендуется) |
| eval-source-map | Очень быстро ★ | Полное — per-module eval | Открывает код ⚠ | Разработка (быстрые пересборки) |
| eval | Очень быстро ★ | Только модули (без mapping) | Худшая ⚠ | Разработка, быстрая итерация |
Каждый сборщик имеет свой API, но базовые опции соответствуют одним и тем же стратегиям. Вот как настроить каждый инструмент.
Webpack использует опцию devtool. Для продакшна с Sentry:
// webpack.config.js — продакшн
module.exports = {
devtool: 'hidden-source-map',
// Создаёт .map файлы без //# sourceMappingURL
// .map загружаются в Sentry, с сервера — удаляются
};
// webpack.config.js — разработка
module.exports = {
devtool: 'eval-source-map',
// Быстрые пересборки с полным качеством отладки
};
Для точного контроля используйте SourceMapDevToolPlugin:
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.SourceMapDevToolPlugin({
filename: '[file].map',
exclude: ['vendors-*.js', 'runtime-*.js'],
include: ['app-*.js'],
}),
],
};
Vite использует build.sourcemap в vite.config.ts.
Rolldown (новый сборщик Vite на Rust, стабилен с начала 2026) поддерживает
те же опции:
// vite.config.ts — продакшн с мониторингом
import { defineConfig } from 'vite';
export default defineConfig({
build: {
sourcemap: 'hidden', // без sourceMappingURL
// true, false, 'inline', 'hidden'
},
});
// vite.config.ts — разработка
import { defineConfig } from 'vite';
export default defineConfig({
build: {
sourcemap: true, // Полные source maps
},
});
# esbuild — продакшн со скрытыми картами
esbuild src/index.ts --bundle --minify \
--sourcemap=external \
--outfile=dist/main.js
# .map файл создаётся рядом с бандлом
# Комментарий sourceMappingURL отсутствует
# esbuild — разработка
esbuild src/index.ts --bundle \
--sourcemap \
--outfile=dist/main.js
esbuild поддерживает: --sourcemap (встроенный),
--sourcemap=external (внешний без комментария),
--sourcemap=linked (внешний с комментарием),
--sourcemap=both (встроенный + внешний).
// tsconfig.json
{
"compilerOptions": {
"sourceMap": true,
"inlineSources": true,
"sourceRoot": "/src",
"mapRoot": "/maps"
}
}
Настоящая сила source maps раскрывается в паре с системами мониторинга ошибок. Вот как настроить полный пайплайн.
// @sentry/webpack-plugin (v3+)
const SentryPlugin = require('@sentry/webpack-plugin');
module.exports = {
devtool: 'hidden-source-map',
plugins: [
new SentryPlugin({
org: process.env.SENTRY_ORG,
project: process.env.SENTRY_PROJECT,
authToken: process.env.SENTRY_AUTH_TOKEN,
release: process.env.RELEASE_VERSION,
include: './dist',
ignore: ['node_modules'],
urlPrefix: '~/',
}),
],
};
# Альтернатива: sentry-cli (работает с любым сборщиком)
export SENTRY_AUTH_TOKEN=ваш_токен
export VERSION=$(git rev-parse HEAD)
sentry-cli releases new "$VERSION"
sentry-cli releases files "$VERSION" \
upload-sourcemaps ./dist \
--url-prefix '~/'
sentry-cli releases set-commits "$VERSION" --auto
sentry-cli releases finalize "$VERSION"
export DD_API_KEY=ваш_ключ
export DD_APP_KEY=ключ_приложения
datadog-ci sourcemaps upload ./dist \
--service=my-web-app \
--release-version=$(git rev-parse HEAD) \
--minified-path-prefix='https://yourdomain.com/assets/'
Source maps в продакшне — это палка о двух концах. Они позволяют отлаживать, но в то же время раскрывают исходный код любому, кто откроет DevTools.
hidden-source-map (webpack) или hidden
(Vite/Rolldown) создаёт .map файл на диске, но не добавляет
//# sourceMappingURL в бандл. Это значит:
.map файл существует на сервере для загрузки в Sentrymain.js.map) нужно блокировать .map на уровне сервера# nginx — блокируем .map файлы
location ~* \.map$ {
deny all;
return 404;
}
# Или: Apache .htaccess
<FilesMatch "\.map$">
Require all denied
</FilesMatch>
Пайплайн продакшн source maps состоит из трёх этапов: генерация, загрузка и аудит. Вот полный пример GitHub Actions:
# .github/workflows/deploy.yml
name: Build, Upload Source Maps, Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
- name: Install & Build
run: |
npm ci
npm run build # dist/ с .map файлами
- name: Upload to Sentry
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
run: |
VERSION=$(git rev-parse HEAD)
sentry-cli releases new "$VERSION"
sentry-cli releases files "$VERSION" \
upload-sourcemaps ./dist \
--url-prefix '~/'
sentry-cli releases finalize "$VERSION"
- name: Deploy to Server
run: |
rsync -avz ./dist/ user@server:/var/www/app/
# Удаляем .map из публичной папки
ssh user@server 'rm /var/www/app/**/*.map'
Критический шаг, который многие пропускают: удаляйте .map
файлы с публичного сервера после деплоя. Даже с hidden-source-map
оставшиеся .map файлы — это риск безопасности.
Правильно настроенный пайплайн source maps — одна из самых эффективных инвестиций во фронтенд-инфраструктуру. Он превращает "минифицированная ошибка в строке 1, колонке 48732" в точные стектрейсы с указанием исходного кода.
Вот моя рекомендуемая стратегия:
1. Используйте hidden-source-map для продакшна.
Лучшее качество отладки с минимальным риском безопасности.
2. Интегрируйте загрузку source maps в CI/CD. Автоматическая выгрузка в Sentry или DataDog гарантирует, что каждый релиз имеет отлаживаемые стектрейсы. Настройка занимает 15 минут и экономит часы отладки.
3. Блокируйте .map файлы на веб-сервере.
Даже со скрытыми source maps, аудит безопасности найдёт .map
файлы на публичном сервере. Запретите доступ ко всем .map.
4. Регулярно проверяйте доступность source maps.
Простая curl-проверка: curl -sI https://yourdomain.com/main.js.map.
Если ответ 200 — ваш код открыт. Добавьте это в CI/CD security scanning.
Source maps — это только часть надёжной системы мониторинга фронтенда. Я помогаю командам настраивать полный пайплайн: от конфигурации сборки до интеграции с Sentry/DataDog и процесса реагирования на инциденты.
Я full-stack разработчик с большим опытом в отладке продакшн-приложений, оптимизации производительности и фронтенд-инфраструктуре. Свяжитесь со мной для бесплатной консультации.
Нужна помощь с настройкой source maps, интеграцией Sentry или инфраструктурой отладки фронтенда? Я провожу бесплатные консультации.