ts-jest 自定义 AST Transformer 完全指南:astTransformers 配置、编写与实战
测试开发工具【免费下载链接】ts-jestA Jest transformer with source map support that lets you use Jest to test projects written in TypeScript.项目地址https://gitcode.com/gh_mirrors/ts/ts-jest点击查看免费下载astTransformers是ts-jest提供的核心扩展点允许开发者在 Jest 编译 TypeScript 的过程中注入自定义的 TypeScript AST transformer实现源码级的代码转换与钩子注入。本文以ts-jest仓库中的官方文档为骨架结合 types 定义、配置解析实现 与端到端测试用例系统讲解before/after/afterDeclarations三种阶段的区别、两种配置写法、自定义 transformer 的模块契约以及真实项目中的集成案例——读完你可以独立写出并接入自己的 AST transformer。一、astTransformers是什么ts-jest默认会通过一个 TypeScript AST transformer 对少数jest方法如jest.mock、jest.unmock等执行 hoisting提升操作保证它们在使用前就绪。这是ts-jest内建行为不需要用户干预。同时ts-jest把这条通道开放给了用户你可以编写自定义的 TypeScript AST transformer并通过astTransformers选项把它纳入编译流程。该选项的类型定义如下见 src/types.tsexport interface AstTransformerT Recordstring, unknown { path: string options?: T } export interface ConfigCustomTransformer { before?: Arraystring | AstTransformer after?: Arraystring | AstTransformer afterDeclarations?: Arraystring | AstTransformer }可以看到每个阶段都接受两种形态的元素纯字符串路径或带options的对象{ path, options }。三个执行阶段astTransformers允许你指定三种类型的 TypeScript AST transformer它们分别对应 TypeScript 编译器中CustomTransformers的三个阶段阶段含义拿到的语法形态before在 TypeScript 自身的 transformers之前运行原始 TS 语法例如import语句尚未转成require或defineafter在 TypeScript 自身的 transformers之后运行转译后的语法例如require/define等 CommonJS 或模块包装形态afterDeclarations在d.ts声明文件生成阶段运行输出的类型声明.d.ts可在此改写类型输出简而言之before处理人写的源码after处理机器转出来的代码afterDeclarations处理对外发布的类型声明。三种阶段既可以单独使用也可以同时配置。二、基础配置使用 transformer 字符串路径最简单的用法是在before中直接给出 transformer 模块的路径import type { Config } from jest const jestConfig: Config { // [...] transform: { // ^.\\.[tj]sx?$ 处理 ts,js,tsx,jsx // ^.\\.m?[tj]sx?$ 处理 ts,js,tsx,jsx,mts,mjs,mtsx,mjsx ^.\\.tsx?$: [ ts-jest, { astTransformers: { before: [my-custom-transformer], }, }, ], }, } export default jestConfigts-jest在解析阶段会把字符串路径当作模块路径解析使用 Node 风格解析即nodeResolve: true见 config-set.ts 中的resolveTransformers实现const resolveTransformers (transformers: Arraystring | AstTransformer): AstTransformerDesc[] transformers.map((transformer) { if (typeof transformer string) { return resolveTransformerFunc(this.resolvePath(transformer, { nodeResolve: true })) } else { return { ...resolveTransformerFunc(this.resolvePath(transformer.path, { nodeResolve: true })), options: transformer.options, } } })因此路径可以指向一个 npm 包名如my-custom-transformer也可以指向仓库内或 node_modules 中的相对/绝对路径。三、带 options 的配置为 transformer 传递参数很多 transformer 需要接收配置参数。此时把元素从字符串升级为对象import type { Config } from jest const jestConfig: Config { // [...] transform: { // ^.\\.[tj]sx?$ 处理 ts,js,tsx,jsx // ^.\\.m?[tj]sx?$ 处理 ts,js,tsx,jsx,mts,mjs,mtsx,mjsx ^.\\.tsx?$: [ ts-jest, { astTransformers: { before: [ { path: my-custom-transformer-that-needs-extra-opts, options: {}, // 在此传入 transformer 需要的额外配置 }, ], }, }, ], }, } export default jestConfig这些options最终会作为factory(compilerInstance, opts)的第二个参数传给 transformer 工厂函数对应 AstTransformerDesc 中factory(tsCompiler, opts?)的签名。重要限制options配置值会被jest-worker序列化因此只允许传可序列化SERIALIZABLE的值——即 JSON 能表示的数据类型。函数、类实例、RegExp、Map等不可序列化对象不应出现在options中否则在 worker 进程间传递时会出问题。仓库端到端测试 e2e/transformer-options/jest-compiler-cjs.config.ts 给出了带 options 的真实用法——接入formatjs/ts-transformer并关闭默认消息提取astTransformers: { before: [ { path: rootDir/node_modules/formatjs/ts-transformer/ts-jest-integration, options: { removeDefaultMessage: true, }, }, ], },对应测试 transformer-options.spec.tsx 验证了defaultMessage确实被移除import App from ../src/App describe(transformer-options, () { it(should pass, () { expect(App.prototype.render().props.defaultMessage).toBeUndefined() }) })四、编写自定义 TypeScript AST transformer自定义 transformer 本质上是一个普通 JS/TS 模块需要导出一个固定形态的模块version、name与factory。ts-jest自己使用的 transformer见 src/transformers/hoist-jest.ts就是最好的范本/** * 每次 transformer 内容变化时记得提升 version。 * 这是为了告知 Jest 不要复用包含旧 transformer 内容的缓存 */ export const version 4 // 用于构造缓存键 export const name hoist-jest export function factory({ configSet }: TsCompilerInstance) { // ... }模块契约详解version: number缓存版本号。修改 transformer 实现后必须递增否则 Jest 可能复用旧缓存导致变更不生效name: stringtransformer 名称参与缓存键构造factory(compilerInstance, options?)返回一个(ctx) (sf) ...形式的 TypeScript transformer 工厂。src/transformers/README.md见 README.md给出了一个可直接套用的 boilerplateimport { SourceFile, TransformationContext, Transformer, Visitor } from typescript import type { TsCompilerInstance } from ts-jest/dist/types /** * Remember to increase the version whenever transformers content is changed. This is to inform Jest to not reuse * the previous cache which contains old transformers content */ export const version 1 // Used for constructing cache key export const name hoist-jest export function factory(compilerInstance: TsCompilerInstance) { const ts compilerInstance.configSet.compilerModule function createVisitor(ctx: TransformationContext, sf: SourceFile) { const visitor: Visitor (node) { // 在此检查每个节点必要时返回新节点 // 若保持节点不变则继续遍历子节点 return ts.visitEachChild(node, visitor, ctx) } return visitor } // 返回 CustomTransformers 所期望的工厂 return (ctx: TransformationContext): TransformerSourceFile { return (sf: SourceFile) ts.visitNode(sf, createVisitor(ctx, sf)) } }注意factory的第一个参数是TsCompilerInstance它携带configSet、compilerModule等信息你可以在 transformer 内访问用户配置与当前typescript模块实例。仓库中还有一个最简实现 src/mocks/funny-transformer.ts它只是原样返回源码文件可作为最小的空 transformer模板export const version 1 export const name funny-transformer export function factory(): TransformerFactorySourceFile { return () { return (sf: SourceFile) sf } }内建 hoist-jest transformer 的工作机制ts-jest默认在before阶段注入的 hoist-jest transformersrc/transformers/hoist-jest.ts负责对mock、unmock、enableAutomock、disableAutomock、deepUnmock这五个 jest 方法进行源码提升。它支持多种引用形式见 hoist-jest.ts命名导入import { jest } from jest/globals别名导入import { jest as aliasedJest } from jest/globals命名空间导入import * as JestGlobals from jest/globals它通过visitEachChild自底向上遍历 AST对可提升的语句块排序hoist-jest.ts。理解这个内建 transformer 的实现是编写高质量自定义 transformer 的最佳起点。五、底层原理ts-jest 如何解析与装配 transformersastTransformers的解析发生在ConfigSet初始化阶段见 config-set.ts核心流程如下默认注入内建 transformerresolvedTransformers.before首先被置为[hoistJestTransformer]factory/name/version三元组。解析每个自定义 transformer对.ts后缀的 transformer 源文件使用esBuild先转译为 CJS 再加载config-set.ts其他路径直接require若模块缺少version或name会打印对应警告MissingTransformerVersion/MissingTransformerName。按阶段装配before追加到内建 hoist transformer 之后resolvedTransformers.before?.push(...)after直接覆盖resolvedTransformers.afterafterDeclarations直接覆盖resolvedTransformers.afterDeclarations。装配完成后这些 transformers 会被传入 TypeScript 编译器。在编译路径上ts-compiler.ts 通过transpileModule或tsTranspileModule把resolvedTransformers传入在需要完整程序program的场景下则通过getCustomTransformers提供给语言服务ts-compiler.ts。最终解析结果对应 src/types.ts 中定义的TsJestAstTransformerexport interface TsJestAstTransformer { before: AstTransformerDesc[] after: AstTransformerDesc[] afterDeclarations: AstTransformerDesc[] }编写 transformer 的两种语言形态从解析流程可知transformer 模块有两种书写方式JS 模块直接导出version/name/factory如 src/mocks/funny-transformer.ts 经构建后的产物TS 源文件以.ts结尾的 transformer 文件也会被支持——ts-jest会用 esBuild 现场转译后加载。端到端测试 e2e/transformer-in-ts/jest-compiler-cjs.config.ts 就是直接把src/transformers/hoist-jest.ts当作beforetransformer 使用astTransformers: { before: [ { path: rootDir/../../src/transformers/hoist-jest.ts, }, ], },六、实战建议与注意事项1. 阶段选择决定代码形态需要操作源码级语义如重写import、处理装饰器、插入jest.mock提升逻辑→ 用before需要操作转译后代码如改写require调用、处理模块包装→ 用after需要改写对外类型声明→ 用afterDeclarations。2. 保持缓存版本纪律version字段直接关系到 Jest 的转换缓存。每次修改 transformer 逻辑都要递增version否则可能出现代码改了但测试跑的仍是旧转换结果的诡异问题。3. 遵守序列化约束options会被jest-worker序列化传递到 worker 进程只能传 JSON 可序列化的数据。复杂对象函数、类实例、Map/Set请改用其他方式传递例如通过模块闭包或环境变量。4. 路径解析ts-jest会以nodeResolve方式解析 transformer 路径因此既可以是 npm 包名也可以是绝对/相对路径对.ts源文件会自动转译加载。在 Jest 配置中推荐使用rootDir前缀如rootDir/node_modules/formatjs/ts-transformer/ts-jest-integration以保证路径与项目根目录对齐。5. 与 typescript 模块保持同一实例在factory内部优先通过compilerInstance.configSet.compilerModule获取ts模块如 boilerplate 所示而不是自行import * as ts from typescript。这样能确保 AST 节点判断ts.isXxx与工厂函数与ts-jest实际使用的编译器实例一致避免跨版本类型判断失效。七、总结astTransformers是ts-jest面向需要自定义编译行为场景的一等扩展点通过before/after/afterDeclarations三阶段配置你可以精确选择在原始源码、转译后代码或类型声明三个层面介入编译过程。配合字符串路径或{ path, options }对象两种配置形态、version/name/factory的模块契约以及仓库中 hoist-jest.ts、funny-transformer.ts 等现成范本你可以快速落地 i18n 消息提取、代码混淆、装饰器增强、类型改写等自定义编译能力同时保持与 Jest 缓存、worker 序列化体系的兼容。进一步参考配置解析实现见 config-set.ts类型定义见 types.ts编写指南见 src/transformers/README.md端到端验证用例见 transformer-options 与 transformer-in-ts。赞分享测试开发工具【免费下载链接】ts-jestA Jest transformer with source map support that lets you use Jest to test projects written in TypeScript.项目地址https://gitcode.com/gh_mirrors/ts/ts-jest点击查看免费下载相关推荐ts-jest 的 astTransformers 配置指南编写并接入自定义 TypeScript AST Transformerts jest 的 astTransformers 配置指南编写并接入自定义 TypeScript AST Transformer ts jest 在把 Ty测试开发工具ts-jest 自定义 TypeScript AST 变换器astTransformers配置与编写完整指南ts jest 自定义 TypeScript AST 变换器astTransformers配置与编写完整指南 ts jest 在将 TypeScript 代测试开发工具ts-jest 的 astTransformers 选项在 Jest 编译管线中注入自定义 TypeScript AST Transformer 的完整指南ts jest 的 astTransformers 选项在 Jest 编译管线中注入自定义 TypeScript AST Transformer 的完整指南测试开发工具上一篇Wand-Enhancer终极指南3步解锁WeMod完整功能告别Pro限制下一篇如何将Windows电脑变成AirPlay接收器5步快速部署指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

