ソースマップは、デバッグを大幅に容易にする、最新のウェブ開発に欠かせないツールです。このページでは、ソースマップの基本、生成方法、デバッグ体験の向上方法について説明します。
ソースマップの必要性
初期のウェブアプリは複雑さが低く構築されていました。デベロッパーは、HTML、CSS、JavaScript ファイルをウェブに直接デプロイしていました。
よりモダンで複雑なウェブアプリでは、開発ワークフローでさまざまなツールが必要になる場合があります。次に例を示します。
- テンプレート言語と HTML プリプロセッサ: Pug、Nunjucks、Markdown。
- CSS プリプロセッサ: SCSS、LESS、PostCSS。
- JavaScript フレームワーク: Angular、React、Vue、Svelte。
- JavaScript メタフレームワーク: Next.js、Nuxt、Astro。
- 高水準プログラミング言語: TypeScript、Dart、CoffeeScript。
これらのツールでは、ビルドプロセスを使用して、ブラウザが理解できる標準の 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.map
、styles.css.map
)。Vite、webpack、Rollup、Parcel、esbuild など、ほとんどのビルドツールで生成できます。
一部のツールには、デフォルトでソースマップが含まれています。その他のファイルは、生成するために追加の構成が必要になる場合があります。
/* 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 や Source Map Visualization などのソースマップ ビジュアライザーを使用して表示できます。
左側の [生成] 列には圧縮されたコンテンツ、[元] 列には元のソースが表示されます。
ビジュアライザは、original 列の各行を、generated 列の対応するコードで色分けします。
[mappings] セクションには、コードのデコードされたマッピングが表示されます。たとえば、エントリ 65 -> 2:2
は次を意味します。
- 生成されたコード:
const
という単語は、圧縮されたコンテンツの 65 番目の位置から始まります。 - 元のコード:
const
という単語は、元のコンテンツの 2 行目 2 列目から始まります。
これにより、デベロッパーは圧縮されたコードと元のコードの関係をすばやく特定でき、デバッグがスムーズになります。
ブラウザのデベロッパー ツールは、これらのソースマップを適用して、ブラウザでデバッグの問題をすばやく特定できるようにします。
ソースマップ拡張機能
ソースマップは、x_
接頭辞で始まるカスタム拡張フィールドをサポートしています。たとえば、Chrome DevTools で提案されている x_google_ignoreList
拡張フィールドがあります。これらの拡張機能がコードに集中できるようにする仕組みについて詳しくは、x_google_ignoreList をご覧ください。
ソースマップのデメリット
残念ながら、ソースマッピングは、必要なほど完全であるとは限りません。最初の例では、変数 greet
の値が最終的な文字列出力に直接埋め込まれているにもかかわらず、ビルドプロセス中に最適化されました。
この場合、コードをデバッグするときに、デベロッパー ツールが実際の値を推定して表示できないことがあります。このようなエラーがあると、コードのモニタリングと分析が難しくなる可能性があります。
これは、ソースマップの設計内で解決する必要がある問題です。考えられる解決策の一つは、他のプログラミング言語がデバッグ情報で行っているように、ソースマップにスコープ情報を含めることです。
ただし、そのためには、エコシステム全体が協力してソースマップの仕様と実装を改善する必要があります。ソースマップによるデバッグ可能性の向上に関する最新情報については、GitHub の ソースマップ v4 の提案をご覧ください。