見出し画像

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を追加で設定する。

Recommended choice for production builds with high quality SourceMaps.

と書かれている事・このプロジェクトでは開発と本番ビルドの設定は同じにしているという事から、 "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 後のソースマップされたファイルのパス・行番号を、元のファイルのパス・行番号に書き換えるという事をしてくれる。

It uses the source-map module to replace the paths and line numbers of source-mapped files with their original paths and line numbers.

導入は簡単で、 "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 を読みやすくする設定をやってみた。これで開発中に起きるエラーの原因特定が格段にしやすくなる。また、本番環境でエラーが起きた時にどこでエラーになっているのか?を追えないと困るので上記のような設定は必要になる。

_________________________________

執筆者プロフィール:Katayama Yuta
SaaS ERPパッケージベンダーにて開発を2年経験。 SHIFTでは、GUIテストの自動化やUnitテストの実装などテスト関係の案件に従事したり、DevOpsの一環でCICD導入支援をする案件にも従事。 最近開発部門へ異動し、再び開発エンジニアに。座学で読み物を読むより、色々手を動かして試したり学んだりするのが好きなタイプ。

お問合せはお気軽に
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/