Watch at Ease看见世界,读懂生活。

科技 · 实用指南

Node 报 ERR_REQUIRE_ESM?改模块设置前先追踪运行时调用方

源码可能写着 import,但编译后的 JavaScript、某个依赖或测试运行器仍在调用 require()。先证明运行时边界,再选择完整迁移 ESM、动态 import 桥接或经过评审的临时版本固定。

原创技术图:从源码、编译输出和包元数据追踪到 Node 运行时。
原创示意图:改全局模块设置前先沿运行路径追踪。 Watch at Ease

ERR_REQUIRE_ESM 指向运行时的模块格式边界。Node 实际执行的行不一定是你写下的行:TypeScript、Babel、Vite、Electron、Jest 或传递依赖都可能生成或调用 require()。

先保留第一份完整堆栈,记录精确 Node 版本,并检查实际执行的 JavaScript,再改 package.json。修复取决于谁拥有 CommonJS 调用方,以及目标 ESM 图能否同步加载。

先冻结运行时证据

保存完整堆栈、命令、工作目录、Node 与包管理器版本及 lockfile 修订。标出目标 ESM 文件和 Node 内部之外的首个调用方。

不要先删 node_modules 或 lockfile;那会改变依赖图并抹掉原始失败边界。

检查真正执行的文件

打开堆栈中的调用方。如果它位于 dist、out、build 或 Electron bundle,确认输出行是否仍是 require()。

TypeScript 项目记录 module、moduleResolution、调用方扩展名及最近 package.json 的 type。node16、node18、nodenext 才会按文件模拟 Node 行为。

映射包边界和依赖所有者

用 lockfile 与依赖树确认精确包版本,并判断 CommonJS 调用属于你的代码、直接依赖还是传递依赖;读取该包的 exports 与 type。

同时记录 Node 版本。特定版本解析回归或包含顶层 await 的图,与普通 ESM-only 升级不是同一修法。

只选择一条修复路径

能一致迁移时,同时调整包边界、编译输出、相对扩展名与运行入口,并测试所有入口。必须保留 CommonJS 时,只在可异步化的自有边界使用动态 import。

调用位于依赖内时优先升级到支持当前运行时的上游版本。临时固定旧版必须记录原因、安全暴露与移除日期。

在同一边界验证

清空旧输出后重建,但不要意外重写 lockfile。确认执行文件不再用 require 调 ESM-only 目标,或桥接返回了正确导出。

用同一 Node 版本运行原命令、测试与生产入口;保存最终依赖树和输出片段。

原创技术图:一个模块格式冲突分成三条修复路线并汇合到验证结果。
原创示意图:先证明边界,再迁移、桥接或临时固定。 Watch at Ease