ソースマップはどのように元コードへの参照を保持するか

2.1K Views

November 30, 25

スライド概要

Docswellを使いましょう

(ダウンロード不可)

関連スライド

各ページのテキスト
1.

ソースマップはどのように 元コードへの参照を保持するか 2025.11.30 フロントエンドカンファレンス関西2025 @yuta-ike エムスリー / エンジニアリンググループ

2.

1 ソースマップについて 私たちが書くプログラム ≠ 実行されるプログラム プログラマ ブラウザ TypeScript JavaScript JavaScript Transpile Minify / Transpile

3.

1 ソースマップについて 私たちが書くプログラム ≠ 実行されるプログラム プログラマ TypeScript index.jsの1234行目の 5678文字目でエラー! ブラウザ JavaScript JavaScript Transpile Minify / Transpile

4.

1 ソースマップについて 私たちが書くプログラム ≠ 実行されるプログラム TSファイル上で どこか教えてくれ... プログラマ TypeScript index.jsの1234行目の 5678文字目でエラー! ブラウザ JavaScript JavaScript Transpile Minify / Transpile

5.

1 ソースマップについて ソースマップの重要性 変換前後の対応を保持しておき、ログやスタックトレースで 変換前の位置情報を提供する仕組み。 App.tsの5行目の なるほど理解 12文字目でエラー! index.jsの1234行目の 5678文字目でエラー! プログラマ ブラウザ TypeScript JavaScript Transpile JavaScript Minify / Transpile

6.

1 ソースマップについて ソースマップの重要性 フロントエンドでは大なり小なりTranspileが当たり前の世界 JSファイルの何行目/何文字目が、変換前のファイルのどこに対応する か?がわかることは開発生産性に直結。 → コード変換前後のマッピングを管理するのがソースマップ。 〇〇.jsの5行目は、変換前でいうと△△.tsの10行目だよ。 ...みたいなことを実現する

7.

1 ソースマップについて ソースマップはどこにあるの?

8.

2 ソースマップの仕組み シンプルなコンポーネント viteの最新バージョンのdevモード。 (ビルドするとバンドルされてファイルサイズが大きくなるのでdevモードで説明)

9.

2 ソースマップの仕組み ビルド結果 App Component JSX処理済み・fileNameなどが 見受けられるがこれはjsx用のデ バッグ情報。 HMR用の処理 これもまた面白い... またの機会に...

10.

2 ソースマップの仕組み ビルド結果 この1行がソースマップ! 1行なだけで結構長い。

11.

2 ソースマップの仕組み ソースマップ vite devモードではファイルにインラインに埋め込まれる。 本番ビルド時は別ファイル(hoge.js.map)に分割することも可能。 その場合はソースマップファイルのパスが記載される。 ファイル内にコメントで埋め込む以外に、HTTPヘッダで指定する方法もある sourcemap: <URL> の形式 viteのsourcemap オプション: https://vite.dev/config/build-options#build-sourcemap sourcemap をHTTPヘッダーで指定する仕様: https://426.ecma-international.org/1.0/index.html#linking-http-header sourcemap ヘッダーの解説:https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/SourceMap

12.

2 ソースマップの仕組み ソースマップ //# sourceMappingURL=data:application/ json;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0Esa0JBQWtCO0FBQzNCLFNBQVNDLGtCQU FrQjtBQUMzQixPQUFPO0FBQ1AsT0FBT0MsU0FBUzs7O0FBRWhCRCxXQUFXRSxTQUFTQyxlQUFlLE9BQU8sQ0FBRSxD QUFDQyxPQUMzQyx3QkFBQyx3QkFDQyx3QkFBQzs7OztRQUFHOzs7O1FBRVIsQ0FBQyIsIm5hbWVzIjpbIlN0cmljdE 1vZGUiLCJjcmVhdGVSb290IiwiQXBwIiwiZG9jdW1lbnQiLCJnZXRFbGVtZW50QnlJZCIsInJlbmRlciJdLCJpZ25v cmVMaXN0IjpbXSwic291cmNlcyI6WyJtYWluLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBTdHJpY3 RNb2RlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBjcmVhdGVSb290IH0gZnJvbSAncmVhY3QtZG9tL2NsaWVudCdc bmltcG9ydCAnLi9pbmRleC5jc3MnXG5pbXBvcnQgQXBwIGZyb20gJy4vQXBwLnRzeCdcblxuY3JlYXRlUm9vdChkb2 N1bWVudC5nZXRFbGVtZW50QnlJZCgncm9vdCcpISkucmVuZGVyKFxuICA8U3RyaWN0TW9kZT5cbiAgICA8QXBwIC8+ XG4gIDwvU3RyaWN0TW9kZT4sXG4pXG4iXSwiZmlsZSI6Ii9Vc2Vycy9pa2VjaGFuL2Rldi90b3lib3gvZmVjX2thbn NhaS9zcmMvbWFpbi50c3gifQ==

