修复 Vue Devtools Open In Editor 找不到 Toolbox WebStorm

Vue / Nuxt Devtools 里的 Open in editor 本来是一个很顺手的入口:点一下组件或文件路径,IDE 直接跳到对应行。最近它在本机突然不好用了,表现是找不到 WebStorm,或者没有打开当前正在用的那套 WebStorm。

这个问题不在 Vue 组件,也不在 Nuxt 页面。真正卡住的是本机编辑器发现逻辑和 JetBrains Toolbox 的安装路径没有对上。

当前机器上的 WebStorm 是 JetBrains Toolbox 管理的,而且正在运行的是带版本号的应用包:

~/Applications/WebStorm 2026.1.3.app/Contents/MacOS/webstorm

但 Nuxt Devtools 最后调用的是 launch-editor,它内置的 macOS WebStorm 入口是:

/Applications/WebStorm.app/Contents/MacOS/webstorm

如果本机没有这个标准路径,或者这个路径还指向旧的 Toolbox 应用,Open in editor 就会断在打开编辑器这一步。

先把修复脚本放在前面

脚本已经跟文章一起放到公开资源里:

https://shengsheng.fun/files/vue-devtools-webstorm-toolbox-open-in-editor/scripts/fix-vue-devtools-webstorm-link.sh

建议先下载,看一眼,再执行:

curl -fsSLO https://shengsheng.fun/files/vue-devtools-webstorm-toolbox-open-in-editor/scripts/fix-vue-devtools-webstorm-link.sh
bash fix-vue-devtools-webstorm-link.sh

如果想顺手验证能不能打开某个文件,可以带 --open

bash fix-vue-devtools-webstorm-link.sh --open "$PWD/nuxt.config.ts:1:1"

脚本做的事情很窄:

  1. ps 找当前正在运行的 WebStorm*.app/Contents/MacOS/webstorm
  2. /Applications/WebStorm.app/Contents/MacOS/webstorm 软链到真实运行中的 WebStorm 二进制。
  3. 如果旧的 /Applications/WebStorm.app 只是一个没有 Info.plist 的壳目录,而且内部目录没有当前用户写权限,就先备份这个壳目录,再把 /Applications/WebStorm.app 软链到真实的 WebStorm app。
  4. 如果 ~/Library/Application Support/JetBrains/Toolbox/scripts/webstorm 是 Toolbox 自动生成的脚本,也先备份,再替换成同一个软链。

它不修改项目代码,也不动 Nuxt 配置。影响范围只在本机的 WebStorm 启动入口。

为什么是这个路径

Nuxt Devtools 处理 Open in editor 时,会把传进来的路径补成真实文件路径,再调用 launch-editor

// @nuxt/devtools/dist/chunks/module-main.mjs:10464
let editor = getOptions()?.behavior.openInEditor ?? void 0;
if (editor === "auto")
  editor = void 0;
await import('launch-editor').then((r) => (r.default || r)(path + suffix, editor));

如果 Devtools 配的是自动模式,launch-editor 会先看当前运行中的编辑器进程;如果指定了编辑器,例如 webstorm,它就直接尝试用这个命令打开文件。

问题出在 macOS 的 WebStorm 候选表。launch-editor@2.14.0 里 WebStorm 的映射是:

// launch-editor/editor-info/macos.js:39
module.exports = {
  '/Applications/WebStorm.app/Contents/MacOS/webstorm':
    '/Applications/WebStorm.app/Contents/MacOS/webstorm',
}

它的进程匹配逻辑也围绕这个标准路径做判断:

// launch-editor/guess.js:43
const processNameWithoutApplications = processName.replace('/Applications', '')

if (output.indexOf(processNameWithoutApplications) !== -1) {
  if (processName !== COMMON_EDITORS_MACOS[processName]) {
    return [COMMON_EDITORS_MACOS[processName]]
  }

  const runningProcess = processList.find((procName) =>
    procName.endsWith(processNameWithoutApplications),
  )
  if (runningProcess !== undefined) {
    return [runningProcess]
  }
}

这段逻辑能覆盖一种常见情况:应用不在 /Applications,但包名仍然叫 WebStorm.app。比如 ~/Applications/WebStorm.app/Contents/MacOS/webstorm 仍然包含 /WebStorm.app/Contents/MacOS/webstorm,所以能被认出来。

但 Toolbox 有时会把稳定版放成带版本号的目录:

~/Applications/WebStorm 2026.1.3.app/Contents/MacOS/webstorm

这个路径里没有 /WebStorm.app/Contents/MacOS/webstorm 这一段。自动识别就很容易跳过 WebStorm,或者落到别的正在运行的编辑器上。即使手动指定 webstorm,也要看 PATH 里的 webstorm 命令是不是指向当前真实版本。

修复点落在本机编辑器入口:把 Devtools 会访问的标准 WebStorm 路径补齐,让它指向当前真实运行的 Toolbox 版本。

/Applications/WebStorm.app/Contents/MacOS/webstorm
    -> ~/Applications/WebStorm 2026.1.3.app/Contents/MacOS/webstorm

如果旧的 /Applications/WebStorm.app 只是一个壳目录,脚本会退一步,把 app 目录本身指过去:

