program 字段必须以 ${workspaceFolder} 为根显式拼接路径,因 VSCode 不以当前文件目录为基准解析相对路径,而是始终从工作区根出发;否则会因路径错误导致模块找不到或断点失效。

launch.json 里写死 "program": "./src/index.js" 在多级子文件夹下会直接报错——VSCode 不会自动向上查找或解析相对路径的“真实根”,它只认 ${workspaceFolder} 这个锚点。你必须显式告诉它从哪开始算“相对”。
为什么 ./src/index.js 在子文件夹里跑不起来
VSCode 的 program 字段解析路径时,**不以当前打开的文件所在目录为基准**,而是始终以 ${workspaceFolder}(即 VSCode 工作区根目录)为起点。如果你在 packages/api/src 下打开文件并点击调试,./src/index.js 会被拼成 ${workspaceFolder}/./src/index.js,而不是 ${workspaceFolder}/packages/api/src/index.js。
- 错误现象:
Cannot find module './src/index.js'或断点全灰、调试器静默退出 - 根本原因:
program是工作区绝对路径语义,不是 shell 当前目录语义 - 常见误操作:把终端里能跑通的
node ./src/index.js直接抄进program字段
program 必须用 ${workspaceFolder} 显式拼接
所有路径都要从工作区根出发,用变量补全。哪怕你只在 packages/cli 下开发,也要写清楚完整相对路径:
{
"name": "Debug CLI",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/packages/cli/src/index.ts",
"outFiles": ["${workspaceFolder}/packages/cli/dist/**/*.js"],
"sourceMaps": true,
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/ts-node",
"args": ["--loader", "ts-node/esm"]
}
-
${workspaceFolder}是唯一可靠起点;${file}、${relativeFile}等变量不能用于program - 如果项目用了 pnpm workspace 或 yarn workspaces,确保
${workspaceFolder}指向的是 workspace 根(即含pnpm-workspace.yaml或workspaces字段的目录) - TypeScript 项目务必同步配置
outFiles和sourceMaps,否则断点无法命中源码
多包项目里怎么避免每个 launch.json 都手写路径
没有“动态根目录”机制,但有可复用的组织方式:
- 在 workspace 根目录的
.vscode/launch.json中,为每个 package 写独立配置,用清晰的name区分,比如"Debug api: dev"、"Debug cli: test" - 用
preLaunchTask触发对应 package 的构建任务(需配合.vscode/tasks.json定义build:api、build:cli等) - 避免在子文件夹里单独建
.vscode——VSCode 不支持嵌套工作区级launch.json,子目录下的配置会被忽略 - 如果真要按目录切换上下文,改用 VSCode 的 “Multi-root Workspace”:把每个 package 作为独立文件夹加入 workspace,再为每个 folder 单独配
launch.json
webRoot 和 outFiles 的路径也得对齐 ${workspaceFolder}
前端或全栈项目里,webRoot(Chrome 调试用)和 outFiles(Node sourcemap 用)如果写成 "./src" 或 "dist/**",一样会失效:
-
"webRoot": "${workspaceFolder}/packages/web"—— 告诉 Chrome 调试器,sourcemap 里的src/App.tsx实际在 workspace 根下的这个位置 -
"outFiles": ["${workspaceFolder}/packages/web/dist/**/*.js"]—— 让 Node 调试器知道去哪找编译产物 - 只要路径里出现任何没带
${workspaceFolder}的相对片段,就等于放弃路径控制权,结果不可控
最易被忽略的其实是 outFiles 的 glob 模式匹配失败——它不报错,只让断点变灰,排查时得打开 trace: true 看日志里实际加载了哪些 map 文件。


