13.

2 ソースマップの仕組み ソースマップ //# sourceMappingURL=data:application/ json;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0Esa0JBQWtCO0FBQzNCLFNBQVNDLGtCQU FrQjtBQUMzQixPQUFPO0FBQ1AsT0FBT0MsU0FBUzs7O0FBRWhCRCxXQUFXRSxTQUFTQyxlQUFlLE9BQU8sQ0FBRSxD # sourceMappingURLというコメント QUFDQyxPQUMzQyx3QkFBQyx3QkFDQyx3QkFBQzs7OztRQUFHOzs7O1FBRVIsQ0FBQyIsIm5hbWVzIjpbIlN0cmljdE 1vZGUiLCJjcmVhdGVSb290IiwiQXBwIiwiZG9jdW1lbnQiLCJnZXRFbGVtZW50QnlJZCIsInJlbmRlciJdLCJpZ25v cmVMaXN0IjpbXSwic291cmNlcyI6WyJtYWluLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBTdHJpY3 RNb2RlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBjcmVhdGVSb290IH0gZnJvbSAncmVhY3QtZG9tL2NsaWVudCdc /^[@#]\s*sourceMappingURL=(\S*?)\s*$/ という正規表現が用いられる。 bmltcG9ydCAnLi9pbmRleC5jc3MnXG5pbXBvcnQgQXBwIGZyb20gJy4vQXBwLnRzeCdcblxuY3JlYXRlUm9vdChkb2 N1bWVudC5nZXRFbGVtZW50QnlJZCgncm9vdCcpISkucmVuZGVyKFxuICA8U3RyaWN0TW9kZT5cbiAgICA8QXBwIC8+ (@は古い書き方) XG4gIDwvU3RyaWN0TW9kZT4sXG4pXG4iXSwiZmlsZSI6Ii9Vc2Vycy9pa2VjaGFuL2Rldi90b3lib3gvZmVjX2thbn NhaS9zcmMvbWFpbi50c3gifQ== ソースマップはJSのみを想定しない(CSS/WebAssemblyなど)ため、詳細な書き方は各言 語に依存する。

14.

2 ソースマップの仕組み ソースマップ //# sourceMappingURL=data:application/ json;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0Esa0JBQWtCO0FBQzNCLFNBQVNDLGtCQU FrQjtBQUMzQixPQUFPO0FBQ1AsT0FBT0MsU0FBUzs7O0FBRWhCRCxXQUFXRSxTQUFTQyxlQUFlLE9BQU8sQ0FBRSxD QUFDQyxPQUMzQyx3QkFBQyx3QkFDQyx3QkFBQzs7OztRQUFHOzs7O1FBRVIsQ0FBQyIsIm5hbWVzIjpbIlN0cmljdE data url。application/jsonでbase64エンコード 1vZGUiLCJjcmVhdGVSb290IiwiQXBwIiwiZG9jdW1lbnQiLCJnZXRFbGVtZW50QnlJZCIsInJlbmRlciJdLCJpZ25v cmVMaXN0IjpbXSwic291cmNlcyI6WyJtYWluLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBTdHJpY3 RNb2RlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBjcmVhdGVSb290IH0gZnJvbSAncmVhY3QtZG9tL2NsaWVudCdc bmltcG9ydCAnLi9pbmRleC5jc3MnXG5pbXBvcnQgQXBwIGZyb20gJy4vQXBwLnRzeCdcblxuY3JlYXRlUm9vdChkb2 N1bWVudC5nZXRFbGVtZW50QnlJZCgncm9vdCcpISkucmVuZGVyKFxuICA8U3RyaWN0TW9kZT5cbiAgICA8QXBwIC8+ XG4gIDwvU3RyaWN0TW9kZT4sXG4pXG4iXSwiZmlsZSI6Ii9Vc2Vycy9pa2VjaGFuL2Rldi90b3lib3gvZmVjX2thbn NhaS9zcmMvbWFpbi50c3gifQ==

