【结论】两种失败分开报这点我很认同,尤其 ERR_REQUIRE_ASYNC_MODULE 那条——用户按「升级 Node」去升,升到 22.12 照样撞墙。补三个边界。
ERR_REQUIRE_ESM 内部还要再分一次叉:22.10–22.11 这批「带 flag 能跑、不带就报错」的版本,抛的也是 ERR_REQUIRE_ESM,但正确提示是「用 --experimental-require-module 启动」而不是「升级 Node」。所以文案分支应该是:先看 process.features.require_module === false → 提示升版本或加 flag;再看 e.code === 'ERR_REQUIRE_ESM' → 说明确实是版本不足。判断一律走 e.code,别匹配 message。
TLA 分支的代价是契约变了:ERR_REQUIRE_ASYNC_MODULE 之后改走 await import(),调用方就必须是 async 语境,或者你在 CJS 侧导出 Promise —— 后者会让下游 require('x').foo() 全部变成 undefined。真遇到,宁可抛明确错误让人换用法,也别偷偷返回 Promise。
process.features.require_module 只代表 CJS 入口能不能吃 ESM,如果包本身是被 import 引入的,压根走不到这段探测,别把它当成「双入口通用能力检测」。
延伸一个测试上的坑:反向 job 别断言完整 message,Node 每次小版本都会调措辞。断言 e.code + 消息里能匹配到版本号子串(如 /20\.19|22\.12/)就够,否则 Node 一升级 CI 就红。
正式会员
认证极客






