inquirer.js 竟导致公司项目构建失败

455 阅读3分钟

?? 引发的 ”火灾”

9.15,在这个平平无奇的早晨,除了 Team 集体出差深圳办公,与往常无异。

按照正常的工作流程,我开始了前端项目的远程构建工作,但不到几分钟,飞书就发来了一条消息:”构建失败!“

WTF,当时就心里一震,由于急着给测试同学最新的版本,我连忙打开了远程控制台,查看构建日志:

根据日志内容,可知错误发生在 inquirer.js 依赖库中,其使用了 Nullish coalescing operator (??) 操作符(中文翻译为空值合并运算符),最终导致构建失败。

?? 是一个逻辑操作符,虽然在日常工作中使用频率较低,但也有所耳闻,是 ES11(ES2020)中新引入的特性,当左侧的操作数为 null 或者 undefined 时,返回其右侧操作数,否则返回左侧操作数,具体例子如下:

let result = undefined ?? "Hello";
console.log(result); // Hello

result = null ?? true;
console.log(result); // true

result = false ?? true;
console.log(result); // false

“救火” 阶段

定位到问题后,我立马着手修复工作。

首先,inquirer.js 是一个基于询问式的命令行工具,能简化用户在终端的交互输入,那么它必然依赖于 Node 环境,而不是我们熟知的浏览器环境(基于 ES11 发布已经近一年的事实,市面上的现代浏览器早已支持 ?? 操作符)。

我马上查看了远程构建环境的 Node 版本,显示为 v12.14.1.

Node.js 基于 V8 引擎实现,于是我查询了 V8 引擎的官网文档,搜索 “Nullish coalescing” 关键词,找到了相关的文章:Nullish coalescing.

除了介绍 Nullish coalescing 的使用方式外,细心的官方还在文章末尾处贴了一张图,告知各主流环境的最低支持版本:

问题显而易见,远程构建环境的 Node 版本低于 v14,自然不支持 ?? 操作符,导致了构建失败。

处于安全和稳定性考虑,放弃了升级版本 Node 的方案,公司项目不是自己的玩具,不能随意更换构建环境 Node 版本,故依然采用 v12.14.1.

备选方案是将 inquirer.js 回退到上一个正常的版本并且锁定,我查看了 Github 上导致这个问题的 commit,是从 v8.1.3 升级至 v8.1.4 引起的,所以使用 inquirer@8.1.3 即可。

根据 inquirer 的 package.json 表明,最低支持的 Node 版本为 v8.

{
  "engines": {
    "node": ">=8.0.0"
  }
}

此次更新虽然只升级了 patch 版本号,即补丁版本,但使用了 v14 才支持的 Nullish coalescing,引入了 Breaking Change,导致 Node 版本低于 v14 的用户都受到影响。

前车之鉴,后车之师

在经历了半个小时的紧急修复,我心里的一块石头算是落地了。

事后的第一时间,我也在 issue: Unexpected token '??' #1046 中进行了评论。

尽管 inquirer.js 在 Github 上有着 15k 的 star,周下载量高达 22,347,585,但作为一个明星项目,依旧会犯错。

因为代码始终是靠人编写,人是感性动物,那就一定会出错,只是概率问题罢了。

前阵子,我看到一句话,分享给大家:

由于任何服务、代码都可能存在外部调用,只要外部调用就存在不确定性,代码就可能出现异常。

对于一名技术精湛的工程师而言,要时刻警惕外部调用、依赖库带来的威胁,可能是一个不起眼的更新就会引发程序崩溃,我们更要对线上环境充满敬佩。