ソースマップとは何ですか?

ソースマップは、デバッグを大幅に容易にする、最新のウェブ開発に欠かせないツールです。このページでは、ソースマップの基本、生成方法、デバッグ体験の向上方法について説明します。

ソースマップの必要性

初期のウェブアプリは複雑さが低く構築されていました。デベロッパーは、HTML、CSS、JavaScript ファイルをウェブに直接デプロイしていました。

よりモダンで複雑なウェブアプリでは、開発ワークフローでさまざまなツールが必要になる場合があります。例:

さまざまなツールの概要。
一般的なウェブアプリ開発ツールの例。

これらのツールでは、ビルドプロセスを使用して、ブラウザが理解できる標準の HTML、JavaScript、CSS にコードをトランスパイルする必要があります。また、Terser などのツールを使用して、これらのファイルを圧縮して結合し、パフォーマンスを最適化するのも一般的な方法です。

たとえば、ビルドツールを使用すると、次の TypeScript ファイルをトランスパイルして 1 行の JavaScript に圧縮できます。GitHub にあるデモで実際に試してみることができます。

/* A TypeScript demo: example.ts */

document.querySelector('button')?.addEventListener('click', () => {
  const num: number = Math.floor(Math.random() * 101);
  const greet: string = 'Hello';
  (document.querySelector('p') as HTMLParagraphElement).innerText = `${greet}, you are no. ${num}!`;
  console.log(num);
});

圧縮されたバージョンは次のようになります。

/* A compressed JavaScript version of the TypeScript demo: example.min.js  */

document.querySelector("button")?.addEventListener("click",(()=>{const e=Math.floor(101*Math.random());document.querySelector("p").innerText=`Hello, you are no. ${e}!`,console.log(e)}));

ただし、コードを圧縮すると、デバッグが難しくなる可能性があります。ソースマップを使用すると、この問題を解消できます。コンパイルされたコードを元のコードにマッピングし直すことで、エラーの原因をすばやく見つけることができます。

ソースマップを生成する

ソースマップは、名前が .map で終わるファイルです(例: example.min.js.mapstyles.css.map)。VitewebpackRollupParcelesbuild など、ほとんどのビルドツールで生成できます。

一部のツールには、デフォルトでソースマップが含まれています。それらを生成するために追加の構成が必要な場合もあります。

/* Example configuration: vite.config.js */
/* https://vitejs.dev/config/ */

export default defineConfig({
  build: {
    sourcemap: true, // enable production source maps
  },
  css: {
    devSourcemap: true // enable CSS source maps during development
  }
})

ソースマップの概要

デバッグに役立つように、これらのソースマップ ファイルには、コンパイルされたコードが元のコードにマッピングされる方法に関する重要な情報が含まれています。ソースマップの例を次に示します。

{
  "mappings": "AAAAA,SAASC,cAAc,WAAWC, ...",
  "sources": ["src/script.ts"],
  "sourcesContent": ["document.querySelector('button')..."],
  "names": ["document","querySelector", ...],
  "version": 3,
  "file": "example.min.js.map"
}

これらの各フィールドについては、ソースマップの仕様またはソースマップの構造をご覧ください。

ソースマップで最も重要な部分は、mappings フィールドです。VLQ Base64 エンコード文字列を使用して、コンパイルされたファイル内の行と位置を対応する元のファイルにマッピングします。このマッピングは、source-map-visualizationソースマップの可視化などのソースマップ ビジュアライザを使用して表示できます。

ソースマップの可視化。
ビジュアリザによって生成された、前のコードサンプルの可視化。

左側の「生成された」列は圧縮されたコンテンツを示し、「元の」(元の)列は元のソースを示しています。

ビジュアライザは、original 列の各行を、generated 列の対応するコードで色分けします。

[mappings] セクションには、コードのデコードされたマッピングが表示されます。たとえば、エントリ 65 -> 2:2 の意味は次のとおりです。

  • 生成されたコード: 圧縮されたコンテンツの 65 番目の位置から const という単語が始まります。
  • 元のコード: const という単語は、元のコンテンツの 2 行目 2 列目から始まります。
マッピング エントリ。
65 -> 2:2 エントリに焦点を当てたマッピングの可視化。

これにより、デベロッパーは圧縮されたコードと元のコードの関係をすばやく特定でき、デバッグがスムーズになります。

ブラウザのデベロッパー ツールは、これらのソースマップを適用して、ブラウザでデバッグの問題をすばやく特定できるようにします。

ソースマップを適用するデベロッパー ツール。
ブラウザ デベロッパー ツールでソースマップを適用してファイル間のマッピングを表示する方法の例。

ソースマップ拡張機能

ソースマップは、x_ 接頭辞で始まるカスタム拡張フィールドをサポートしています。たとえば、Chrome DevTools で提案されている x_google_ignoreList 拡張フィールドがあります。これらの拡張機能がコードに集中しやすくなる仕組みについては、x_google_ignoreList をご覧ください。

ソースマップのデメリット

残念ながら、ソースマッピングは、必要なほど完全であるとは限りません。最初の例では、変数 greet の値が最終的な文字列出力に直接埋め込まれているにもかかわらず、ビルドプロセス中に最適化されました。

変数の greet がマッピングされていません。
元のコードの greet 変数がマッピングにありません。

この場合、コードをデバッグするときに、デベロッパー ツールが実際の値を推測して表示できない可能性があります。このようなエラーはコードのモニタリングや 分析を難しくする可能性があります

変数 greet が未定義です。
デベロッパー ツールで greet の値が見つかりません。

これは、ソースマップの設計内で解決する必要がある問題です。考えられる解決策の一つは、他のプログラミング言語がデバッグ情報で行っているように、ソースマップにスコープ情報を含めることです。

ただし、そのためには、エコシステム全体が連携してソースマップの仕様と実装を改善する必要があります。ソースマップによるデバッグ可能性の向上に関する最新情報については、GitHub の ソースマップ v4 の提案をご覧ください。