15.

2 ソースマップの仕組み ソースマップ //# sourceMappingURL=data:application/ json;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0Esa0JBQWtCO0FBQzNCLFNBQVNDLGtCQU FrQjtBQUMzQixPQUFPO0FBQ1AsT0FBT0MsU0FBUzs7O0FBRWhCRCxXQUFXRSxTQUFTQyxlQUFlLE9BQU8sQ0FBRSxD QUFDQyxPQUMzQyx3QkFBQyx3QkFDQyx3QkFBQzs7OztRQUFHOzs7O1FBRVIsQ0FBQyIsIm5hbWVzIjpbIlN0cmljdE 1vZGUiLCJjcmVhdGVSb290IiwiQXBwIiwiZG9jdW1lbnQiLCJnZXRFbGVtZW50QnlJZCIsInJlbmRlciJdLCJpZ25v cmVMaXN0IjpbXSwic291cmNlcyI6WyJtYWluLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBTdHJpY3 RNb2RlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBjcmVhdGVSb290IH0gZnJvbSAncmVhY3QtZG9tL2NsaWVudCdc bmltcG9ydCAnLi9pbmRleC5jc3MnXG5pbXBvcnQgQXBwIGZyb20gJy4vQXBwLnRzeCdcblxuY3JlYXRlUm9vdChkb2 N1bWVudC5nZXRFbGVtZW50QnlJZCgncm9vdCcpISkucmVuZGVyKFxuICA8U3RyaWN0TW9kZT5cbiAgICA8QXBwIC8+ XG4gIDwvU3RyaWN0TW9kZT4sXG4pXG4iXSwiZmlsZSI6Ii9Vc2Vycy9pa2VjaGFuL2Rldi90b3lib3gvZmVjX2thbn NhaS9zcmMvbWFpbi50c3gifQ== base64をデコード!→

16.
[beta]
2 ソースマップの仕組み
ソースマップ(JSON)
{

mappings:
'AAAA,SAAS,gBAAgB;;;;AAEzB,OAAO,SAAS,MAAM;;CACpB,MAAM,CAAC,OAAO,YAAY,SAAS,EAAE;AAErC,QACE,wBAAC,mBACC,wBAAC;EAAO,eAAe,UAAU,UAAU,QAAQ,EAAE;aAAE,a
AC3C;;;;;SACH;;;;SACL',

names: [],

sources: [ 'App.tsx' ],

sourcesContent: [

'import { useState } from "react";\n' +

'\n' +

'export function App() {\n' +

' const [count, setCount] = useState(0);\n' +

'\n' +

' return (\n' +

' <div>\n' +

'
<button onClick={() => setCount((count) => count + 1)}>\n' +

'
count is {count}\n' +

'
</button>\n' +

' </div>\n' +

' );\n' +

'}\n'

],

version: 3

}

17.
[beta]
2 ソースマップの仕組み
ソースマップ(JSON)
{

mappings:
'AAAA,SAAS,gBAAgB;;;;AAEzB,OAAO,SAAS,MAAM;;CACpB,MAAM,CAAC,OAAO,YAAY,SAAS,EAAE;AAErC,QACE,wBAAC,mBACC,wBAAC;EAAO,eAAe,UAAU,UAAU,QAAQ,EAAE;aAAE,a
AC3C;;;;;SACH;;;;SACL',

names: [],

sources: [ 'App.tsx' ],

sourcesContent: [

'import { useState } from "react";\n' +

'\n' +

'export function App() {\n' +

' const [count, setCount] = useState(0);\n' +

'\n' +

' return (\n' +

' <div>\n' +

'
<button onClick={() => setCount((count) => count + 1)}>\n' +

'
count is {count}\n' +

'
</button>\n' +

' </div>\n' +

' );\n' +

'}\n'

],

version: 3

}

