Open13

Rollupでsourcemapの出力場所を変えたい

kazuma1989kazuma1989

モチベーション

モチベーションはSentryへのアップロード。

実行ファイルはS3にアップロードしてソースマップは隠したい。
実行ファイルとソースマップをSentryへアップロードしてSentry上ではソースを見られるようにしたい。

dist/public/xxx.js
dist/sourcemap/xxx.js.map

のように別れていれば、S3へはdist/publicを、Sentryへはdist/publicdist/sourcemapをアップロードするだけでいいため。

kazuma1989kazuma1989

解決策

  • 実行ファイルとソースマップを一箇所に出力して、mvコマンドで移動する。
  • 実行ファイルとソースマップを一箇所に出力して、S3へのアップロード時にソースマップを除外する。

それぞれ//# sourceMappingURL=index.be4ea714.js.mapのようなリファレンスコメントをどうするか(残すか消すか)問題は残る。

  • 消さない。
  • S3へのアップロード時だけ消す。
  • S3とSentryどちらのアップロード時にも消す。いわゆるhiddenソースマップ。
kazuma1989kazuma1989

消さない。

メリット

  • 簡単。

デメリット

  • ブラウザーの開発者ツールに、ソースマップが404であるとワーニングが大量に出てうるさい。
kazuma1989kazuma1989

S3へのアップロード時だけ消す。

メリット

  • Sentryのソースマップ紐付けがうまくいきやすい。標準的な方法かつ現プロジェクトで実績ありのため。
  • ブラウザーの開発者ツールにワーニングが出ない。

デメリット

  • S3とSentryのアップロードに順序性が生じたり、S3のアップロードにカスタムロジックが混じったりする。

順序性。
Sentryにアップロード後sourceMappingURLコメントを消してS3にアップロードするとなると、アップロード順序を逆にはできないということ。

カスタムロジック。
ローカルのディレクトリとS3のバケットをただ同期するだけ、といったシンプルなロジックでは対応できない。
特定拡張子のファイルを除外する、くらいだったらまだ汎用的だと言い張れるが、ファイル末尾の特定コメントを削ぎ落としてアップロードとなると厳しい。

これらのデメリットは、S3用とSentry用のアップロードファイルを別の場所に置けば解決はする。

kazuma1989kazuma1989

S3とSentryどちらのアップロード時にも消す。いわゆるhiddenソースマップ。

メリット

  • シンプル。S3とSentryどちらにも同じ実行ファイルをアップロードしているのでわかりやすい。
  • ブラウザーの開発者ツールにワーニングが出ない。

デメリット

  • Sentryがソースマップ紐付けをしてくれない可能性がある。

SentryはsourceMappingURLコメントもしくはSourcemapヘッダー(実行ファイルのHTTPレスポンスヘッダーのことだと思う)を見てソースマップを探すので、コメントを消すとなるとSourcemapヘッダー一択となる。
ただ、ファイルの中身に特定のコメントが書いてあるかと、特定のヘッダーが返ってきているかだと、デバッグは前者が楽。

kazuma1989kazuma1989

Sourcemapヘッダーはどう付与されるのだろうか

sentry-cli sourcemaps upload --helpコマンドの結果を見ると、気になるオプションがあった。

--no-sourcemap-reference
    Disable emitting of automatic sourcemap references.
    By default the tool will store a 'Sourcemap' header with minified files so that
    sourcemaps are located automatically if the tool can detect a link. If this causes
    issues it can be disabled.

sentry-cliはデフォルトでSourcemapヘッダーを保存する。
それが困るならこのオプションで無効にできる。

つまり、Sourcemapヘッダーはsentry-cliがアップロード時に付与してくれるようだ。
その処理を追えばSourcemapヘッダー方式がうまくいくかわかるかもしれない。

kazuma1989kazuma1989
  1. no-sourcemap-referenceでgrep。

https://github.com/getsentry/sentry-cli/blob/c7f85ab84752865a2f1d44339eba6a5e0d3ce794/src/commands/sourcemaps/upload.rs#L64-L74

  1. no_sourcemap_referenceでgrep。

https://github.com/getsentry/sentry-cli/blob/c7f85ab84752865a2f1d44339eba6a5e0d3ce794/src/commands/sourcemaps/upload.rs#L296-L298

  1. add_sourcemap_referencesを見つける。

https://github.com/getsentry/sentry-cli/blob/c7f85ab84752865a2f1d44339eba6a5e0d3ce794/src/utils/sourcemaps.rs#L517-L548

source.ty != SourceFileType::MinifiedSourceでないならguess_sourcemap_referenceをしてくれるらしい。

推測(ファイルが実在するか見るようだから「探索」かな)パターンは4つ。

// foo.min.js -> foo.map
// foo.min.js -> foo.min.js.map
// foo.min.js -> foo.js.map
// foo.min.js -> foo.min.map

cf. fn guess_sourcemap_reference(sourcemaps: &HashSet<String>, min_url: &str) -> Result<String> {

実行ファイル名はどうしても*.min.jsでないといけない雰囲気がある。

kazuma1989kazuma1989

source.tyを決定するのは次の箇所のようだ。if式だ。

https://github.com/getsentry/sentry-cli/blob/c7f85ab84752865a2f1d44339eba6a5e0d3ce794/src/utils/sourcemaps.rs#L203-L233

is_ram_bundle_sliceis_hermes_bytecodeなど見慣れない名前の判定関数が見えるが、これはReact Native関係のようなので、WebにJSファイルをアップロードするだけの今回は関係ないだろう。

するとこの条件がsource.ty == SourceFileType::MinifiedSourceにする(そしてguess_sourcemap_referenceをする)ための条件となる。

https://github.com/getsentry/sentry-cli/blob/c7f85ab84752865a2f1d44339eba6a5e0d3ce794/src/utils/sourcemaps.rs#L214-L222

kazuma1989kazuma1989

is_likely_minified_jsmight-be-minifiedというライブラリーのメソッドで、中身は次のようになっている。

https://github.com/mitsuhiko/might-be-minified/blob/3ffef76bcdbc4c5fe2294f3c5e0176342e9afa63/src/lib.rs#L221-L237

よくわからない。
ファイルの中身をなんとかしてsource.ty == SourceFileType::MinifiedSource判定を得るのは難しそうだ。
誤判定もあるし、判定ロジックも変わりうる。

つまりx.contains(".min.")、実行ファイルのファイル名が.min.を含むかで判定をもらうしかない。

Sourcemapヘッダーをsentry-cliにつけてほしかったら、実行ファイルはfoo.min.jsとしろということだ。