90DaysOfDevOps Day 83:Grafana 数据可视化实战——基于 kube-prometheus 与 Prometheus Operator 部署监控面板

90DaysOfDevOps Day 83:Grafana 数据可视化实战——基于 kube-prometheus 与 Prometheus Operator 部署监控面板

文档/教程 【免费下载链接】90DaysOfDevOps This repository started out as a learning in public project for myself and has now become a structured learning map for many in the community. We have 3 years under our belt covering all things DevOps, including Pri…

2026/10/9 17:08:58 阅读更多 →
学校心理咨询聊天室毕设源码:小程序+SSM+MySQL全链路解析

学校心理咨询聊天室毕设源码:小程序+SSM+MySQL全链路解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 17:09:20 阅读更多 →
OpenRig:基于Node.js+tmux的本地化AI开发环境构建指南

OpenRig:基于Node.js+tmux的本地化AI开发环境构建指南

1. 项目概述:OpenRig 是什么,它解决的到底是什么问题?OpenRig 这个名字乍一听像某种硬件矿机管理工具,或是开源的图形渲染集群调度器——但结合当前热词里高频出现的Node.js、tmux、Claude、Codex,再叠加“cc switch l…

2026/10/9 17:09:45 阅读更多 →

最新新闻

SwiftUI中的Agent+Skill框架:摆脱if-else,构建可扩展的任务调度系统

