多段SourceMapの対応方法とライブラリ
Source Map
SourceMapとは何かについては以前、Source Mapを扱う関連ライブラリのまとめにて紹介しました。
Source Mapとは
- Source Map Revision 3 Proposal – Google ドキュメント
- SourceMap仕様
- #JSオジサンで Source Map について話してきました : document
- SourceMap概要
- JavaScriptのSource Mapの内部表現について
- Base64の
mappings
部分の仕組み - source-map-visualization ビジュアライズツール
- Base64の
- mozilla/source-map
- SourceMapのコアと言えるモジュール(色々なモジュールが使う)
SourceMapはAltJS等からJavaScriptへの変換など、Original Code -> Generated Codeへと変換する時に、 変換後のファイルから、変換前のファイルの該当する場所(Line numberとColumn)をたどるための情報が入った マッピングファイルです。
変換後のコードと変換前のコードの位置関係を参照できるので、SassやCoffeeScriptやTypeScript等のデバッグに使われていますね。
多段SourceMap(Multi-level SourceMap)とは
仕様書のNOTE部分にも書いてありますが、現在の仕様では以下のような複数回の変換を経由した時の標準的な仕組みは存在していません。
例えば、CoffeeScriptで書いて(Original)、それをJavaScriptに変換して(Generated)、さらにそれを圧縮した(Minified)時などが該当します。 このように変換とSourceMapの生成を複数回繰り返して行った場合の事を多段SourceMapと呼んでると思います。
この場合、圧縮したコードから、元のCoffeeScriptのコードの対応関係をそのままだと見ることが出来ません。 (例としてMinifiedなどとしていますが、これはSourceMapを生成する変換なら何でもいいです。テンプレートの変換やConcatも同じです)
多段SourceMapについては仕様上のサポートがないので、現状をまとめると以下のような感じですね。
- 圧縮したコード(Minified)から変換されたJS(Generated)のSourceMapはある
- 変換されたJS(Generated)からCoffeeScript(Original)のSourceMapはある
- 圧縮したコード(Minified)からCoffeeScript(Original)のSourceMapがない
multi-stage-sourcemap
圧縮したコード(Minified)からCoffeeScript(Original)のSourceMapがない
先ほどの図から想像できると思いますが、中間のそれぞれのSourceMapは存在しているため、Minified -> Original というSourceMapを作ることは容易だと分かりますね。
これを行うのモジュールとして multi-stage-sourcemap というものを作りました。
中間の2つのSourceMapを使って、Minified -> OriginalというジャンプしたSourceMapを作ることが出来ます。
つまり、最初と最後だけを繋いだSourceMapを作り直すだけです。
これは、仕様書のNOTEで中間地点の情報は失うけど簡単な方法として書かれています。
The easy but lossy way is to ignore the intermediate steps in the process for the purposes of debugging, the source location information from the translation is either ignored (the intermediate translation is considered the “Original Source”) or the source location information is carried through (the intermediate translation hidden). - Source Map Revision 3 Proposal
自分も仕様見る前に多分出来るんだと思ってたので、それのProof of conceptとして作りました。(探したのですが何故か汎用的なものがなかった)
先ほどの例であげている圧縮に関してはUglifyJS2が同様の実装をしています。
ユースケース
汎用的なものがなかったのは、この多段SourceMapが必要な状況に遭遇するケースが少なかったからかもしれません。
幾つかユースケースをあげておきます。
- AltJS -> JavaScript -> 圧縮.js
- 例に上げている一番遭遇するパターン
- UglifyJS2は対応済み、esmangle等も multi-stage-sourcemapのような仕組みを使えば対応できる
- AltJS + power-assert
- power-assert は用意されてテストコードを変換し、テストが失敗した時に分かりやすい情報が出せるようにしている。
- そのため、AltJSで書いたものを利用する場合はAltJS -> JavaScript -> power-assert化されたコードとなる
- power-assert 0.9.0 にて多段SourceMapに対応した
- 実際にGrunt、Gulp、Browserify、モジュールで多段SourceMapの対応する方法についてはpower-assert 多段 SourceMap 対応の方針を参照
- ES6 -> ES5 -> Spy-js
- ES6で書いたコードをES5で動くように変換したものをspy-jsでプロファイリングするときに有用です。
- Spy-jsはJavaScriptのコードをプロファイリングするために、計測用の関数を仕込んだ形に変換してから実行しています。
- AltJS -> JS -> instrument.js という感じです。
- AltJS -> JavaScript -> コードカバレッジ
- 思いついただけ
- テンプレート + JS -> 圧縮
- 一般的にはテンプレートとJSを一緒に変換する(BrowserifyやWebPack等)ので、問題ない
- ただ、テンプレートをJSとして変換済みのものを用意して、concatして、それを圧縮したいとなった場合、元のテンプレートへの参照には多段SourceMapが必要かも
おわりに
SourceMapにおける多段SourceMap(Multi-level SourceMap)が必要になる状況とその解決方法の一つを提供するazu/multi-stage-sourcemapについて紹介しました。
multi-stage-sourcemap はAPIがイマイチ(パラメータの名前がどっちがわかりにくい)なので、BREAKING CHANGEレベルのContributingも募集しています。
実際にmulti-stage-sourcemapを使った実装については以下を参照するといいと思います。
お知らせ欄
JavaScript Primerの書籍版がAmazonで購入できます。
JavaScriptに関する最新情報は週一でJSer.infoを更新しています。
GitHub Sponsorsでの支援を募集しています。