/Applications/WebStorm.app
    -> ~/Applications/WebStorm 2026.1.3.app

Toolbox 的 webstorm 命令也同步指过去:

~/Library/Application Support/JetBrains/Toolbox/scripts/webstorm
    -> ~/Applications/WebStorm 2026.1.3.app/Contents/MacOS/webstorm

这样两条路都能通:需要标准 app 路径时,/Applications/WebStorm.app 可执行;指定 webstorm 命令时,PATH 里的命令也能打开当前版本。

还有一个细节要单独记下来:launch-editor 的自动识别依赖当前进程名。如果 WebStorm 已经通过 Toolbox 的带版本号路径启动,进程里看到的仍然可能是 ~/Applications/WebStorm 2026.1.3.app/...,自动模式未必能反推出 WebStorm.app。最稳的做法是让 Devtools 明确使用 webstorm,或者启动 dev server 时带上:

LAUNCH_EDITOR=webstorm pnpm dev

这条环境变量会被 launch-editor 优先读取,比自动猜正在运行的编辑器更稳定。

脚本内容

脚本完整文件建议直接下载执行。这里贴核心判断,方便确认它没有做额外事情:

standard_webstorm_bin="/Applications/WebStorm.app/Contents/MacOS/webstorm"
toolbox_webstorm_cmd="${HOME}/Library/Application Support/JetBrains/Toolbox/scripts/webstorm"

find_running_webstorm_bin() {
  local line pid comm candidate=""

  while IFS= read -r line; do
    pid="${line%% *}"
    comm="${line#* }"
    case "$comm" in
      */WebStorm*.app/Contents/MacOS/webstorm)
        if [[ -x "$comm" ]]; then
          candidate="$comm"
        fi
        ;;
    esac
  done < <(ps -axo pid=,comm=)

  if [[ -n "$candidate" ]]; then
    printf '%s\n' "$candidate"
  fi
}

后面只是把找到的 target_bin 写成软链,并在替换 Toolbox 生成脚本前做一次备份。完整脚本在:

/files/vue-devtools-webstorm-toolbox-open-in-editor/scripts/fix-vue-devtools-webstorm-link.sh

执行后怎么确认

脚本跑完会打印当前使用的 WebStorm 版本,以及两个入口的状态。重点看这几行:

使用 WebStorm 入口:/Users/me/Applications/WebStorm 2026.1.3.app/Contents/MacOS/webstorm
WebStorm 2026.1.3
已更新软链:/Applications/WebStorm.app/Contents/MacOS/webstorm -> /Users/me/Applications/WebStorm 2026.1.3.app/Contents/MacOS/webstorm

如果旧入口目录没有内部写权限,输出会变成下面这样:

旧 WebStorm.app 壳目录已备份:/Applications/WebStorm.app.backup-20260611-203000
已创建标准 WebStorm.app 软链:/Applications/WebStorm.app -> /Users/me/Applications/WebStorm 2026.1.3.app

Toolbox 命令的复查仍然应该指向当前版本:

复查 PATH 中的 webstorm:/Users/me/Library/Application Support/JetBrains/Toolbox/scripts/webstorm
WebStorm 2026.1.3

也可以手动查:

ls -l /Applications/WebStorm.app/Contents/MacOS/webstorm
webstorm --version

如果 webstorm --version 输出的是当前正在用的版本,再用 LAUNCH_EDITOR=webstorm 启动 Nuxt dev server,或者在 Devtools 设置里把编辑器指定成 WebStorm,Open in editor 就会走这条已经修好的命令。

也可以用同一个依赖做一次最小验证:

LAUNCH_EDITOR=webstorm node - <<'NODE'
const { createRequire } = require('node:module')
const requireFromNuxtDevtools = createRequire(require.resolve('@nuxt/devtools/package.json'))
const launchEditor = requireFromNuxtDevtools('launch-editor')

launchEditor('/path/to/project/nuxt.config.ts:1:1', (file, message) => {
  console.error('launch-editor-error', file, message)
  process.exitCode = 1
})

setTimeout(() => {
  console.log('launch-editor requested WebStorm successfully')
}, 1500)
NODE

这个验证不代表 Devtools 自动模式一定会猜中 WebStorm,它验证的是「指定 WebStorm 后,launch-editor 能否成功打开当前 WebStorm」。

什么时候需要重跑

这类软链是本机补丁。Toolbox 更新 WebStorm、切换 EAP / 稳定版,或者重新生成 ~/Library/Application Support/JetBrains/Toolbox/scripts/webstorm 后,都可能把入口改回去。

再次遇到 Devtools 找不到 WebStorm,先启动目标 WebStorm,再重跑脚本即可。脚本会重新从进程里找当前版本,不需要手动改路径。

如果机器上同时开了多个 WebStorm 版本,脚本会选择进程列表里最后一个符合 WebStorm*.app/Contents/MacOS/webstorm 的入口。最稳的做法是只保留要打开的那个 WebStorm 在运行,再执行脚本。

这个修复只解决本地编辑器入口问题。组件路径不存在、源码映射不对、Devtools 传来的文件路径不是本机路径,仍然要回到项目配置和 Devtools 请求本身继续查。