ソースマップにより、ウェブ デバッグのエクスペリエンスが向上します。
今回は、ソースマップについて説明します。ソースマップは、デバッグを大幅に簡易化する、最新のウェブ開発における重要なツールです。この記事では、ソースマップの基本、ソースマップの生成方法、デバッグ エクスペリエンスの改善について解説します。
ソースマップの必要性
昔は、純粋な 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 を使用して JavaScript の圧縮やマングリングを行う)して結合し、サイズを小さくしてウェブでの効率を高めるのが一般的です。
たとえば、ビルドツールを使用すると、次の 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)}));
ただし、この最適化により、デバッグが難しくなることがあります。すべてを 1 行で記述し、変数名が短い圧縮コードでは、問題の原因を特定するのが難しくなることがあります。ここで必要になるのがソースマップです。ソースマップは、コンパイルしたコードを元のコードにマッピングします。
ソースマップの生成
ソースマップは、名前が .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 や Source Map visibility などのソースマップ ビジュアライザを使用して可視化できます。
左側の [生成済み] 列には圧縮コンテンツが表示され、[オリジナル] 列には元のソースが表示されます。
ビジュアライザでは、元の列の各行と、生成された列の対応するコードが色分けされます。
[mappings] セクションには、デコードされたコードのマッピングが表示されます。たとえば、65-> 2:2
というエントリは次のことを意味します。
- 生成されたコード: 圧縮済みコンテンツの 65 番目から「
const
」という単語が出現します。 - オリジナル コード: 単語
const
はオリジナル コンテンツの 2 行目と 2 列目から始まります。
これによりデベロッパーは、圧縮されたコードと元のコードの関係をすばやく特定し、よりスムーズなデバッグを行うことができます。
ブラウザ デベロッパー ツールはこれらのソースマップを適用し、ブラウザでデバッグの問題を速やかに特定できるようにします。
この画像は、ブラウザのデベロッパー ツールがソースマップを適用する仕組みと、ファイル間のマッピングを示しています。
ソースマップ拡張機能
ソースマップは拡張機能をサポートしています。拡張機能は、x_
命名規則で始まるカスタム フィールドです。その一例が、Chrome DevTools で提案されている x_google_ignoreList
拡張機能フィールドです。これらの拡張機能がコードに集中するのにどのように役立つかについて詳しくは、x_google_ignoreList をご覧ください。
完璧ではない
この例では、変数 greet
はビルドプロセス中に最適化されています。値は最終的な文字列出力に直接埋め込まれました。
この場合、コードをデバッグするときに、デベロッパー ツールで実際の値を推測して表示できないことがあります。これは、ブラウザのデベロッパー ツールだけの問題ではありません。また、コードのモニタリングや分析が難しくなります。
もちろん、これは解決可能な問題です。その方法の一つは、他のプログラミング言語のデバッグ情報と同様に、ソースマップにスコープ情報を含めることです。
ただし、それにはエコシステム全体が連携してソースマップの仕様と実装を改善する必要があります。ソースマップによるデバッグ性の向上に関する活発な議論があります。
ソースマップを改善し、デバッグの負担を軽減することを楽しみにしています。