webpackでバンドルしたnodejsのファイル(人間が読めない)にエラーが発生した場合、ソースコードの場所(人間が読めるソースコード)を特定する必要があるでしょう。わかったらすごく簡単と知りながら結局時間がかかりましたので、方法を記録します。
簡単なexpressプロジェクト
極簡単なWebpack + Typescript + expressプロジェクト。
※ ソースコードはこちらへ
src/api.ts
import express from "express";
const app = express();
const port = 3000;
app.use(express.json());
app.get("/", (req, res) => {
const body = req.body;
throw Error("Error here!!");
res.send("Hello world!");
});
app.listen(port, () => {
console.log(`listening at http://localhost:${port}`);
});
http://localhost:3000
へアクセスすると、下記のエラーが吐かれます。
Error: Error here!! at /Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:93121
at s.handle_request (/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:84016)
at s (/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:82547)
at p.dispatch (/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:82569)
at s.handle_request (/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:84016)
at /Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:79523
at Function.v.process_params (/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:80405)
at w (/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:79466)
at /Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:96914
at s.handle_request (/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:84016)
エラーメッセージを読むとわかる。
at /Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:93121
意味がないstacktraceで、どこにこけたか全くわからず。
source-mapを使って特定
使うパッケージはMozillaのhttps://github.com/mozilla/source-mapとなります。
探すのが大変だったが、実際に使う関数はhttps://github.com/mozilla/source-map#sourcemapconsumerprototypeoriginalpositionforgeneratedpositionとなります。
※ importを使うため、node@16が必要です。
import { SourceMapConsumer } from "source-map";
import fs from "fs";
const map = fs.readFileSync("../dist/main.js.map", { encoding: "utf-8" });
const smc = await SourceMapConsumer.fromSourceMap(map);
console.log(
smc.originalPositionFor({
line: 2,
column: 93121,
})
);
出力は。
{ source: 'webpack:///src/api.ts', line: 10, column: 8, name: 'Error' }
10行目はthrow Error("Error here!!");
!いい感じ!
さらに便利なツールを使えば
stacktrace-parserという超便利なツールがあります。stacktrace情報を分析してくれます。
導入してみれば。
import StackTraceParser from "stacktrace-parser";
const stack = `
Error: Error here!!
at /Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:93121
at s.handle_request (/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:84016)
at s (/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:82547)
at p.dispatch (/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:82569)
at s.handle_request (/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:84016)
at /Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:79523
at Function.v.process_params (/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:80405)
at w (/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:79466)
at /Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:96914
at s.handle_request (/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:84016)
`;
const output = StackTraceParser.parse(stack);
console.log(output);
出力。
[
{
file: '/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js',
methodName: '<unknown>',
arguments: [],
lineNumber: 2,
column: 93121
},
{
file: '/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js',
methodName: 's.handle_request',
arguments: [],
lineNumber: 2,
column: 84016
},
{
file: '/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js',
methodName: 's',
arguments: [],
lineNumber: 2,
column: 82547
},
{
file: '/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js',
methodName: 'p.dispatch',
arguments: [],
lineNumber: 2,
column: 82569
},
{
file: '/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js',
methodName: 's.handle_request',
arguments: [],
lineNumber: 2,
column: 84016
},
{
file: '/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js',
methodName: '<unknown>',
arguments: [],
lineNumber: 2,
column: 79523
},
{
file: '/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js',
methodName: 'Function.v.process_params',
arguments: [],
lineNumber: 2,
column: 80405
},
{
file: '/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js',
methodName: 'w',
arguments: [],
lineNumber: 2,
column: 79466
},
{
file: '/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js',
methodName: '<unknown>',
arguments: [],
lineNumber: 2,
column: 96914
},
{
file: '/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js',
methodName: 's.handle_request',
arguments: [],
lineNumber: 2,
column: 84016
}
]
素晴らしいですね。
では、全てのstacktraceを分析してみましょう。
import StackTraceParser from "stacktrace-parser";
import { SourceMapConsumer } from "source-map";
import fs from "fs";
const stack = `
Error: Error here!!
at /Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:93121
at s.handle_request (/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:84016)
at s (/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:82547)
at p.dispatch (/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:82569)
at s.handle_request (/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:84016)
at /Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:79523
at Function.v.process_params (/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:80405)
at w (/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:79466)
at /Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:96914
at s.handle_request (/Users/liufeng/Code/typescript-nodejs-boilerplate/dist/main.js:2:84016)
`;
const output = StackTraceParser.parse(stack);
const map = fs.readFileSync("../dist/main.js.map", { encoding: "utf-8" });
const smc = await SourceMapConsumer.fromSourceMap(map);
output.forEach(({ file, lineNumber, column }) => {
if (file.includes("main.js")) {
console.log(
smc.originalPositionFor({
line: lineNumber,
column: column,
})
);
}
});
出力は?自分で試してくださいね!
完了
フルソースはこちらへ
read-sourcemap
フォルダを参照してください。