ソースコード本体(optional)

含まれない場合もある

18.
[beta]
2 ソースマップの仕組み
ソースマップ(JSON)
{

mappings:
'AAAA,SAAS,gBAAgB;;;;AAEzB,OAAO,SAAS,MAAM;;CACpB,MAAM,CAAC,OAAO,YAAY,SAAS,EAAE;AAErC,QACE,wBAAC,mBACC,wBAAC;EAAO,eAAe,UAAU,UAAU,QAAQ,EAAE;aAAE,a
AC3C;;;;;SACH;;;;SACL',

names: [],

sources: [ 'App.tsx' ],

sourcesContent: [

'import { useState } from "react";\n' +

'\n' +

'export function App() {\n' +

' const [count, setCount] = useState(0);\n' +

'\n' +

' return (\n' +

' <div>\n' +

'
<button onClick={() => setCount((count) => count + 1)}>\n' +

'
count is {count}\n' +

'
</button>\n' +

' </div>\n' +

' );\n' +

'}\n'

],

version: 3

}

ファイル名

バンドルされている場合は、バンドル元のファイルが複数並ぶ

19.
[beta]
2 ソースマップの仕組み
ソースマップ(JSON)
{

mappings:
'AAAA,SAAS,gBAAgB;;;;AAEzB,OAAO,SAAS,MAAM;;CACpB,MAAM,CAAC,OAAO,YAAY,SAAS,EAAE;AAErC,QACE,wBAAC,mBACC,wBAAC;EAAO,eAAe,UAAU,UAAU,QAAQ,EAAE;aAAE,a
AC3C;;;;;SACH;;;;SACL',

names: [],

sources: [ 'App.tsx' ],

sourcesContent: [

'import { useState } from "react";\n' +

'\n' +

'export function App() {\n' +

' const [count, setCount] = useState(0);\n' +

'\n' +

' return (\n' +

' <div>\n' +

'
<button onClick={() => setCount((count) => count + 1)}>\n' +

'
count is {count}\n' +

'
</button>\n' +

' </div>\n' +

' );\n' +

'}\n'

],

version: 3

}

mappings: 謎の文字列

20.

2 ソースマップの仕組み ソースマップ: mappings mappings: 'AAAA,SAAS,gBAAgB;;;;AAEzB,OAAO,SAAS,MAAM;;CA CpB,MAAM,CAAC,OAAO,YAAY,SAAS,EAAE;AAErC,QACE, wBAAC,mBACC,wBAAC;EAAO,eAAe,UAAU,UAAU,QAAQ,EA AE;aAAE,aAC3C;;;;;SACH;;;;SACL',

21.

2 ソースマップの仕組み mappingsは何を表しているの?

22.

3 ソースマップの仕組み ソースマップの基本的な考え方 シンボル (変数、予約語、記号、etc...)単位でマッピングを持つ。 変換前の行・列 → 変換後の行・列 の4つの整数がベース。 function calc(price: number, tax: number){ const result = Math.floor(price * tax) console.log(`Answer is ${result}`) return result } function calc(price, tax){ const result = Math.floor(price * tax) console.log(`Answer is ${result}`) return result }

23.

3 ソースマップの仕組み ソースマップの基本的な考え方 シンボル単位でマッピングを持つ。 シンボルごとに、変換前の行・列 → 変換後の行・列、の4個の要素を持つ function calc(price: number, tax: number){ const result = Math.floor(price * tax) console.log(`Answer is ${resut}`) 1行目の29文字目 return result } 1 29 function calc(a, b){ const c = Math.floor(a * b) console.log(`Answer is ${c}`) return c }

24.

3 ソースマップの仕組み ソースマップの基本的な考え方 シンボル単位でマッピングを持つ。 シンボルごとに、変換前の行・列 → 変換後の行・列、の4個の要素を持つ function calc(price: number, tax: number){ const result = Math.floor(price * tax) console.log(`Answer is ${result}`) return result } function calc(a, b){ const c = Math.floor(a * b) console.log(`Answer is ${c}`) 1行目の17文字目 return c } 1 29 1 17

25.