SwiftUI中的Agent+Skill框架:摆脱if-else,构建可扩展的任务调度系统

项目名我起了个SwiftUI-Agent-Skill,翻译过来没啥歧义:一套跑在SwiftUI应用里的Agent技能框架。Agent在这里不是玄学,就是一个负责拆解用户意图、派发任务、汇总结果的调度核心;Skill则是挂在它下面的一个个独立能力模块&#xff…

2026/10/13 16:49:38 阅读更多 →
MySQL图书管理系统数据库设计:从库表建模到并发事务与故障排查

MySQL图书管理系统数据库设计:从库表建模到并发事务与故障排查

简介:一份基于MySQL的图书管理系统数据库设计完整文档,面向需要完成数据库课程设计、毕业设计或参与图书管理项目开发的人员。文档结构完整,从题目概述出发,依次展开需求分析、功能需求、数据需求、数据安全与约束、数据流图、概要…

2026/10/13 16:49:38 阅读更多 →
黎明杀机联动尼尔崩溃?小蓝熊EAC与UE5闪退完整修复指南

黎明杀机联动尼尔崩溃?小蓝熊EAC与UE5闪退完整修复指南

先说明一下,我不是什么专业程序调试员,就是个在黎明杀机里泡了上千小时的普通玩家。前两天《黎明杀机》和《尼尔》联动上线,我兴冲冲买了DLC准备开爽,结果一进游戏就遭遇了连环暴击——先是小蓝熊EAC无限转圈加载,好不…

