ソースマップは、最新のウェブ開発において極めて簡単にデバッグできるツールです。このページでは、ソースマップの基本、ソースマップの生成方法、デバッグ エクスペリエンスの改善方法について説明します。
ソースマップの必要性
初期のウェブアプリは、あまり複雑にならないように作られていました。開発者は 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 Base 64 でエンコードされた文字列を使用して、コンパイル済みファイル内の行と場所を、対応する元のファイルにマッピングします。このマッピングは、source-map-visualization やソースマップの可視化などのソースマップ ビジュアライザを使用して表示できます。
左側の [生成されたコンテンツ] 列に圧縮コンテンツが表示され、[オリジナル] 列に元のソースが表示されます。
ビジュアライザは、元の列の各行を、生成された列の対応するコードとともに色分けします。
[mappings] セクションには、コードのデコードされたマッピングが表示されます。たとえば、エントリ 65 -> 2:2
は次のことを意味します。
- 生成されたコード: 圧縮済みコンテンツの 65 番目から「
const
」という単語が出現します。 - オリジナル コード: 単語
const
はオリジナル コンテンツの 2 行目と 2 列目から始まります。
これにより、デベロッパーは圧縮されたコードと元のコードの関係をすばやく特定し、デバッグをよりスムーズに行うことができます。
ブラウザのデベロッパー ツールは、これらのソースマップを適用して、ブラウザでデバッグ中の問題をすばやく特定できるようにします。
ソースマップ拡張機能
ソースマップは、x_
接頭辞で始まるカスタム拡張フィールドをサポートしています。その一例が、Chrome DevTools で提案されている x_google_ignoreList
拡張フィールドです。これらの拡張機能を使用してコードに集中できるようにする方法について詳しくは、x_google_ignoreList をご覧ください。
ソースマップの欠点
残念ながら、ソース マッピングが必ずしも必要であるとは限りません。最初の例では、変数 greet
は最終的な文字列出力に直接埋め込まれていますが、ビルドプロセス中に最適化されました。
この場合、コードをデバッグする際に、デベロッパー ツールで実際の値を推測して表示できない場合があります。この種のエラーはコードのモニタリングや分析を 難しくするかもしれません
これは、ソースマップの設計内で解決する必要がある問題です。考えられる解決策の 1 つは、他のプログラミング言語のデバッグ情報と同じように、ソースマップにスコープ情報を含めることです。
ただし、これを行うには、エコシステム全体が連携してソースマップの仕様と実装を改善する必要があります。ソースマップによるデバッグ可能性の改善については、GitHub のソースマップ v4 の提案をご覧ください。