ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Midway validation-* 包构建产物测试实战:用 ESM/CJS 双模块测试发现并修复 56% 功能失效的严重 Bug

2026/9/28 2:45:50 拓冰建站 浏览量
Midway validation-* 包构建产物测试实战:用 ESM/CJS 双模块测试发现并修复 56% 功能失效的严重 Bug 后端微服务云原生【免费下载链接】midway A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 项目地址https://gitcode.com/gh_mirrors/mi/midway点击查看免费下载导读本文基于 midway 仓库中 add-validation-dist-tests 变更总结 及其配套的 FINDINGS.md、FIX_SUMMARY.md、FINAL_REPORT.md 等记录完整复盘一次为构建产物dist补测试、在用户使用前拦截生产级 Bug的真实实践。读完本文你将掌握如何为 tsup 构建的双模块CJS ESM包编写可独立执行的构建产物测试、如何通过深度调用测试而不是浅层存在性检查暴露真实问题、以及 ESM/CJS 互操作中import * as与import default的差异及其修复方案。背景为什么构建产物也需要测试midway 的validation-*系列子包validation-zod、validation-zod4、validation-joi、validation-class-validator均使用 tsup 构建同时产出 CJSdist/index.js与 ESMdist/index.mjs两套产物并通过package.json的exports字段做条件导出例如 validation-joi 的配置{ main: ./dist/index.js, module: ./dist/index.mjs, types: ./index.d.ts, exports: { .: { types: ./index.d.ts, require: ./dist/index.js, import: ./dist/index.mjs } } }问题是当时这些包只对源码做单元测试从未验证构建产物在真实运行环境中的可用性。按照 proposal.md 的说明缺少 dist 测试可能带来四类隐患包配置错误exports、main/module字段指向错误未被发现模块系统兼容性问题在发布后才暴露给用户构建后的导出 API 不可用或名称与源码不一致TypeScript 类型声明文件.d.ts与实际代码不匹配。因此该变更的目标很明确为这 4 个 tsup 构建、同时支持 CJS ESM 的包补上独立的构建产物测试套件在 CI/CD 阶段提前拦截这些问题。测试方案设计测试范围与文件组织按照 spec.md 的规范测试采用统一的文件组织方式测试文件放置在package/test/目录下命名规则为esm-dist.test.mjsESM 测试与源码单元测试完全隔离独立执行、互不影响。最终落地到仓库的 4 个 ESM 构建测试文件测试文件对应包状态validation-zod/test/esm-dist.test.mjsmidwayjs/validation-zod完全正常validation-zod4/test/esm-dist.test.mjsmidwayjs/validation-zod4完全正常validation-joi/test/esm-dist.test.mjsmidwayjs/validation-joi发现并修复 Bugvalidation-class-validator/test/esm-dist.test.mjsmidwayjs/validation-class-validator完全正常说明测试只针对ESM 构建dist/index.mjs因为 CJS/ESM 互操作问题最容易在 ESM 入口暴露而 CJS 路径经由 Node.js 原生require解析风险较低。测试内容两层递进每个测试文件包含结构测试与深度调用测试两个层次第一层结构测试浅层检查包能够被import成功加载存在default导出validateServiceHandler是函数schemaHelper是对象且 9 个方法isRequired、isOptional、setRequired、setOptional、getSchema、getIntSchema、getBoolSchema、getFloatSchema、getStringSchema全部存在且类型正确。第二层深度测试实际调用真实调用validateServiceHandler(mockContainer)验证它能返回验证服务实例逐一调用 9 个 schemaHelper 方法验证返回值非空、类型正确、无运行时异常例如getSchema(TestDTO)应返回一个可用的 Joi/Zod schema 对象。以 validation-joi/test/esm-dist.test.mjs 为例深度测试的核心模式如下// 浅层检查最初的做法所有包都能通过 assert.strictEqual(typeof schemaHelper.getSchema, function); // ✅ // 深度测试改进后真实调用才能发现问题 const schema schemaHelper.getSchema(TestDTO); // ❌ validation-joi 抛出异常每个方法调用都有独立的try/catch包裹并记录失败明细测试结束时若有任何方法失败会输出清晰的错误报告并process.exit(1)使测试失败。运行方式一条命令打通全部门根目录 package.json 中通过 lerna 聚合了 4 个包的test:dist脚本# 测试所有 validation-* 包根目录统一入口 pnpm test:dist # 测试单个包 cd packages/validation-zod pnpm test:dist # 先构建再测试dist 产物不存在或过期时必须先构建 pnpm build pnpm test:dist各子包内的脚本定义也很简单例如 validation-joi/package.jsonscripts: { build: tsup, test:dist: node test/esm-dist.test.mjs }按 spec.md 的要求test:dist只运行构建产物测试、不触碰源码单元测试测试使用构建后的dist目录而非src目录因此必须先构建再测试。重大发现validation-joi 的 ESM 构建有 5/9 方法完全失效深度测试立刻显出了价值midwayjs/validation-joi的 ESM 构建存在严重 BugFINAL_REPORT.md 将其严重程度标为 极高。失败的方法5/9占功能 56%方法报错影响getIntSchema()Joi.number is not a function无法创建数字 schemagetBoolSchema()Joi.boolean is not a function无法创建布尔 schemagetFloatSchema()Joi.number is not a function无法创建浮点 schemagetStringSchema()Joi.string is not a function无法创建字符串 schemagetSchema()Joi.object is not a function无法创建对象 schema核心功能正常的方法4/9isRequired()、isOptional()、setRequired()、setOptional()均不直接使用 Joi 的顶层 API而是基于getRuleMeta元数据与schema.describe()实现见 src/index.ts 第 162-226 行因此不受影响。根本原因CJS 模块被 ESM 包装成{ default: Joi }joi是一个 CommonJS 模块。在 ESM 环境下Node.js 会把 CJS 模块的module.exports包装为一个带default属性的命名空间对象// 问题写法import * as 在 ESM 中取到的是命名空间包装 import * as Joi from joi; // 实际结果: Joi { default: [真正的 Joi 对象] } // 所以 Joi.object 是 undefined调用变成 undefined.object() ❌而源码此前恰恰使用了import * as Joi from joi于是 5 个依赖Joi.xxx()顶层 API 的方法全部崩溃。这解释了为什么CJS 用户完全正常、ESM 用户完全无法创建任何 schema——CJS 的require()直接返回module.exports不存在default包装层。为什么 tsup 没有自动修复tsup 对require()调用能通过__commonJS包装器正确转换但import * as是源码层面的语法选择tsup 无法判断开发者意图、更无法自动改写导入方式。这类问题只能在源码层面修复。修复方案与落地细节推荐方案修改源码导入方式按 FIX_SUMMARY.md 的记录实际采用的是最彻底、最符合 ESM 最佳实践的方案——将命名空间导入改为默认导入当前 src/index.ts 第 6 行已确认是修复后的写法// 修改前Bug import * as Joi from joi; // 修改后修复 import Joi from joi;配套配置更新单改源码还不够需要让 TypeScript 与 Jest 都认可默认导入 CJS 模块的写法1. tsconfig.json 添加allowSyntheticDefaultImportsvalidation-joi/tsconfig.json{ extends: ../../tsconfig.json, compilerOptions: { rootDir: src, outDir: dist, allowSyntheticDefaultImports: true } }2. jest.config.js 同步开启esModuleInterop让 ts-jest 在编译测试代码时也按默认导入语义处理 CJS 模块。备选方案对比FINDINGS.md 中还记录了另外两种备选思路可作技术参考方案做法优点缺点方案 A已采用import Joi from joi彻底解决、代码简洁、符合 ESM 最佳实践需改源码方案 Bimport * as JoiNs from joi; const Joi JoiNs.default \|\| JoiNs;同时兼容 CJS/ESM、改动最小少量运行时开销方案 Ctsup 配置format: [cjs]临时移除 ESM立竿见影、不影响 CJS失去 ESM 支持非长期方案验证结果从9/9 失败到36/36 全部通过修复后重新执行测试FIX_SUMMARY.md 记录了完整的验证结果✅ validation-zod: 9/9 方法通过 ✅ validation-zod4: 9/9 方法通过 ✅ validation-joi: 9/9 方法通过 ← 修复成功 ✅ validation-class-validator: 9/9 方法通过单元测试同步回归validation-joi的 Jest 套件35 passed, 3 skipped。至此 4 个包共 36 次方法调用全部通过CJS 用户不受任何影响ESM 用户恢复了完整的 schemaHelper 能力。关键洞察浅层测试给人虚假的安全感这次实践最有价值的结论是——测试不是为了通过而是为了发现问题。测试层次代码形态结果浅层检查assert.strictEqual(typeof fn, function)所有 4 个包全部通过 ✅深度调用const result fn()validation-joi 5/9 方法崩溃 ❌如果只停留在方法存在性检查validation-joi 的 ESM 构建56% 功能失效会带着严重缺陷直接发布给用户正是实际调用方法的深度测试让 Bug 在用户使用前就被拦截。从仓库现状看这次建立的模式已经固化为可复用的资产统一的test:dist入口、规范的esm-dist.test.mjs测试模板、清晰的中文错误报告未来任何新增的 tsup 构建包都可以直接套用同一套模板。经验教训与后续建议四条核心经验深度测试 浅层检查只有真实调用方法才能暴露运行时问题ESM/CJS 互操作很复杂import * as X from cjs-module与const X require(cjs-module)行为完全不同前者在 ESM 中拿到{ default: X }tsup 不是万能的它能处理好require()但无法自动改写源码中的import * as跨模块系统的兼容必须靠源码层面的正确写法测试的投资回报率高花几小时补测试避免了生产环境的一次严重故障。后续行动清单源自相关文档短期排查其他包是否也存在import * as导入 CJS 模块的隐患在代码评审中关注 CJS 模块的导入方式中期将test:dist完整接入 GitHub Actions CI 流程构建后执行为其他使用 tsup 的包扩展同类测试长期引入 ESLint 规则检测 CJS 模块的错误导入在构建阶段自动校验 ESM 导出的可用性在贡献者指南中补充如何测试构建产物章节。结语这次围绕 validation-* 包的构建产物测试实践完整走通了设计测试 → 发现问题 → 定位根因 → 源码修复 → 全量回归的闭环。它同时证明了两件事双模块CJS/ESM包的产物必须接受真实运行环境的检验以及只有深度调用测试才能把看起来正常和真正可用区分开。相关完整记录可在仓库的 add-validation-dist-tests 目录下查阅proposal.md、FINDINGS.md、SUMMARY.md、FIX_SUMMARY.md、FINAL_REPORT.md测试模板可直接参考 4 个包下的test/esm-dist.test.mjs文件。赞分享后端微服务云原生【免费下载链接】midway A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 项目地址https://gitcode.com/gh_mirrors/mi/midway点击查看免费下载相关推荐Midway validation-* 包构建产物测试实战从 test:dist 到捕获 ESM/CJS 互操作 BugMidway validation 包构建产物测试实战从 test:dist 到捕获 ESM/CJS 互操作 Bug 导读 本文以 Midway 开源仓库中后端微服务云原生XUnity Auto Translator终极指南3分钟学会为Unity游戏添加实时翻译XUnity Auto Translator终极指南3分钟学会为Unity游戏添加实时翻译 还在为看不懂外语游戏而烦恼吗XUnity Auto Transl后端微服务云原生Midway validation-* 包构建产物测试实战从 ESM 互操作 Bug 到可重复的 dist 测试框架Midway validation 包构建产物测试实战从 ESM 互操作 Bug 到可重复的 dist 测试框架 导读 本文围绕 Midway 仓库中 pac后端微服务云原生上一篇NuQS与Apollo Client集成GraphQL变量与URL参数下一篇Virgilio 开源项目解析以《神曲》为隐喻的三层数据科学学习路径与仓库结构创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考