ソースマップのビジュアライザをテスト基盤に追加
ソースマップの検証をテキストの座標比較から、入出力を並べたビジュアル表示に切り替えることで、ソースマップのデバッグとスナップショットテストの精度検証が大幅に行いやすくなりました。
背景
ソースマップのテストが、人間にとって解読困難な座標の羅列になっていたことが今回の変更のきっかけです。関連PR #19996 において @variant 処理に関するソースマップテストが追加されましたが、生成された座標(例: 3:0-3:5 <- 1:2-1:7)を目視で確認するには、入力ファイルと出力ファイルを頭の中で照らし合わせる必要がありました。
座標を並べるだけでは「正しい座標が記録されているか」を直感的に判断できず、レビューコストが高い状態でした。特にコンパイル後のCSSが長くなるほど、どの行のどの文字に対応しているかを追うのは困難になります。
技術的な変更
visualize-source-map.ts という新しいモジュールが追加され、既存のテストヘルパー formattedMappings() がこのモジュールに置き換えられました。
変更前は source-map.test.ts 内に定義された formattedMappings() 関数が SourceMapConsumer を直接呼び出し、各マッピングを dst <- src 形式の文字列として返していました。
変更前:
function formattedMappings(map: RawSourceMap) {
const smc = new SourceMapConsumer(map)
const annotations: Record<number, { ... }> = {}
smc.eachMapping((mapping) => {
let annotation = (annotations[mapping.generatedLine] = ...)
// ...
})
// returns string[] like ["3:0-3:5 <- 1:2-1:7"]
}
変更後:
import { visualizeSourceMap } from './visualize-source-map'
// ...
let annotations = visualizeSourceMap(map, css)
新しい visualize-source-map.ts は2つの公開関数を提供します。
-
visualizeSourceMap(map, css):RawSourceMapを受け取り、生成されたCSSと元のソースを並べた文字列を返す。ソースマップの各マッピングポイントがA、B、C... といった記号でハイライトされる -
visualizeSourceMapRanges(sources, css, ranges):SourceMapVisualizationRange[]を受け取り、任意の範囲情報を同じ形式で可視化する
内部定数として COLUMN_WIDTH = 100 と CONTEXT_LINES = 3 が定義されており、表示幅と前後の文脈行数を制御しています。マッピングが複数のファイルにわたる場合は、それぞれのソースファイルも並べて表示されます。
translation-map.test.ts も同様に更新されており、従来の文字列配列を返す format() 関数が SourceMapVisualizationRange[] を構築して visualizeSourceMapRanges() に渡す形式に変わっています。
import { visualizeSourceMapRanges, type SourceMapVisualizationRange } from './visualize-source-map'
function format(node: AstNode) {
let ranges: SourceMapVisualizationRange[] = []
for (let [oStart, oEnd, gStart, gEnd] of translate(node)) {
ranges.push({
original: {
source: 'input.css',
start: [oStart.line, oStart.column],
end: [oEnd.line, oEnd.column],
},
generated: gStart && gEnd
? { start: [gStart.line, gStart.column], end: [gEnd.line, gEnd.column] }
: null,
})
}
return visualizeSourceMapRanges({ 'input.css': input }, css, ranges)
}
また、ビジュアライザ自体の動作を保証する visualize-source-map.test.ts も新規追加されており、単一行・複数行・複数ファイルにまたがるケースがスナップショットテストとして記録されています。
設計判断
スナップショットテストとの組み合わせを前提とした設計が採用されています。ビジュアライザの出力は人間が読みやすいテキスト形式であるため、Vitestのインラインスナップショット(toMatchInlineSnapshot)と組み合わせることで、ソースマップの変更がテストのdiffとして即座に視覚的に確認できます。
記号(A、B、C...)によるハイライト方式を採用することで、同一行内の複数マッピングポイントを区別できます。座標の数値を直接比較する方式では発見しにくかった「同じ行の別の列を指している」ケースも、記号の対応関係として明示されます。
PR本文にも記載されているとおり、この変更はテスト基盤のみを対象としており、プロダクションのソースコードには一切変更を加えていません。visualize-source-map.ts はテスト用途のモジュールとして分離されているため、バンドルサイズや実行時の動作への影響はありません。
まとめ
ソースマップのテストインフラを座標比較からビジュアル表示へ刷新したことで、マッピングの正確性をスナップショットdiffとして確認できる体制が整いました。ソースマップに変更が生じた際のレビューコストが下がり、@variant 処理の複雑化に伴うマッピング検証の精度向上に貢献します。