webpack×Babelでコード変換(トランスパイル)しているNode.jsのstack traceを読みやすくする
はじめに
こんにちは、SHIFT の開発部門に所属しているKatayamaです。
Node.js で ES6 の構文を使うために、webpack × Babel でコード変換(トランスパイル)している場合、エラーが発生すると以下のように stack trace からどこでエラーが発生したのか?を判断しにくいという問題があります。具体的には、"at eval (webpack://my-webpack-project/./src/index.js?:10:9)"とログが出力されており、トランスパイル後のファイルのパス・ファイル位置になっているため、実装箇所だとどこでエラーが発生しているのか?の判断が難しくなります。
Error: Data Not Found
at eval (webpack://my-webpack-project/./src/index.js?:10:9)
at Layer.handle [as handle_request] (/root/workspace/node-express/node_modules/express/lib/router/layer.js:95:5)
at next (/root/workspace/node-express/node_modules/express/lib/router/route.js:137:13)
at Route.dispatch (/root/workspace/node-express/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/root/workspace/node-express/node_modules/express/lib/router/layer.js:95:5)
at /root/workspace/node-express/node_modules/express/lib/router/index.js:281:22
at Function.process_params (/root/workspace/node-express/node_modules/express/lib/router/index.js:335:12)
at next (/root/workspace/node-express/node_modules/express/lib/router/index.js:275:10)
at jsonParser (/root/workspace/node-express/node_modules/body-parser/lib/types/json.js:110:7)
at Layer.handle [as handle_request] (/root/workspace/node-express/node_modules/express/lib/router/layer.js:95:5)
※理想としては以下のようにトランスパイルする前のコードのどの部分でエラーが発生しているのか?すぐに分かるような状態です。
Error: Data Not Found
at /root/workspace/node-express/dist/webpack:/node-express/src/index.js:9:8
at Layer.handle [as handle_request] (/root/workspace/node-express/node_modules/express/lib/router/layer.js:95:5)
...
そこで今回はエラーが発生した時に、その stack trace の出力のされ方をエラー発生個所がすぐに特定できるように変える設定をやっていきたいと思います。
※Node.js で ES Modules を利用するための設定についてはNode.js で import・export(ES6 の構文)を使えるように webpack × Babel の設定をやってみたを参照ください。
トランスパイルしている時の stack trace を改善する
結論:どうすればいいか?
// webpack.config.js
...
module.exports = {
...
devtool: 'source-map', // ←これを追加
entry: {
index: './src/index.js'
},
...
};
// index.js
import 'source-map-support/register'; // ←importを追加
...
エラーが発生するコード全体は以下。
// index.js
import "source-map-support/register";
import express from "express";
const app = express();
app.use(express.json());
// eslint-disable-next-line no-unused-vars
app.get("/", (req, res) => {
throw new Error("Data Not Found");
});
// eslint-disable-next-line no-unused-vars
app.use((err, req, res, next) => {
console.log(err);
res.status(500).send();
});
app.listen(3000, () => console.log("listening on port 3000!"));
以下で詳細についてみていく。
webpack の devtools の設定を追加する
まずは webpack のDevtoolを追加で設定する。
と書かれている事・このプロジェクトでは開発と本番ビルドの設定は同じにしているという事から、 "devtool: 'source-map'" を設定する。この設定を追加した上で webpack で build をし node コマンドで実装してみると、以下のような stack trace に変わる。
Error: Data Not Found
at /root/workspace/node-express/dist/index.js:97:9
at Layer.handle [as handle_request] (/root/workspace/node-express/node_modules/express/lib/router/layer.js:95:5)
at next (/root/workspace/node-express/node_modules/express/lib/router/route.js:137:13)
...
// dist/index.js 一部のみ
...
const app = express__WEBPACK_IMPORTED_MODULE_0___default()();
app.use(express__WEBPACK_IMPORTED_MODULE_0___default().json()); // eslint-disable-next-line no-unused-vars
app.get('', (req, res) => {
throw new Error('Data Not Found'); // ←ここが "index.js:97:9" の部分
}); // eslint-disable-next-line no-unused-vars
app.use((err, req, res, next) => {
console.log(err);
res.status(err.status || 500).json({
message: err.message,
errors: err.errors
});
});
app.listen(3000, () => console.log('listening on port 3000!'));
})();
/******/ })()
;
//# sourceMappingURL=index.js.map
これでも『はじめに』でみた stack trace に比べると格段にどこでエラーが起きているのか?が分かりやすくはなっているが、trace に出ているファイルが build 後のファイルになっているので、これを実装しているファイルになるようにさらに追加で設定をする。
※最初のログではeval()関数でのエラーログが出力されており、eval()関数の引数に渡っているコード(圧縮されたもの)を見る必要があったが、今回はトランスパイル前の元のコードと全く同じ実装をしている箇所がトランスパイル後のコード内で見つけられる状態になっており、どこでエラーが発生しているか?の特定がしやすくなっている。
Source Map Supportを導入する
Source Map Supportはbuild 後のソースマップされたファイルのパス・行番号を、元のファイルのパス・行番号に書き換えるという事をしてくれる。
導入は簡単で、 "yarn add source-map-support" のようにライブラリを依存に追加して、 "import 'source-map-support/register';" を webpack の entry にしているファイルの先頭に追記すればいい。追記した後でエラーの stack trace を見てみると、
Error: Data Not Found
at /root/workspace/node-express/dist/webpack:/node-express/src/index.js:9:8
at Layer.handle [as handle_request] (/root/workspace/node-express/node_modules/express/lib/router/layer.js:95:5)
at next (/root/workspace/node-express/node_modules/express/lib/router/route.js:137:13)
...
// src/index.js 一部のみ
...
// eslint-disable-next-line no-unused-vars
app.get('/', (req, res) => {
throw new Error('test'); // ←ここが"index.js:9:8"の部分
});
...
のように、元のファイルのどこでエラーが出ているか?が分かるようになる。
※ちなみに、上記の stack trace の "/root/workspace/node-express/dist/webpack:/node-express/src/index.js:9:8" の部分は、VS Code 上でクリックしてそのファイルの当該箇所に飛ぶという事ができない形式で出力されており、手動で "src/index.js:9:8" のように書き換えないといけない・・・。これはFormat the output so it can be clickedで Issue として挙げられている。
まとめとして
今回は webpack の source-map とSource Map Supportを使って stack trace を読みやすくする設定をやってみた。これで開発中に起きるエラーの原因特定が格段にしやすくなる。また、本番環境でエラーが起きた時にどこでエラーになっているのか?を追えないと困るので上記のような設定は必要になる。
_________________________________
お問合せはお気軽に
https://service.shiftinc.jp/contact/
SHIFTについて(コーポレートサイト)
https://www.shiftinc.jp/
SHIFTのサービスについて(サービスサイト)
https://service.shiftinc.jp/
SHIFTの導入事例
https://service.shiftinc.jp/case/
お役立ち資料はこちら
https://service.shiftinc.jp/resources/
SHIFTの採用情報はこちら
https://recruit.shiftinc.jp/career/