天天看点

Node.js 中 source map 使用问题总结

node 应用功能越来越复杂,很多业务都开始尝试使用 typescript 来开发。现在前端写的 js 大部分是经过编译过程的,浏览器中通过 source map 的使用,可以很好的解决源码和编译运行时代码差异的问题。

那么,在 node 服务器环境应该如何使用 source map 呢?最近在重新搭建一个完全基于 ts 的 node 应用,所有的相关流程看起来都挺好的,唯一的缺陷是报错信息错误信息指向的是 js 文件。我觉得应该探索下如何让 node 支持 source map 。

对于 node 而言,服务器 source map 最大的价值在于错误信息有正确的错误堆栈,所以只要我们能够实现自定义错误堆栈信息就可以了。

<code>error.preparestacktrace</code> 方法可以拿到两个参数,错误基本信息和结构化错误堆栈,第二个参数是一个数组,通过这个数组可以拿到错误文件以及位置信息。最后基于这些信息重新返回一个字符串,这样就可以覆盖 error 对象的 stack 属性了。

基本代码结构如下:

在 <code>wrapcallsite</code> 方法里面可以通过分析源码,找到 sourcemap 然后返回正确的位置信息。

这看起来已经很完美了。source map 读取只在出现错误的时候才执行,所以这个功能不会有性能问题,在生成环境也可以开启。

这个问题排查了很久,最终定位到在 <code>wrapcallsite</code> 方法中拿到的 frame 对象返回的行号就是错误的,而这个获取行号的方法是 native code ,这个几乎没法调试了。我想,难道是 node 的问题?要调试到 node 源码么?

问题定位到后,解决就容易了。但解决这个问题得先讲讲 power-assert 是如何实现的。

power-assert 作为一个断言库,最大的特色在于错误信息的报告是非常友好的,一张图可以很清晰看到区别

Node.js 中 source map 使用问题总结

实现这样炫酷的报告是需要做一些特殊的处理,把测试用例的代码进行一次转换,举个例子

注:上面的代码不是真实运行的代码,经过一些删减

对于 <code>assert(a === b);</code> 这样一个表达式,会通过 <code>capture</code> 捕获每一个运算过程的位置和值,最终通过 <code>expr</code> 运算。这样经过转换后,代码运行逻辑不变,但是异常发生的时候可以返回 assert 表达式中每一步的返回值。

我所遇到的问题也就是因为 power-assert 对代码进行了转换,最终异常抛出时,真实 js 异常位置信息是转换后的位置,这个位置自然是无法正确定位到源码位置了。

运行简单流程图如下

Node.js 中 source map 使用问题总结

回到最初的问题,跑用例的时候行号不对了。power-assert 的影响在于两点

测试文件源码会被 power-assert 修改,增加一些信息收集代码

既然 power-assert 有引入 sourcemap ,那么是不是我关闭自己引入的 sourcemap 就可以了呢?理论上是应该如此的,但是因为 power-assert 对 sourcemap 文件不支持(inline sourcemap 是支持的),所以只能定位到 js 源码,无法定位到 ts 源码。

这两个问题解决后,在自己的业务用引入 source-map-support 也没有问题了,power-assert 返回的错误堆栈也可以正确的指向 sourcemap 位置了。

有时候可能会遇到一些奇怪的错误行号的问题,这可能是某个依赖包对 js 进行了转换,毕竟这在前端太常见了,动不动就重新编译 js 源码。

继续阅读