SvelteKit应用错误堆栈追踪行号不正确的原因及解决方法
手动创建Error时堆栈行号异常的原因及解决方法
问题场景
通过以下命令创建SvelteKit项目骨架:
npm create svelte@latest lineno cd lineno npm install
创建src/hooks.server.js脚本,代码如下:
export const handle = async ({ event, resolve }) => { console.log(new Error("My error").stack) return await resolve(event); }
执行后打印的错误堆栈显示行号为4,但实际创建Error的代码明明在第2行:
Error: My error at Object.handle (/erikor/Documents/Business/lineno/src/hooks.server.js:4:15)
有意思的是,代码里自动抛出的运行时错误(比如直接写throw new Error('test'))却能显示正确的源码行号。
原因分析
这是因为SvelteKit在开发或构建阶段会对代码做转译处理——比如给异步函数添加辅助逻辑、做语法降级、注入框架相关代码等,最终运行的代码和你写的源码结构、行号都不一样。
手动创建Error时,堆栈信息是基于转译后的代码生成的;而运行时自动抛出的错误,SvelteKit(或其底层的Vite/Rollup)会通过sourcemap自动把转译后的行号映射回源码行号,但手动打印Error.stack时,默认不会触发这个映射逻辑。
解决方法
1. 开发环境快速解决:开启Node.js的sourcemap支持
启动项目时添加--enable-source-maps参数,让Node.js自动根据sourcemap转换行号:
node --enable-source-maps node_modules/@sveltejs/kit/dist/cli.js dev
也可以直接修改package.json里的dev脚本,以后启动不用每次加参数:
{ "scripts": { "dev": "node --enable-source-maps node_modules/@sveltejs/kit/dist/cli.js dev" } }
这样手动打印的Error.stack就会显示源码里的正确行号了。
2. 生产环境:手动解析sourcemap
如果是生产环境,需要借助source-map包手动转换堆栈行号:
- 先安装依赖:
npm install source-map
- 编写工具函数处理堆栈:
import { SourceMapConsumer } from 'source-map'; import fs from 'fs'; import path from 'path'; async function fixErrorStack(error) { const stack = error.stack; // 生产环境的sourcemap路径根据实际构建输出调整 const sourceMapPath = path.join(process.cwd(), '.svelte-kit/output/server/hooks.server.js.map'); if (!fs.existsSync(sourceMapPath)) return stack; const rawSourceMap = JSON.parse(fs.readFileSync(sourceMapPath, 'utf8')); const consumer = await new SourceMapConsumer(rawSourceMap); const correctedStackLines = stack.split('\n').map(line => { const match = line.match(/at .+ \((.+):(\d+):(\d+)\)/); if (!match) return line; const [, filePath, lineNum, colNum] = match; if (!filePath.includes('hooks.server.js')) return line; const originalPos = consumer.originalPositionFor({ line: parseInt(lineNum), column: parseInt(colNum) }); if (originalPos.source) { return line.replace( `${filePath}:${lineNum}:${colNum}`, `${path.basename(originalPos.source)}:${originalPos.line}:${originalPos.column}` ); } return line; }); consumer.destroy(); return correctedStackLines.join('\n'); } // 使用示例 export const handle = async ({ event, resolve }) => { const error = new Error("My error"); const correctedStack = await fixErrorStack(error); console.log(correctedStack); return await resolve(event); }
内容的提问来源于stack exchange,提问作者ejkreboot
相关产品推荐
相关产品推荐

