自定义TypeScript Transformer配合ts-node运行失效问题
问题原因
这个问题的核心原因是 ttypescript@1.5.13 没有适配 ts-node@10.x 的 ESM 运行模式,二者在编译钩子对接上存在兼容缺陷:
- 开启
ts-node的esm: true配置后,ts-node会先生成UnparsedSource(kind值305)类型的预解析节点做模块加载缓存 - ttypescript在ESM模式下没有正确挂载到TypeScript的完整编译流程,直接把这个未完成语法解析的预解析节点传给了自定义转换器,没有生成标准的
SourceFile节点 - 调用
getChildrenCount()这类仅SourceFile节点支持的方法时,就会触发TypeScript内部的调试断言报错,遍历节点时也只能拿到UnparsedSource类型的节点。
另外两个配置问题会放大这个兼容问题:
- 根目录
compilerOptions.plugins里给转换器加了"type": "raw"配置,这个配置仅给ttsc命令行编译使用,ts-node运行时不会识别该参数,反而会干扰ttypescript的插件加载逻辑 - 在
builder目录单独放置"type": "module"的package.json,会导致ts-node加载转换器文件时走独立的ESM模块解析逻辑,和ttypescript默认的CommonJS转换器加载逻辑冲突。
修复方案
按稳定性优先级排序:
- 方案1(推荐,长期可用):放弃ttypescript,改用ts-patch给TypeScript打补丁的方式支持自定义转换器,完全兼容ts-node ESM模式
操作步骤:- 卸载不维护的ttypescript:
npm uninstall ttypescript - 安装维护状态正常的ts-patch:
npm install -D ts-patch - 执行补丁注入命令:
npx ts-patch install - 修改tsconfig.json里的ts-node配置,删除
"compiler": "ttypescript"字段,ts-patch会直接修改原生typescript的编译逻辑,不需要替换编译器 - 删除根compilerOptions里插件配置的
"type": "raw"字段,删除builder目录下单独的package.json,转换器文件和项目其他文件共用根目录的ESM配置即可 - 把插件配置统一放到根compilerOptions里,不需要单独给ts-node写重复的plugins配置,最终相关配置参考如下:
{ "compilerOptions": { "target": "ESNext", "module": "ESNext", "moduleResolution": "node", "outDir": "./build", "esModuleInterop": true, "strict": true, "skipLibCheck": true, "plugins": [ { "transform": "./builder/transformer.ts", "after": true } ] }, "ts-node": { "esm": true, "experimentalSpecifierResolution": "node" }, "include": ["src"] } - 卸载不维护的ttypescript:
- 方案2(临时兼容,不需要换工具链):关闭ts-node的ESM模式,改用CommonJS模式运行
操作步骤:- 删除根目录package.json里的
"type": "module"字段 - 把tsconfig里的
module配置改成CommonJS,删除ts-node配置里的esm: true和experimentalSpecifierResolution字段 - 删除builder目录下单独的package.json,转换器按CommonJS规范编写即可
该方案下ttypescript可以正常工作,但项目无法使用原生ESM模块特性。
- 删除根目录package.json里的
- 方案3(保留ESM的临时方案):提前把自定义转换器编译成CommonJS格式,不要让ts-node直接加载ts格式的转换器源文件
操作步骤:- 新增独立的tsconfig配置,把builder目录下的转换器提前编译成CommonJS规范的js文件
- tsconfig里的插件
transform路径指向编译后的js文件,不要指向ts源文件 - 调整builder目录下package.json的配置,把编译后的转换器文件标记为CommonJS模块类型
该方案不需要替换工具链,但每次修改转换器代码都要重新编译,调试效率低。
内容的提问来源于stack exchange,提问作者Bagel03
相关产品推荐
相关产品推荐

