1-vscode调试ts代码思路
以下内容以 VSCode 作为编程和调试环境。
TS 代码最终通常会先编译成 JS,再由 Node.js 或浏览器运行。VSCode 调试 TS 的关键,就是让调试器知道:
运行中的 JS 代码,对应哪一份 TS 源码
这个映射关系主要依赖 sourceMap。
1. 调试项目自身的 TS 代码
如果要调试的是当前项目里的业务代码,比如:
bash
src/app.controller.ts
src/app.service.ts
流程比较直接。
在 VSCode 的"运行和调试"面板中添加一个调试配置,例如 Launch via NPM,配置文件默认生成在:
bash
.vscode/launch.json
Nest 项目里常见配置大概是:
json
{
"type": "node",
"request": "launch",
"name": "Launch via NPM",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "start:debug"],
"console": "integratedTerminal"
}
然后在业务代码中打断点,点击运行调试配置。进入 Debugger attached 状态后,请求对应接口,代码就会在断点处停住。
这种情况一般不需要额外处理 node_modules,因为当前项目的 tsconfig.json 通常已经开启了:
json
"sourceMap": true
只要项目自身能生成 sourcemap,VSCode 就能把运行中的 JS 映射回 src 下的 TS 文件。
2. 调试 node_modules 中已经打包后的源码
调试 node_modules 里的包会复杂一些,因为 npm 下载下来的包通常只包含编译后的 JS,不一定包含可用的 sourcemap。
以调试 NestJS 源码为例,整体思路是:
rust
拉取框架源码
-> 修改构建配置,生成 .js.map
-> 构建源码
-> 用构建产物替换业务项目 node_modules 中对应的包
-> 配置 VSCode 允许解析 node_modules 下的 sourcemap
首先在 VSCode 的调用栈中确认你要调试的包。例如普通 Nest HTTP 项目中,核心包通常是:
less
@nestjs/core
@nestjs/common
@nestjs/platform-express
不要直接替换整个:
css
node_modules/@nestjs
因为里面还可能有:
less
@nestjs/cli
@nestjs/schematics
这些是开发工具包。比如 nest start 依赖 @nestjs/cli,如果误删或覆盖它,就会出现找不到 nest.js 的错误。
拉取 Nest 源码后,需要修改实际参与构建的配置。对于 Nest 这种 monorepo 项目,根目录的 tsconfig.json 不一定是构建入口,真正生效的通常是各 package 的 tsconfig.build.json,或者它们共同继承的公共 build 配置。
关键配置是:
json
{
"compilerOptions": {
"sourceMap": true,
"inlineSources": true,
"sourceRoot": "/本机绝对路径/nest/packages"
}
}
其中:
arduino
sourceMap
决定是否生成 .js.map 文件。
inlineSources
把 TS 源码内容内联进 sourcemap,方便调试器读取。
sourceRoot
告诉调试器 sourcemap 里的 sources 应该从哪个源码根目录查找。
需要注意,sourceRoot 指向的是本地源码目录,不是 node_modules 目录。
例如:
json
"sourceRoot": "/Users/mac/project/nest/packages"
构建后,.js.map 中可能会出现:
json
{
"sourceRoot": "/Users/mac/project/nest/packages/",
"sources": ["core/router/router-execution-context.ts"]
}
调试器就能组合出真实源码路径:
bash
/Users/mac/project/nest/packages/core/router/router-execution-context.ts
构建完成后,还要确认 .js.map 是否真的被复制到了最终产物目录。以 Nest 为例,npm run build 会先编译 packages,再通过 postbuild 把产物移动到源码仓库自己的:
css
node_modules/@nestjs
如果移动脚本只复制了 .js 和 .d.ts,没有复制 .js.map,那最终替换到业务项目里的包依然没有 sourcemap,需要补充搬运规则。
最后,只替换业务项目中需要调试的包,例如:
bash
nest/node_modules/@nestjs/core -> 项目/node_modules/@nestjs/core
nest/node_modules/@nestjs/common -> 项目/node_modules/@nestjs/common
nest/node_modules/@nestjs/platform-express -> 项目/node_modules/@nestjs/platform-express
VSCode 的 launch.json 中还需要允许解析 node_modules 下对应包的 sourcemap。默认配置可能会排除 node_modules,例如:
arduino
"!**/node_modules/**"
调试源码时需要去掉这个排除,或者更精确地允许 Nest 相关包:
perl
{
"resolveSourceMapLocations": [
"${workspaceFolder}/**",
"${workspaceFolder}/node_modules/@nestjs/**"
]
}
这样当代码运行到 node_modules/@nestjs/core 时,调用栈里点击对应节点,就可以跳转到本地 Nest 源码的 TS 文件。
3. 常见问题
如果断点是灰色的,提示未绑定,优先检查是否真的生成了 .js.map:
arduino
node_modules/@nestjs/core/**/*.js.map
如果能跳转源码,但打开的是只读文件,通常是 sourceRoot 或 sources 拼出来的路径不是真实本地源码路径。
如果启动时提示找不到 nest.js,一般是错误覆盖了整个 node_modules/@nestjs,把 CLI 包删掉了。
如果项目根目录下放了框架源码,比如:
bash
项目/nest
需要在业务项目的 tsconfig.json / tsconfig.build.json 中排除它:
json
{
"exclude": ["node_modules", "dist", "test", "nest", "**/*.spec.ts"]
}
否则 nest start --watch 可能会把整个框架源码、sample、integration 测试代码都纳入业务项目编译,产生大量无关 TS 错误。
4. 总结
VSCode 调试 TS 的本质是:
rust
运行 JS
-> 通过 sourcemap 找到 TS
-> 在 TS 源码中命中断点
调试普通业务代码时,只需要创建合适的 launch.json,确保项目开启 sourceMap,然后打断点运行即可。
调试 node_modules 中的源码时,需要额外完成三件事:
arduino
源码包重新构建出 .js.map
业务项目使用这份带 sourcemap 的构建产物
VSCode 允许解析 node_modules 下的 sourcemap
如果断点位置不对、跳转文件不对、或者源码只读,本质上都是 sourcemap 没有正确指向真实源码文件。
2-例子
nest代码为例,创建一个nest代码。
css
npm i -g @nestjs/cli
nest new nest-test
创建launch.json脚本,打上断点。

访问get请求(直接访问localhots:3000页面),可以看到断点生效了。

如果调试node_modules/的代码包的源代码,比如@nestjs,需要拉取源码,获取sourceMap文件,覆盖node_modules/下@nestjs对应文件,具体操作如下: nest源码文件需要在packages目录下针对每个仓库代码进行tsconfig.build.json配置。

执行npm install && npm run build,获取node_modules/@nestjs我们需要的产物,去替换项目的@nestjs。

在nest源码中打上断点,我们就可以在nest代码被执行时,跳转到源码进行断点。
