バージョン違いの@sentry/typesが依存にいると@sentry/browserがランタイムにクラッシュする
webpackでビルドしているWeb SPAプロジェクト。
"@sentry/browser": "6.3.5",
"@sentry/types": "5.6.1",
こんな感じの依存関係にしたら(Renovateがそうしてきた)、@sentry/browserのinit処理がクラッシュした。
Uncaught TypeError: Cannot read property 'Ok' of undefined
at new Session (webpack-internal:///cj9L:17)
at Hub.startSession (webpack-internal:///VKa5:345)
at startSessionTracking (webpack-internal:///jrvE:201)
at init (webpack-internal:///jrvE:104)
at Object.init (webpack-internal:///mAsu:92)
at eval (webpack-internal:///2YZa:113)
at Module.2YZa (main.f710ea1176bb2f6626aa.js:313)
at __webpack_require__ (runtime.f710ea1176bb2f6626aa.js:849)
at fn (runtime.f710ea1176bb2f6626aa.js:151)
at Object.0 (main.f710ea1176bb2f6626aa.js:146)
at __webpack_require__ (runtime.f710ea1176bb2f6626aa.js:849)
at checkDeferredModules (runtime.f710ea1176bb2f6626aa.js:46)
at Array.webpackJsonpCallback [as push] (runtime.f710ea1176bb2f6626aa.js:33)
at main.f710ea1176bb2f6626aa.js:1
TLDR. 対処としては次でOKだった。@sentry/types
を直接使っているところはなかったので。
"@sentry/browser": "6.3.5",
- "@sentry/types": "5.6.1",
ソースマップが有効だったのでブラウザーのdevtoolsで見てみた。
import { SessionStatus } from '@sentry/types';
import { dropUndefinedKeys, uuid4 } from '@sentry/utils';
/**
* @inheritdoc
*/
var Session = /** @class */ (function () {
function Session(context) {
this.errors = 0;
this.sid = uuid4();
this.timestamp = Date.now();
this.started = Date.now();
this.duration = 0;
this.status = SessionStatus.Ok; // ❌
this.init = true;
if (context) {
this.update(context);
}
}
なるほど SessionStatus
がundefinedだと。
@sentry/types
は @sentry/browser
の依存パッケージ。なのでyarn.lockはこうなっていた。
"@sentry/browser@6.3.5":
version "6.3.5"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-6.3.5.tgz#fc89538cf73752cd5b10060a914c6c5ece2d2893"
integrity sha512-fjkhPR5gLCGVWhbWjEoN64hnmTvfTLRCgWmYTc9SiGchWFoFEmLqZyF2uJFyt27+qamLQ9fN58nnv4Ly2yyxqg==
dependencies:
"@sentry/core" "6.3.5"
"@sentry/types" "6.3.5"
"@sentry/utils" "6.3.5"
tslib "^1.9.3"
"@sentry/types@5.6.1":
version "5.6.1"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.6.1.tgz#5915e1ee4b7a678da3ac260c356b1cb91139a299"
integrity sha512-Kub8TETefHpdhvtnDj3kKfhCj0u/xn3Zi2zIC7PB11NJHvvPXENx97tciz4roJGp7cLRCJsFqCg4tHXniqDSnQ==
"@sentry/types@6.3.5":
version "6.3.5"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.3.5.tgz#d5eca7e76c250882ab78c01a8df894a9a9ca537d"
integrity sha512-tY/3pkAmGYJ3F0BtwInsdt/uclNvF8aNG7XHsTPQNzk7BkNVWjCXx0sjxi6CILirl5nwNxYxVeTr2ZYAEZ/dSQ==
自分のpackage.jsonで指定した @sentry/types@5.6.1
と、@sentry/browser@6.3.5
の依存の @sentry/types@6.3.5
がそれぞれ存在している。
それぞれの物理ファイルの居場所はこんな感じ。
% head node_modules/@sentry/browser/node_modules/@sentry/types/package.json
{
"name": "@sentry/types",
"version": "6.3.5",
"description": "Types for all Sentry JavaScript SDKs",
"repository": "git://github.com/getsentry/sentry-javascript.git",
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/types",
"author": "Sentry",
"license": "BSD-3-Clause",
"engines": {
"node": ">=6"
% head node_modules/@sentry/types/package.json
{
"name": "@sentry/types",
"version": "5.6.1",
"description": "Types for all Sentry JavaScript SDKs",
"repository": "git://github.com/getsentry/sentry-javascript.git",
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/types",
"author": "Sentry",
"license": "BSD-3-Clause",
"engines": {
"node": ">=6"
パッケージのエントリーポイントであるindex.jsはそれぞれこんな感じ。
v5.6.1
Object.defineProperty(exports, "__esModule", { value: true });
var loglevel_1 = require("./loglevel");
exports.LogLevel = loglevel_1.LogLevel;
var severity_1 = require("./severity");
exports.Severity = severity_1.Severity;
var status_1 = require("./status");
exports.Status = status_1.Status;
//# sourceMappingURL=index.js.map
v.6.3.5
Object.defineProperty(exports, "__esModule", { value: true });
var loglevel_1 = require("./loglevel");
exports.LogLevel = loglevel_1.LogLevel;
var session_1 = require("./session");
exports.SessionStatus = session_1.SessionStatus;
var severity_1 = require("./severity");
exports.Severity = severity_1.Severity;
var status_1 = require("./status");
exports.Status = status_1.Status;
var transaction_1 = require("./transaction");
exports.TransactionSamplingMethod = transaction_1.TransactionSamplingMethod;
//# sourceMappingURL=index.js.map
違った。package.jsonにmainとmoduleそれぞれのフィールドがあったらmoduleを優先してくれるっぽい。のでこっちが使われてた(node_modules内のファイルを書き換えて確かめた)
export { LogLevel } from './loglevel';
export { Severity } from './severity';
export { Status } from './status';
//# sourceMappingURL=index.js.map
export { LogLevel } from './loglevel';
export { SessionStatus } from './session';
export { Severity } from './severity';
export { Status } from './status';
export { TransactionSamplingMethod, } from './transaction';
//# sourceMappingURL=index.js.map
ここに書いてある通りだった。問題のプロジェクトでは target
未指定だったので、browser
, module
, main
の順で拾われる。
よくみたらtarget=nodeでも module > main の優先順は変わらないのか。
@sentry/types v5.6.1
には SessionStatus
なんていないことがわかった。@sentry/browser v6.3.5
のバンドルで誤ってv5.6.1がインポートされてしまい、今回のクラッシュになったのだろう。
対処としては簡単だったからいいが、ちゃんと知っておきたい。
そのインポート(正確にはrequireか)の解決はwebpackがやっているはずだから、webpackくんがどう考えてv5.6.1のほうをチョイスしたか知る必要がある。
package.jsonの記述はそのままに、node_modules/@sentry/types
を丸ごと消したら、ビルド段階で失敗するようになった。優先順があるとかではなく、node_modulesにネストしたnode_modulesは見ていないっぽいな。
package.jsonから "@sentry/types": "5.6.1",
の記述を消した場合、@sentry/browser
の依存にある @sentry/types
が node_modules/@sentry/browser/node_modules
ではなくルートの node_modules
に置かれるようになった。これはYarnによる処理。
webpackは何も変わっていないが、パッケージファイルの置き場が変わったことで @sentry/browser
から @sentry/types
への参照がうまくいくようになっただけか。
ミニマルなプロジェクトを作ってみたところ、ネストしたnode_modulesとルートのnode_modulesをちゃんと区別してくれた。なんで・・・
いやこれ完全に自分がアホなだけだった。webpackのresolve.modules設定に次があった・・・
modules: [
path.resolve(basePath, './src'),
path.resolve(basePath, './node_modules'),
path.resolve(basePath, '../../node_modules'),
],
src → ルート(各ワークスペース)のnode_modules → ルート(ワークスペースの親玉の位置)のnode_modules という順でしか解決しない。はい解散。
引き継いだwebpack.configのことを何もわかってやってなかった。