3 ソースマップの仕組み ソースマップの基本的な考え方 シンボル単位でマッピングを持つ。 シンボルごとに、変換前の行・列 → 変換後の行・列、の4個の要素を持つ function calc(price: number, tax: number){ const result = Math.floor(price * tax) console.log(`Answer is ${result}`) return result } マッピングが完成! function calc(a, b){ const c = Math.floor(a * b) console.log(`Answer is ${c}`) return c } 1 29 1 21

26.

3 ソースマップの仕組み ソースマップの基本的な考え方 シンボル単位でマッピングを持つ。 シンボルごとに、変換前の行・列 → 変換後の行・列、の4個の要素を持つ function calc(price: number, tax: number){ function calc(a, b){ const result = Math.floor(price * tax) const c = Math.floor(a * b) console.log(`Answer is ${result}`) console.log(`Answer is ${c}`) return result return c 変換前の行番号は後から導出可能なので削除 } } 29 1 21

27.

3 ソースマップの仕組み ソースマップの基本的な考え方 シンボル単位でマッピングを持つ。 シンボルごとに、変換前の行・列 → 変換後の行・列、の4個の要素を持つ 加えて、変換前のファイル名・ function calc(price: number, tax: number){ const result = Math.floor(price * tax) console.log(`Answer is ${result}`) return result } function calc(a, b){ const c = Math.floor(a * b) console.log(`Answer is ${c}`) return c } 変換前の"名前" 29 1 21 "tax"

28.

3 ソースマップの仕組み ソースマップの基本的な考え方 シンボル単位でマッピングを持つ。 シンボルごとに、変換前の行・列 → 変換後の行・列、の4個の要素を持つ 加えて、変換前のファイル名・変換前の名前(option) function calc(price: number, tax: number){ const result = Math.floor(price * tax) console.log(`Answer is ${result}`) return result } 変換前のファイル名 function calc(a, b){ const c = Math.floor(a * b) console.log(`Answer is ${c}`) return c } 29 "calc.ts" 1 21 "tax"

29.

3 ソースマップの仕組み ソースマップの基本的な考え方 5つの要素で、特定のトークン(tax)のマッピングを表す 29 "calc.ts" 1 21 "tax" 何文字目(変換前) 何文字目(変換後) 何行目(変換後)

30.

3 ソースマップの仕組み ソースマップの基本的な考え方 他のシンボルに対しても同様に計算していく function calc(price: number, tax: number){ const result = Math.floor(price * tax) console.log(`Answer is ${result}`) return result } function calc(a, b){ const c = Math.floor(a * b) console.log(`Answer is ${c}`) return c } 14 "calc.ts" 1 14 "price" 29 "calc.ts" 1 21 "tax"

31.

3 ソースマップの仕組み ソースマップの基本的な考え方 lc" 他のシンボルに対しても同様に計算していく 同様に、2行目・3行目...に対しても計算 function calc(price: number, tax: number){ const result = Math.floor(price * tax) console.log(`Answer is ${result}`) return result } 14 "calc.ts" 1 14 "price" function calc(a, b){ const c = Math.floor(a * b) console.log(`Answer is ${c}`) return c } 29 "calc.ts" 1 21 "tax" 4

32.

3 ソースマップの仕組み ソースマップの基本的な考え方 文字列は切り出して、配列のIndexとして保持 0 "calc.ts" 0 0 9 "calc.ts" 1 9 "calc" 14 "calc.ts" 1 14 "price" 29 "calc.ts" 1 21 "tax" 41 "calc.ts" 1 19

33.

3 ソースマップの仕組み ソースマップの基本的な考え方 文字列は切り出して、配列のIndexとして保持 0 "calc.ts" 0 0 9 "calc.ts" 1 9 "calc" 14 "calc.ts" 1 14 "price" 29 "calc.ts" 1 21 "tax" 41 "calc.ts" 1 19 同じ文字列がたくさん = 無駄なので容量を減らしたい

34.

3 ソースマップの仕組み ソースマップの基本的な考え方 文字列は切り出して、配列のIndexとして保持 0 "calc.ts" 0 0 9 "calc.ts" 1 9 "calc" 14 "calc.ts" 1 14 "price" 29 "calc.ts" 1 21 "tax" 41 "calc.ts" 1 19 sources: ["calc.ts"]

