ソースマップメカニズムは、プログラムのソースコードを、それに基づいて生成されたスクリプトにマップするために使用されます。トピックは新しいものではなく、多数の記事がすでに書かれているという事実(たとえば、これ、これ、これ)にも関わらず、いくつかの側面はまだ明確にする必要があります。提示された記事は、このトピックで知られているすべてのものを簡潔でアクセス可能な形式で整理および体系化する試みです。
この記事では、一般的なブラウザー(Google Chrome DevToolsなど)の環境でのクライアント開発に関連するソースマップについて説明しますが、その範囲は特定の言語や環境に限定されていません。もちろん、ソースマップの主なソースは標準ですが、まだ採用されていません(ステータス-提案)が、それでもブラウザで広くサポートされています。
ソースマップの作業は2000年代後半に始まり、Firebug Closure Inspectorプラグイン用に最初のバージョンが作成されました。 2番目のバージョンは2010年にリリースされ、マップファイルのサイズの縮小に関する変更が含まれていました。 3番目のバージョンは、GoogleとMozillaのコラボレーションの一部として開発され、2011年に提案されました(2013年に最終改訂)。
現在、クライアント開発環境では、ソースコードがWebページに直接統合されることはほとんどありませんが、処理のさまざまな段階を経ています。縮小、最適化、連結、さらに、ソースコード自体は、翻訳が必要な言語で記述できます。 ... この場合、デバッグの目的で、人間が読める元のコードをデバッガーで確認できるメカニズムが必要です。
ソースマップには次のファイルが必要です。
- 実際に生成されたJavaScriptファイル
- 作成に使用されたソースコードを含むファイルのセット
- それらを相互にマップするマップファイル
地図ファイル
ソースマップの全体の作業は、次のようなマップファイルに基づいています。
{
"version":3,
"file":"index.js",
"sourceRoot":"",
"sources":["../src/index.ts"],
"names":[],
"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,SAAS,SAAS;IACd,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;",
"sourcesContent": []
}
通常、マップファイルの名前は、それが属するスクリプトの名前と、拡張子「.map」、bundle.js-bundle.js.mapが追加された名前の合計です。これは、次のフィールドを持つ通常のjsonファイルです。
- 「バージョン」-ソースマップのバージョン。
- 「ファイル」-(オプション)現在のマップファイルが属する生成ファイルの名前。
- "SourceRoot"-(オプション)ソースファイルへのパスのプレフィックス。
- 「ソース」-ソースファイルへのパスのリスト(スクリプトタグのsrcアドレスと同じ方法で解決されます。ファイルを使用できます://。);
- 「名前」-生成されたファイルで変更された変数と関数の名前のリスト。
- 「マッピング」-Base64 VLQ形式で生成されたファイルへのソースファイルのマッピング変数と関数の座標。
- "SourcesContent"-(オプション)自己完結型のmap-fileの場合、各行にソースからのファイルのソーステキストが含まれる行のリスト。
ソースマップをダウンロード
ブラウザがマップファイルをロードするには、次のいずれかの方法を使用できます。
- JavaScriptファイルにはHTTPヘッダーが付属しています:SourceMap:<url>(以前は非推奨のX-SourceMap:<url>が以前に使用されていました)
- 生成されたJavaScriptファイルには、次の形式の特別なコメントがあります。
//# sourceMappingURL=<url> ( CSS /*# sourceMappingURL=<url> */)
したがって、マップファイルをダウンロードすると、ブラウザは「ソース」フィールドからソースを取得し、「マッピング」フィールドのデータを使用して、生成されたスクリプトにそれらを表示します。[Sources DevTools]タブには、両方のオプションがあります。
疑似プロトコルファイル://パスを示すために使用できます。また、<url>には、Base64エンコーディングでマップファイルのコンテンツ全体を含めることができます。Webpack用語では、このようなソースマップはインラインソースマップと呼ばれます。
//# sourceMappingURL=data:application/json;charset=utf-8;base64,<source maps Base64 code>
ソースマップの読み込みエラー
, map- -, Network DevTools. , map-, Console DevTools : «DevTools failed to load SourceMap: ...». , : «Could not load content for ...».
自己完結型マップファイル
ソースファイルのコードは、「sourcesContent」フィールドのマップファイルに直接含めることができます。このフィールドが利用可能な場合は、個別にダウンロードする必要はありません。この場合、「ソース」のファイル名は実際のアドレスを反映しておらず、完全に任意である可能性があります。そのため、DevToolsの[Sources]タブで、奇妙な「プロトコル」を確認できます。webpack://、ng://などです。
マッピング
マッピングメカニズムの本質は、生成されたファイルの変数と関数の名前の座標(行/列)が、対応するソースコードファイルの座標にマッピングされることです。表示メカニズムが機能するには、次の情報が必要です。
(#1)生成されたファイルの行番号。
(#2)生成されたファイルの列番号。
(#3) "sources"内のソースのインデックス。
(#4)ソース行番号。
(#5)ソース列番号。
このすべてのデータは「マッピング」フィールドにあり、その値は特別な構造とBase64 VLQでエンコードされた値を持つ長い文字列です。
行はセミコロン(;)で区切られて、生成されたファイル(#1)の行に対応するセクションになります。
各セクションはコンマ(、)で区切られたセグメントになり、各セグメントには1、4、または5つの値を含めることができます。
- 生成されたファイルの列番号(#2);
- 「ソース」のソースインデックス(#3);
- ソース行番号(#4);
- ソース列番号(#5);
- 名前リストからの変数/関数名のインデックス。
行番号と列番号の値は相対的であり、前の座標を基準としたオフセットを示し、ファイルまたはセクションの最初からの最初の値のみを示します。
各値はBase64 VLQ番号です。 VLQ(可変長数量)は、固定長の任意の数のバイナリブロックを使用して、任意の大きな数をエンコードする原理です。
ソースマップは6ビットのブロックを使用し、低から高の順に並べられます。各ブロックの最上位6番目のビット(継続ビット)は予約されており、設定されている場合、現在のブロックの後に同じ番号に関連する次のブロックが続きます。クリアされている場合、シーケンスは完了です。
ソースマップでは値に符号が必要なため、最下位の1ビット(符号ビット)も予約されていますが、これはシーケンスの最初のブロックでのみです。予想どおり、設定された符号ビットは負の数を意味します。
したがって、数値が単一のブロックでエンコードできる場合、15(1111 2)を法とすることはできません。シーケンスの最初の6ビットブロックでは2ビットが予約されているため、継続ビットは常にクリアされ、数値の符号に応じて符号ビットが設定されます。
6ビットVLQブロックはBase64エンコーディングにマップされ、各6ビットシーケンスは特定のASCII文字にマップされます。
数値mEをデコードします。順序を逆にします。最後の部分はEmです。 Base64から数値をデコードします。E-000100、m-100110。最初に、高継続ビットと2つの先行ゼロ-100を破棄します。2番目に、高継続ビットと低符号ビットを破棄します(符号ビットはクリアされます-数値は正です)-0011。その結果、100になります。 0011 2、これは10進数の67
に対応します。逆方向にエンコードすることもできます。41をエンコードします。そのバイナリコードは101001 2、2つのブロックに分割します。上位部分-10、下位部分(常に4ビット)-1001。上位部分に上位連続ビット(クリア)と3つの先行ゼロ-000010を追加します。下位部分に上位継続ビット(セット)を追加し、最下位の符号ビット(クリア-数値は正)-110010。Base64で数値をエンコードします:000010-C、110010-y。順序を逆にし、結果としてyCを取得します。
同じ名前のライブラリは VLQでの作業に非常に役立ちます。