2021年7月14日 • ☕️ 5 min read

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フォルダを参照してください。


関連投稿

HEIFファイルのアップロード対策

2021年7月11日

ThunderMiracle

Blog part of ThunderMiracle.com