35.

3 ソースマップの仕組み ソースマップの基本的な考え方 文字列は切り出して、配列のIndexとして保持 0 9 14 29 41 0 0 0 0 0 0 0 1 9 "calc" 1 14 "price" 1 21 "tax" 1 19 sources: ["calc.ts"]

36.

3 ソースマップの仕組み ソースマップの基本的な考え方 文字列は切り出して、配列のIndexとして保持 0 9 14 29 41 0 0 0 0 0 0 0 1 9 "calc" 1 14 "price" 1 21 "tax" 1 19 sources: ["calc.ts"]

37.

3 ソースマップの仕組み ソースマップの基本的な考え方 文字列は切り出して、配列のIndexとして保持 0 9 14 29 41 0 0 0 0 0 0 0 1 9 "calc" 1 14 "price" 1 21 "tax" 1 19 sources: ["calc.ts"] names: ["calc","price","tax"]

38.

3 ソースマップの仕組み ソースマップの基本的な考え方 文字列は切り出して、配列のIndexとして保持 0 9 14 29 41 0 0 0 0 0 0 0 1 9 0 1 14 1 1 21 2 1 19 sources: ["calc.ts"] names: ["calc","price","tax"]

39.

3 ソースマップの仕組み ソースマップの基本的な考え方 function 0 0 0 0 calc 90190 price 14 0 1 14 1 tax 29 0 1 21 2 { 41 0 1 19 // 2行目以降のデータが続く mappings: 'AAAA,SAAS,gBAAgB;;;;AAEzB,OA CpB,MAAM,CAAC,OAAO,YAAY,SAAS, wBAAC,mBACC,wBAAC;EAAO,eAAe,U AE;aAAE,aAC3C;;;;;SACH;;;;SAC 圧縮して文字列結合

40.

3 ソースマップの仕組み 容量を削減する ソースマップの課題は容量の大きさ。 いかに効率よくエンコーディングするか。 面白い点を2つ紹介。 工夫1: 差分符号化 工夫2: Base64 VLQ 0000 90190 14 0 1 14 1 29 0 1 21 2 41 0 1 19 圧縮して文字列結合

41.

3 ソースマップの仕組み 差分符号化 29 0 1 21 2 各フィールドが行数や文字数を表す 都合上、大きなファイルだとどんど ん値が巨大に... 何文字目(変換後) 何行目(変換後) 何文字目(変換前) (ex. 3000行目の500文字目...)

42.

3 ソースマップの仕組み 差分符号化 12 0 1 14 1 1つ前のセグメント +17 +0 +0 +7 +1 差分 29 0 1 21 2 現在のセグメント 直前のセグメントからの 差分で表現する 巨大なファイルでも小さ な数字で表現可能。 隣り合ったシンボルは変換前 後でも隣り合う可能性高いの で、効率が良い。

43.

3 ソースマップの仕組み 差分符号化 12 0 1 14 1 +17 +0 +0 +7 +1 直前のセグメントからの 差分で表現する 巨大なファイルでも小さ な数字で表現可能。 隣り合ったシンボルは変換前 後でも隣り合う可能性高いの 差分を採用 で、効率が良い。

44.

3 ソースマップの仕組み Base64 VLQ (VLQ = 可変長な数量) 小さい整数はアルファベット1文字で、大きな整数は複数文字で表現する。 (整数によってエンコード後の文字数が異なるので可変長) 最大の整数に合わせて桁数を決定するよりも効率よく圧縮できる 小さな整数 大きな整数 +0 +29 A 6B 0A ではない!

45.

3 ソースマップの仕組み Base64 VLQ (VLQ = 可変長な数量) 小さい整数の場合: +0 2進表現 0 00000 0= A 絶対数 符号bit 文字にマッピング(Base64) +1 2進表現 0 00010 2= C ↑継続bit(次ページで説明)

46.

3 ソースマップの仕組み Base64 VLQ (VLQ = 可変長な数量) 大きい整数の場合: +29 000011101 2進表現 00001 1101 前半と後半で分割 0 00001 1 1101 0 継続bit(分割されている印) 2= B 58= 6 6B

47.

3 ソースマップの仕組み Base64 VLQ (VLQ = 可変長な数量) 全体の変換の流れ 29 0 1 21 2 000011101 0000 0001 000010101 0010 111010 000001 000000 000010 101010 000001 000100 6 29 B A 0 C 1 q 21 B E 2

48.

3 ソースマップの仕組み Base64 VLQ (VLQ = 可変長な数量) 全体の変換の流れ 29 0 1 21 2 000011101 0000 0001 000010101 0010 111010 000001 000000 000010 101010 000001 000100 6 29 B A 0 C 1 q 21 B E 2

49.

3 ソースマップの仕組み ソースマップ: mappings mappings: 'AAAA,SAAS,gBAAgB;;;;AAEzB,OAAO,SAAS,MAAM;;CA CpB,MAAM,CAAC,OAAO,YAAY,SAAS,EAAE;AAErC,QACE, wBAAC,mBACC,wBAAC;EAAO,eAAe,UAAU,UAAU,QAAQ,EA AE;aAAE,aAC3C;;;;;SACH;;;;SACL',

50.

3 さいごに Source map format specification(ECMA-426) ソースマップの仕様はECMA Internationalによって定められている。 歴史的にはv1, v2があり、今はv3 v2からv3になったことで圧縮効率が良くなっている。 JavaScriptがメインのユースケースではありつつ、CSSや WebAssemblyでのコメントの書き方も定義されている。 ECMA-426: https://tc39.es/ecma426 ECMA-426 CSS: https://tc39.es/ecma426/#sec-CSSExtractSourceMapURL ECMA-426 WebAssembly: https://tc39.es/ecma426/#sec-WebAssemblyExtractSourceMapURL

51.

3 さいごに Tips Chromeは手動でソースマップをアップロードする機能を提供している 本番環境などソースマップを配布できない環境で、Datadogのような外 部ツールに頼らずデバッグができるそう(まだ試せてない ) https://developer.chrome.com/docs/devtools/developer-resources?hl=ja Visualization Tools ソースマップつきのビルド済みファイルをアップロードすると可視化して くれるツール https://sokra.github.io/source-map-visualization/#custom

52.

3 さいごに ソースマップとdebugger ソースマップは単に行数を変換するだけでなく、debuggerにも関連する。 TypeScriptファイル上でdebuggerを設定できるのはソースマップがあるから。

53.

3 さいごに 現在のソースマップの改善点 現在のソースマップでは、minifyやoptimizerによって削除/インライン化され た関数や変数の扱いが難しい。 これを解決するために、scopeという概念を追加することが議論されている。 // ビルド前 const getName = (name) => `${name}さん` console.log(getName("Yuta")) // ビルド後 console.log("Yutaさん") https://github.com/tc39/ecma426/blob/main/proposals/scopes.md

54.

3 参考リンク So urce map format specification, https://tc39.es/ecma426 tc39/ecma426: Source map specification, RFCs and new proposals., https://github.com/tc39/ecma426 Proposal for adding information about scopes and their bindings to source maps, https://github.com/tc39/ ecma426/blob/main/proposals/scopes.md source-map-visualization, https://sokra.github.io/source-map-visualization/#custom Source map - Glossary | MDN, https://developer.mozilla.org/en-US/docs/Glossary/Source_map JavaScript ソースマップの概要 | Blog | Chrome for Developers, https://developer.chrome.com/blog/ sourcemaps Build Options | Vite, https://vite.dev/config/build-options#build-sourcemap デベロッパー リソース: ソースマップの表示と手動読み込み | Chrome DevTools | Chrome for Developers, https:// developer.chrome.com/docs/devtools/developer-resources Source Mapの仕組み, https://keichi.dev/post/decoding-source-maps/ What are source maps? | Articles | web.dev, https://web.dev/articles/source-maps Variable-length quantity - Wikipedia, https://en.wikipedia.org/wiki/Variable-length_quantity ソースマップのことを何もわかっていなかったので調べた | nabeliwo blog, https://www.nabeliwo.blue/blog/2022/09/ source-map Base64 VLQ 概要, https://speakerdeck.com/rchaser53/base64-vlqgai-yao