はじめに
minify されたコードのエラーを見たとき、こんな行番号が出たことはありませんか?
Uncaught TypeError: Cannot read properties of undefined
at a.b (app.min.js:1:4832)
👉 SourceMap を使えば、圧縮されたコードでも元のファイル・行番号でデバッグできます
この記事では SourceMap の仕組み・デバッグでの使い方・本番運用のリスクを基礎から解説します。
SourceMap とは何か
minify / bundle とは
本番用ビルドでは、コードは次のように変換されます:
【変換前】src/utils.js(読みやすいコード)
function greet(name) {
const message = `Hello, ${name}!`;
console.log(message);
}
【変換後】dist/app.min.js(圧縮・難読化)
function greet(n){const m=`Hello, ${n}!`;console.log(m)}
👉 ファイルサイズは小さくなるが、エラーが起きても元のコードがわからなくなる
SourceMap の役割
SourceMap は「変換後のコード」と「変換前のコード」の対応関係を記録したファイルです。
src/utils.js(元コード)
↓ build
dist/app.min.js ← 実際にブラウザが実行するコード
dist/app.min.js.map ← SourceMap(対応関係の地図)
👉 ブラウザの DevTools がこの .map ファイルを読み込み、元のコードでデバッグできるようにしてくれる
.map ファイルの中身を見てみる
.map ファイルは JSON 形式です。実際に開くと次のような内容です:
{
"version": 3,
"file": "app.min.js",
"sources": ["src/utils.js", "src/main.js"],
"sourcesContent": ["function greet(name) {...}", "import ..."],
"names": ["greet", "name", "message"],
"mappings": "AAAA,SAAS,MAAM,CAAC,IAAI"
}
主要フィールド
| フィールド | 内容 |
|---|---|
version |
SourceMap の仕様バージョン(現在は 3) |
sources |
元ファイルのパス一覧 |
sourcesContent |
元ファイルの内容(インライン埋め込み用) |
names |
変数名・関数名の一覧 |
mappings |
変換後コードの各位置 → 元コードの位置の対応(VLQ エンコード) |
mappings フィールドについて
mappings は VLQ(Variable-Length Quantity) という形式でエンコードされた文字列です。
"AAAA,SAAS,..." → 「変換後の1列目は元ファイルの0行0列目に対応」という情報が圧縮されている
👉 手で読む必要はありません。DevTools や source-map ライブラリが自動で解釈します
ブラウザはどう使うか
ブラウザは変換後のファイルの末尾にある次のコメントを手がかりに .map を取得します:
//# sourceMappingURL=app.min.js.map
👉 このコメントがあると、DevTools が自動的に .map を読み込んで元コードを表示してくれる
デバッグでの使い方
DevTools で元のコードを見る
SourceMap が有効な状態でビルドされたコードをブラウザで開くと、
DevTools の Sources タブに元のファイル構造が表示されます。
Sources タブ
└── webpack://
└── src/
├── utils.js ← 元のソースコードがここに!
└── main.js
👉 ブレークポイントも元ファイルの行に貼れる
//# sourceMappingURL の意味
変換後ファイルの末尾に自動挿入されるコメントです:
// 外部ファイルを参照する場合
//# sourceMappingURL=app.min.js.map
// Base64 で .map をインライン埋め込みする場合
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9u...
👉 インライン埋め込みは .map ファイルを別途配置しなくてよいが、ファイルサイズが大きくなる
//# sourceURL との違い
sourceMappingURL |
sourceURL |
|
|---|---|---|
| 用途 | ビルド済みコードと元コードのマッピング |
eval() などで動的に生成したコードに名前を付ける |
| 対象 | バンドル後のファイル |
eval や new Function
|
// eval したコードに名前を付ける例
eval('function hello() {}' + '\n//# sourceURL=dynamic-module.js');
本番環境でのリスク
❌ SourceMap を本番の公開ディレクトリに置くとどうなるか
//# sourceMappingURL=app.min.js.map があり、かつ .map が公開されていると:
攻撃者がブラウザの DevTools を開く
→ Sources タブに元のソースコードが丸見えになる
→ ビジネスロジック・認証処理・API キーのハードコードなどが露出する
👉 難読化の意味がなくなり、セキュリティ上のリスクになる
✅ 正しい運用パターン3つ
パターン① 本番では SourceMap を生成しない(最もシンプル)
本番ビルド時に SourceMap の出力を無効にする
→ .map ファイルが存在しないのでリスクゼロ
→ デメリット: 本番エラーの調査が難しくなる
パターン② 生成するが非公開ディレクトリに置く
.map ファイルを公開ディレクトリ外に保存する
→ Sentry などのエラートラッキングツールに手動アップロードして活用
→ デメリット: デプロイフローが少し複雑になる
hidden-source-map(webpack)や hidden(Vite)を使うと .map は生成されるが
//# sourceMappingURL コメントが出力されないため、ブラウザから辿られない。
パターン③ 認証が必要な URL に置く
.map ファイルへのアクセスを社内 IP や認証ヘッダーで制限する
→ エンジニアはアクセスできるが外部からは取得できない
→ デメリット: インフラ設定が必要
👉 多くのプロダクションサービスでは パターン①か② が現実的