跳到正文

ESA Pages 体验之旅(5):修复 TableStore SDK 的变量声明错误

ESA Pages 体验之旅 · 1 · 2 · 3 · 4 · 第 5 篇(当前) · 6 · 7 · 8 · 9

TableStore Node SDK 进入 ESM 函数产物后,在未声明的变量赋值处抛出 ReferenceError。ESM 采用严格模式,原先可能隐式创建全局变量的写法在这里被拒绝,需要修正 SDK 的声明。

修复涉及三个文件、六处声明,补上 var 即可。仓库里同时存在协议生成代码和浏览器 bundle,因此先确认待改文件的来源,避免修改会被重新生成的产物。

SDK 实现和生成文件

本次核对固定公开仓库 master 提交 39d4dae43ed73fc97cad5bff3d634d926a8ce1bf、仓库版本 5.6.4,以及实际使用的 npm 5.6.6。仓库是阿里云官方 TableStore Node SDK。

SDK 实现、协议生成文件和浏览器构建产物各有来源

文件或脚本 公开仓库能确认的职责
lib/filter.js 等实现文件 Git 历史直接维护的 JavaScript
build_proto.sh 调用 pbjs、flatc,从协议定义生成相应代码
浏览器构建 Browserify / Uglify 生成浏览器分发包
package.json 当前没有把 TS 或 Babel 源码编成整个 lib 的构建流程

公开 Git 历史直接维护这些 JavaScript 文件,部分文件还使用 const、let 与解构。公开仓库没有展示一套将 TS 或 Babel 源码编译为整个 lib 的流程;内部是否另有源码,现有材料无法确认。

待修复的三个文件不由协议生成命令产生,实际修改的是这些 JavaScript 实现文件。

npm 发布包与 GitHub 的差异

公开仓库与 npm 5.6.4、5.6.6 的实际文件对照

npm 5.6.4 的 lib 文件在统一换行后与该 Git 提交一致。5.6.6 则有九个 lib 文件不同,但待修复的 filter、PlainBuffer builder 仍相同,encoder 虽有其他更新,缺变量声明的位置也仍存在。

5.6.6 的 gitHead 指向 6fbfb291ab4268d47934ddb3a39fac6416a71bad,公开 GitHub 查询返回 422、No commit found。发布包与可见 Git 历史存在同步差异,尚缺对应的公开维护版本。完整版本、文件散列与查询结果见源码与发布包核对记录弹窗。

npm 5.6.6 的这些更新未同步到本次查到的公开 GitHub 版本,因此暂不向旧版 master 提交 PR。直接在旧分支上修改,无法确认补丁与当前发布版本的对应关系。问题先通过阿里云官方售后反馈,待对应源码公开或维护者明确接收分支后,再考虑 PR。

补齐六处变量声明

文件 缺失声明
lib/filter.js 三个 pro 循环变量
lib/protocol/encoder.js 一个 sub 循环变量
lib/protocol/plian_buffer_builder.js 一个 key 循环变量和一个 size 赋值

下面是相同机制的简化片段:

// 修复前:严格模式下,pro 未声明。
for (pro in properties) {
  // ...
}

// 修复后:沿用 SDK 现有的 var 风格。
for (var pro in properties) {
  // ...
}

原代码在非严格 CommonJS 环境中,可能将这些名字写进全局对象。严格模式拒绝这种赋值,错误才变得可见。给宿主补同名全局变量,会继续保留共享状态污染;改成非严格模式,也没有修掉缺陷。

缺失声明可以追到 2017-06-15 的初始实现提交 45e4c44f9ae3fea0010ec4124da98bb66039dca9,提交信息为 add node.js sdk。缺陷长期存在,严格模式让它在这次产物中暴露出来。

复用已有的编解码测试

SDK 已有 PlainBuffer 编解码测试。使用原始 CommonJS 加载时,两项测试通过;将涉及的三个实现文件按严格模式加载时,原代码在 key 处报错,补声明后两项又通过。

这两项既有测试经过了缺陷位置,修复后编码结果保持一致。验证范围限于这些 PlainBuffer 路径。默认的 mocha test 不递归覆盖所有子目录,test/protocol 中的用例需要明确执行。完整命令和结果见既有功能测试对照弹窗。

长期防止再次漏声明,适合使用 no-undef 这样的静态规则;功能测试继续验证编码、过滤和协议结果。无需为每个补上的 var 各写一个只检查源码文字的测试。接入 lint 时仍需登记真实宿主全局,避免把隐式变量误列为合法全局。

用依赖补丁保存修复

应用仍然安装 npm 发布的 SDK,可以用包管理器的补丁机制保存这六处修复。直接改 node_modules 只能影响当前安装;生成补丁并纳入版本管理,才能让其他设备、CI 和下一次安装得到相同结果。

本项目使用 pnpm,已经通过原生补丁机制维护 tablestore@5.6.6。创建或更新补丁时,先运行:

pnpm patch tablestore@5.6.6

在命令给出的临时目录里修改 SDK,再将这个目录路径传给 pnpm patch-commit。它会生成补丁文件并登记 patchedDependencies。当前修复保存在 patches/tablestore@5.6.6.patch,由 pnpm-workspace.yaml 引用;补丁、配置与锁文件一起提交,依赖安装时应用修复。具体命令见 pnpm patch 和 patch-commit。

使用 npm 或 Yarn Classic 的项目,也可以用 patch-package:修改已安装的包后生成 patches 文件,并配置 postinstall 在安装后重新应用。补丁与安装脚本同样需要进入仓库,部署构建时还要确认没有跳过这一步。pnpm 已有原生能力,本项目不再额外引入 patch-package。

补丁针对具体发布版本保存。升级 SDK 时应重新检查差异,不能因为旧补丁仍能应用,就认定新版本仍然需要这些修改。

等待官方修复

这组缺少变量声明的问题已经向阿里云官方售后反馈,目前先保留依赖补丁。后续如果官方发布修复版本,先核对发布包是否补齐这六处声明,再在不应用该补丁的条件下,运行既有功能用例和最终 ESM 产物验证。

确认新发布包修复这些声明后,再升级依赖,删除 TableStore 的补丁文件及 patchedDependencies 对应配置,更新锁文件,并用干净安装复查。

这里撤除的是 SDK 变量声明补丁。ESA 的模块加载、网络传输和其他兼容处理,需要分别判断是否仍然必要。

SDK 的环境依赖

这份补丁修复了 SDK 的变量声明。签名、二进制编码、URL 与网络传输还依赖宿主 API,需要在最终产物中继续验证。SDK 能通过严格模式检查以后,仍可能在初始化或实际调用时遇到环境差异。

TableStore 源码与 npm 发布包核对记录

打开原文件 source-audit.json

包含固定 Git 提交、包版本、文件散列、生成脚本,以及 npm 5.6.6 的 gitHead 查询结果。

既有 PlainBuffer 功能测试与严格模式对照

打开原文件 functional-check.json

使用仓库已有编解码用例比较原始加载、严格模式和补声明后的结果。数据保留实际命令与失败位置,不把这两项测试当成全 SDK 回归。