packageのバージョンアップに伴い、Next.jsを12.0.8にアップデートしました。しかし、今まで動いたソースコードがruntimeエラーになり、動かなくなりました。エラーの内容はこれです。
Unhandled Runtime Error
TypeError: Cannot read properties of undefined (reading '_className')
軽く調べてみると気づいたが、class中のfunctionにthis
がundefinedになっているので、this.xxx
にアクセスできなくなりました。そして、create-next-appで新しいプロジェクトを作って試してみても同じエラーが出ているので、これはNext.jsのバグだとわかりました。しかし、一体どこに問題があり、どう解決すればいいですか。自分のアプローチを紹介します。
アプローチ
1. create-next-appで新しいプロジェクトを作る
シンプルなアプリケーションを作って、Next.js以外の問題を排除できるでしょう。
2. 問題発生したpackage.jsonと照合する
完全に再現するため、各ライブラリのバージョンを一致させる必要があります。
3. Next.jsを起動し、バンドル済みのソースコードを確認する
開発モードのソースコードは下記のフォルダにあります。
.next/static/chunks/xxx
自分の場合、index.tsx
の中に、mockClient.ts
を参照しています。mockClient.ts
にthis.xxx
のundefinedエラーが出ているので、.next/static/chunks/pages/index.js
を探せばいいでしょう。
- developモードでは、アクセスしないとビルドが走らないので、まず、
http://localhost:3000/
にアクセスします。 - そして、
.next/static/chunks/pages/index.js
中mockClient
の部分を取り出しましょう。
めちゃわかりづらいものとなります。
整形してみましょう。問題となるfunctionは18行目〜36行目のものとなります。
function MockClient() {
var className = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "MockClient";
_classCallCheck(this, MockClient);
this.getByArrowFuncWithoutParam = _asyncToGenerator(_Users_shinjo_Study_next_bug_1208_node_modules_next_dist_compiled_regenerator_runtime_runtime_js__WEBPACK_IMPORTED_MODULE_0___default().mark(function _callee() {
return _Users_shinjo_Study_next_bug_1208_node_modules_next_dist_compiled_regenerator_runtime_runtime_js__WEBPACK_IMPORTED_MODULE_0___default().wrap(function _callee$(_ctx) {
while(1)switch(_ctx.prev = _ctx.next){
case 0:
_ctx.next = 2;
return Promise.resolve("".concat(this._className, ": getByArrowFunc"));
case 2:
return _ctx.abrupt("return", _ctx.sent);
case 3:
case "end":
return _ctx.stop();
}
}, _callee, this);
}).bind(this)).bind(this);
this.getByArrowFunc = (function() { var _ref = _asyncToGenerator(_Users_shinjo_Study_next_bug_1208_node_modules_next_dist_compiled_regenerator_runtime_runtime_js__WEBPACK_IMPORTED_MODULE_0___default().mark(function _callee(name) { return _Users_shinjo_Study_next_bug_1208_node_modules_next_dist_compiled_regenerator_runtime_runtime_js__WEBPACK_IMPORTED_MODULE_0___default().wrap(function _callee$(_ctx) { while(1)switch(_ctx.prev = _ctx.next){ case 0: _ctx.next = 2; return Promise.resolve("[".concat(this._className, " ~ ").concat(name, "]: getByArrowFunc")); case 2: return _ctx.abrupt("return", _ctx.sent); case 3: case "end": return _ctx.stop(); } }, _callee, this); }).bind(this)).bind(this); return function(name) { return _ref.apply(this, arguments); }; })(); this._className = className;
}
_createClass(MockClient, [
{
key: "getByNormalFunc",
value: function getByNormalFunc(name) {
return _asyncToGenerator(_Users_shinjo_Study_next_bug_1208_node_modules_next_dist_compiled_regenerator_runtime_runtime_js__WEBPACK_IMPORTED_MODULE_0___default().mark(function _callee() {
return _Users_shinjo_Study_next_bug_1208_node_modules_next_dist_compiled_regenerator_runtime_runtime_js__WEBPACK_IMPORTED_MODULE_0___default().wrap(function _callee$(_ctx) {
while(1)switch(_ctx.prev = _ctx.next){
case 0:
_ctx.next = 2;
return Promise.resolve("[".concat(this._className, " ~ ").concat(name, "]: getByNormalFunc"));
case 2:
return _ctx.abrupt("return", _ctx.sent);
case 3:
case "end":
return _ctx.stop();
}
}, _callee, this);
}).bind(this))();
}
}
]);
return MockClient;
}
正直相変わらずわかりづらいですが、}).bind(this)).bind(this);
をみた瞬間でやばい感じでしょう。
そこで問題を気づきました。
}).bind(this)).bind(this);
return function(name) { return _ref.apply(this, arguments);};
functionを新しく作ってreturn
ため、IIFEにあるthis
がundefinedになってしまいました。
return Promise.resolve("[".concat(this._className, " ~ ").concat(name, "]: getByArrowFunc"));
4. 結論
ここまでわかってきました。これはNext.jsの新しいバンドルswc
のバグです。issueを探してみて見つけました。
https://github.com/swc-project/swc/pull/3252
ひどいバグですね。正直swcにちょっとがっかりですが、頑張ってほしいです。
完了
変なバグが現れたら、バンドル済みのソースコードを分析するのがちょっと面倒かもしれないが、答えを知るための近道でもあります。ご参考になれば幸いです。
再現プロジェクトはこちらです。
(1, 3番に問題ありません。2. Request by getByArrowFuncを押してエラーを確認してみてください)