2026/10/13 16:49:38 阅读更多 →
LiteLLM 实战:用统一模型网关解决多模型接入与治理难题

LiteLLM 实战:用统一模型网关解决多模型接入与治理难题

做 AI 应用开发这几年,模型接口这块我真没少折腾。早期项目只用一家模型供应商,代码里直接对接对方 SDK,写起来还算顺手。等产品上线、需求一变就傻眼了:A 模型偶尔限流、B 模型便宜但效果差一点、客户指定要用 C 模型的私有部署&…

2026/10/13 16:49:38 阅读更多 →
多模态Agent UI测试路径规划实战:从候选集到失败回溯

多模态Agent UI测试路径规划实战:从候选集到失败回溯

最近在折腾一套用多模态 Agent 跑 UI 测试的验证项目。我给它一个纯文字目标——“打开控制台,给默认项目创建一个新的 API Key”——它就得自己看懂页面、找到入口、填表单、提交,最后确认结果。最开始我以为难点在“怎么让模型能控制浏览器”&#xff…

2026/10/13 16:49:38 阅读更多 →
从套壳MCP到研究决策系统:AI工具如何演进成完整工作流

从套壳MCP到研究决策系统:AI工具如何演进成完整工作流

被同事问得最多的一句话是:“你不是说只做个套壳MCP吗?怎么越写越像一个研究决策系统了?”起先我还会解释几句,后来问的人多了,我干脆把整个演进过程重新捋了一遍,发现这事本身就挺有意思:明明一…

2026/10/13 16:48:36 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →