VSCode 插件报错偶现 npm/node not found 问题排查&解决 - 运行时和内置终端 $PATH 不匹配

前言

大家好,我是祯民。今天我想给大家分享一个 vscode 插件开发中比较难排查的偶现问题,报错信息看上去很像一个普通的本地环境问题,但实际并不简单。全文会被分为背景排查解决三个模块,希望可以给遇到类似问题的同学一点启发,下面我们直接进入正题。

背景

前段时间我写了一个 duplication-lint 的 vscode 插件,可以做到代码重复场景防劣化的 lint,当编码阶段有工作空间维度的重复代码时,会警告报错,并提供一些修复方式,比如 AI 修复等,类似下面的效果。

在给业务方同学去使用的时候,有一些同学会偶现下面的报错。

因为依赖的一些特殊历史原因,部分逻辑非常规的调包,而是插件的逻辑中直接使用 node 去执行一个可执行文件,如果没有的话,会走 npm 安装。在逻辑中的体现为,使用 exec 去执行终端命令调起。

当然这个问题我早期并没有放在心上,一个是这个报错信息太明显了,就是内置环境找不到全局的命令(比如 npm 或者 node),另一方面又是小概率事件,很少的一部分用户复现,而且还不是稳定复现。我当时感觉这个问题大概率就是用户本地环境没装好,或者基础命令并不是全局安装。

直到年后回来,我重启了一下电脑,发现我也复现了这个问题(中途我没有进行任何操作)。这次是 node not found 的报错,但估计也是 exec 位置处引起的。

其实开发过程中我也有复现过类似问题,但通常是一闪而过,或者 reload window 就可以解决,vscode 插件运行时是魔改后的 node 运行时,现在回顾起来这样未排查就武断凭 node 开发经验下结果的确是太自负了。所以我决定这次彻底搞清楚到底是什么原因导致的~

问题排查

猜测1:node 可执行文件是否存在

第一个猜测是针对本地环境的,也是对于这个报错最直接的常见问题。当然在终端试验过后,本地终端环境都是正常的。

猜测2:exec 命令是否可执行

既然本地环境没问题,难道是我的 exec 命令本身就跑不起来?但这个可能性很小,毕竟报错信息是 node 这种全局命令找不到,而不是脚本的报错。但出于死马当活马医的心态,我还是决定尝试一下排查。

当然结果也是没问题的,而且快得飞起...

猜测3:cwd 是否异常

排查了 node 本地环境和 exec 的可能后,我的第一反应是,也许是在某些场景下,逻辑中注入了异常的 cwd,导致执行目录出来了问题。

验证后仍然正常...

猜测4:仅限 node ?还是所有的全局命令失效

到这里其实我的心态已经有点崩了,我在想既然直接执行 exec 跑 node 找不到命令,那么就说明至少在当时的执行环境下 node 不是一个全局命令。既然如此,我也许可以用 which node 拿到 node 可执行文件所在的文件路径,直接执行可执行文件。

这个想法看上去很靠谱,但依然失败了

看来不仅是 node 挂了,所有的全局命令全部都不行了。不过 which 这个命令挂了倒的确给了我一个启发,which 命令是用来查找并显示在 $PATH 环境变量所定义的目录列表中是否存在指定命令的可执行文件。 难道当前执行环境 $PATH 本身就是错的?

猜测5:vscode内置shell与系统终端不匹配?

在讲这个猜测前,这里我们先简单温习一下$PATH 是什么?

$PATH 是一个在 Unix-like 操作系统(包括 Linux、macOS 等)和 Windows 操作系统中的环境变量。它是一个字符串,包含了多个由冒号(Unix-like 系统)或分号(Windows 系统)分隔的目录路径列表。 通过配置 $PATH,用户可以轻松地执行位于系统不同目录下的命令,而无需每次都输入完整的路径。这对于频繁使用的命令或自定义工具尤其有用。

简单来说,就是存放全局命令的根目录,全局命令的可执行文件路径需要是 P A T H 的子集。 ' PATH 的子集。` PATH的子集。'PATH` 不同,那就不得不怀疑一下,vscode 的内置 shell 是不是设置得不对了。

我系统中默认使用的终端就是 zsh,看上去仍然是正确的~

猜测6:vscode 内置终端 和 vscode 插件运行时 $PATH 不同?

到这里只剩下最后一个原因了,那就是 vscode 插件运行时的 $PATH 和 vscode 内置终端的 $PATH 并不相同。vscode 插件运行时的 $PATH 继承于启动 vscode 的父进程环境,当然我们设置过默认终端,所以理论上说这两者应该是相同的。

但这里的确也没有其他的可能性了,我们先按这个思路来排查,毕竟也是一个偶现的小概率事件。

我们先看 vscode 内置终端中 $PATH是什么。

这个是符合预期的,也就是它会先在 /Users/bytedance/.local/bin 下找所有的可执行文件,找不到再依次降级,我所有的个人文件都在这里面,所以是可以找到的。

这个通过前面猜测推断,也肯定是正常的,不然不可能都能正常执行。我们接着来看vscode 插件运行时的 $PATH,这个我们需要使用脚本输出,因为它是独立的一个进程。

的确不一样,vscode 插件运行时中的 $PATH 中并没有包含任何全局路径。所以我们也就定位到了原因,vscode 插件运行时在某些情况下并没有顺利继承默认终端中的环境变量 $PATH,而是使用了类Unix操作系统(如Linux、macOS)中的默认命令行路径。(不排除是 vscode 中配置的兜底,在某些场景下继承默认终端变量异常时,就使用系统默认 $PATH)

解决方案

既然定位到了原因,我们就可以开始考虑怎么解决了。我们只需要使得 vscode 插件运行时能顺利继承终端中的环境变量,这个问题就可以迎刃而解了。

方案1:code 启动,自动继承终端的环境变量

第一个方案是,使用 code 命令启动 vscode,这时候 vscode 将会自动继承启动终端的环境变量。还不知道什么是 code 命令的同学可以网上搜下,简单来说就是 vscode 提供的一个全局命令,可以通过 code 直接打开 vscode。

方案2:exec 取 terminal.integrated.env 作为优先级更高的兜底

terminal.integrated.env 是VSCode中用来配置集成终端环境变量的一个设置选项。这个设置项允许你为不同操作系统(环境)自定义集成终端的环境变量。

macOS 的配置示例如下

json 复制代码
"terminal.integrated.env.osx": { 
    "PATH": "${env:PATH}:/Users/user/custom/path" 
}

我们可以配置对应的环境变量到配置项中,然后在执行 child_process 模块的时候,通过 vscode API 取对应的环境变量配置项作为 env 参数的兜底,从而达到在运行时封闭进程内同步外部进程环境变量的效果。

方案3:换依赖调用,而非 exec

这个是我最后用的方案,不再使用 exec,而通过依赖包调用。当然这个不算从根源解决,只是为了让整个流程更开箱即用,逃避了问题而已,并不是所有的场景都有比较合适的方式替代的。

方案4:插件开发阶段,可配置 launch.json path 变量手动注入环境变量

上面的三种方案都是针对用户使用阶段,出现类似问题的解决方案。如果是在插件的开发阶段,我们也可以通过在 launch.json 中配置 path 变量手动注入环境变量。这样通过该 launch.json 启动的环境将强制被配置的环境变量覆盖。

json 复制代码
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Run Extension",
            "type": "extensionHost",
            "request": "launch",
            "args": [
                "--extensionDevelopmentPath=${workspaceFolder}"
            ],
            "outFiles": [
                "${workspaceFolder}/dist/**/*.js"
            ],
            "path": "/Users/bytedance/.local/bin"
        }
    ]
}

小结

到这里本文的课程就结束了,一个看似简单的偶现环境问题,实际也暗藏着不少知识点。相信经过这次学习,对大家都有或多或少的启发,在以后遇到类似全局命令失效的问题时,大家也可以从终端环境、cwd、$PATH展开,针对问题的实际情况深入排查。

感谢大家的时间,有问题欢迎在评论区交流讨论。

相关推荐
猫猫不是喵喵.8 小时前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~9 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华10 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子12 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅12 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni12 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学14 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端
赵庆明老师14 小时前
Vben精讲:21-详解web-antd:tsconfig.json
前端·json·vim