DEBUG.WIKI
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国

ts-node 报未知扩展名

启用 pnpm 全局虚拟仓库后,ts-node 报 ERR_UNKNOWN_FILE_EXTENSION。检查注入的 --import 如何改变主入口,核对 12.11.2 分类修复与模块约定。

启用 pnpm 的全局虚拟仓库后,原来能执行的 ts-node index.ts 报 ERR_UNKNOWN_FILE_EXTENSION。同一项目关闭这项设置又正常时,应检查 pnpm 注入的 NODE_OPTIONS 是否改变了入口加载路径,不要先把整份项目改成 ESM。

已有具体环境是 pnpm 12.8.0、ts-node 10.9.2 与 Node 20.20.2。已核对的正式 pnpm 12.11.2 加载器包含相应入口分类修复。这个问题不等于所有 .ts 扩展名错误,也不能仅凭 Node 24 看似正常,就判断旧加载钩子已恢复。

全局虚拟仓库为什么会影响入口

全局虚拟仓库把包目录放在项目以外,部分提升依赖通过 NODE_PATH 可达。Node 原生 ESM 不按普通 CommonJS 的方式使用 NODE_PATH,因此 pnpm 注入一个 --import 加载钩子来补齐相关解析。

但 --import 不只加载一个辅助模块,也会让主入口经过 ESM 加载器。原来依靠 CommonJS require.extensions 钩子识别 .ts 的入口,在这条路径上可能先被拒绝,尚未轮到 ts-node 处理。

这不能简单归结为旧 Node 没有 module.registerHooks()。已核对的 Node 24.21.0 主入口选择规则同样会因 --import 使用 ESM;某些新 Node 能原生去掉 TypeScript 类型,使 .ts 例子看起来恢复,却不证明其他自定义扩展名的 CommonJS 钩子也可用。

对照开关、入口和实际注入选项

在原脚本环境核对 pnpm、Node、ts-node 的实际版本,查看项目是否设置了 enableGlobalVirtualStore: true,以及启动器最终传给 Node 的选项。只看交互终端里未经过 pnpm 的 NODE_OPTIONS,可能看不到安装或运行阶段加入的加载器。

保留原入口的模块约定:它是否原本使用 CommonJS 钩子、项目是否声明 type: module、错误是在加载入口还是解析后续导入时产生。一个已经进入 ESM 的项目,相对 import 缺少输出扩展名,是另一条ts-node 路径问题。

不要批量清空 NODE_OPTIONS。里面可能还有项目需要的预加载、内存或其他设置;诊断应先找出这次全局虚拟仓库增加的那一项。

选择带入口分类修复的版本

项目兼容时,按既有包管理器安装方式选择包含修复的正式版本,例如 pnpm 12.11.2。该加载器会把 ESM 无法分类的文件主入口交回 CommonJS,以保留原来的自定义扩展名处理;.js 和无扩展名入口仍保留 Node 原本的模块识别规则。

不要把内部实现照抄成一份全局 loader,也不要强制把所有入口标成 commonjs。实际 ESM 文件若被错误分类,会产生新的语法或加载错误。

暂时不能更新时,可以在保留现有配置和锁文件后,评估仅对受影响项目关闭全局虚拟仓库:

enableGlobalVirtualStore: false

这是合入现有工作区设置的片段。它会改变包的存放和解析方式,需按项目原有安装流程重新验证,不应把已有依赖目录直接删除后当作无影响切换。

从原来的 pnpm 脚本入口检查 ts-node 是否实际处理并执行了 .ts 文件,所需依赖是否仍能解析;再检查项目已有的 ESM 入口与类型检查流程。脚本输出一行正常结果,不能证明模块模式和类型检查都符合原约定。