跳至主要内容
版本:29.7

代码转换

Jest 将项目中的代码运行为 JavaScript,但如果使用了一些 Node 默认不支持的语法(例如 JSX、TypeScript、Vue 模板),则需要将这些代码转换为纯 JavaScript,类似于为浏览器构建时所做的那样。

Jest 通过 transform 配置选项支持此功能。

转换器是一个模块,它提供了一种转换源文件的方法。例如,如果希望在模块或测试中使用 Node 尚未支持的新语言特性,可以插入一个代码预处理器,将未来版本的 JavaScript 转换为当前版本。

Jest 会缓存转换结果,并尝试根据一些因素(例如被转换文件的来源和配置更改)使缓存结果失效。

默认值

Jest 默认提供了一个转换器 - babel-jest。它将加载项目的 Babel 配置,并转换任何与 /\.[jt]sx?$/ 正则表达式匹配的文件(换句话说,任何 .js.jsx.ts.tsx 文件)。此外,babel-jest 将注入 Babel 插件,该插件对于 ES 模块模拟 中提到的模拟提升是必需的。

提示

如果希望与其他代码预处理器一起使用,请记住显式包含默认的 babel-jest 转换器。

"transform": {
"\\.[jt]sx?$": "babel-jest",
"\\.css$": "some-css-transformer",
}

编写自定义转换器

可以编写自己的转换器。转换器的 API 如下所示

interface TransformOptions<TransformerConfig = unknown> {
supportsDynamicImport: boolean;
supportsExportNamespaceFrom: boolean;
/**
* The value is:
* - `false` if Jest runs without Node ESM flag `--experimental-vm-modules`
* - `true` if the file extension is defined in [extensionsToTreatAsEsm](Configuration.md#extensionstotreatasesm-arraystring)
* and Jest runs with Node ESM flag `--experimental-vm-modules`
*
* See more at https://jest.node.org.cn/docs/next/ecmascript-modules
*/
supportsStaticESM: boolean;
supportsTopLevelAwait: boolean;
instrument: boolean;
/** Cached file system which is used by `jest-runtime` to improve performance. */
cacheFS: Map<string, string>;
/** Jest configuration of currently running project. */
config: ProjectConfig;
/** Stringified version of the `config` - useful in cache busting. */
configString: string;
/** Transformer configuration passed through `transform` option by the user. */
transformerConfig: TransformerConfig;
}

type TransformedSource = {
code: string;
map?: RawSourceMap | string | null;
};

interface SyncTransformer<TransformerConfig = unknown> {
canInstrument?: boolean;

getCacheKey?: (
sourceText: string,
sourcePath: string,
options: TransformOptions<TransformerConfig>,
) => string;

getCacheKeyAsync?: (
sourceText: string,
sourcePath: string,
options: TransformOptions<TransformerConfig>,
) => Promise<string>;

process: (
sourceText: string,
sourcePath: string,
options: TransformOptions<TransformerConfig>,
) => TransformedSource;

processAsync?: (
sourceText: string,
sourcePath: string,
options: TransformOptions<TransformerConfig>,
) => Promise<TransformedSource>;
}

interface AsyncTransformer<TransformerConfig = unknown> {
canInstrument?: boolean;

getCacheKey?: (
sourceText: string,
sourcePath: string,
options: TransformOptions<TransformerConfig>,
) => string;

getCacheKeyAsync?: (
sourceText: string,
sourcePath: string,
options: TransformOptions<TransformerConfig>,
) => Promise<string>;

process?: (
sourceText: string,
sourcePath: string,
options: TransformOptions<TransformerConfig>,
) => TransformedSource;

processAsync: (
sourceText: string,
sourcePath: string,
options: TransformOptions<TransformerConfig>,
) => Promise<TransformedSource>;
}

type Transformer<TransformerConfig = unknown> =
| SyncTransformer<TransformerConfig>
| AsyncTransformer<TransformerConfig>;

type TransformerCreator<
X extends Transformer<TransformerConfig>,
TransformerConfig = unknown,
> = (transformerConfig?: TransformerConfig) => X;

type TransformerFactory<X extends Transformer> = {
createTransformer: TransformerCreator<X>;
};
注意

为了简洁,上面的定义被简化了。完整的代码可以在 Jest GitHub 仓库 中找到(请记住选择与 Jest 版本匹配的标签/提交)。

有两种方法可以将代码导入 Jest - 使用 Common JS (require) 或 ECMAScript 模块 (import - 存在于静态和动态版本中)。Jest 按需通过代码转换处理文件(例如,当 requireimport 被评估时)。这个过程也称为“转译”,可能同步发生(在 require 的情况下),也可能异步发生(在 importimport() 的情况下,后者也可以从 Common JS 模块中工作)。因此,接口同时公开异步和同步过程的两种方法对:process{Async}getCacheKey{Async}。后者用于确定是否需要调用 process{Async}

如果 processAsync 未实现,异步转译可以回退到同步 process 调用,但同步转译不能使用异步 processAsync 调用。如果代码库仅限于 ESM,则实现异步变体就足够了。否则,如果任何代码通过 require 加载(包括来自 ESM 中的 createRequire),则需要实现同步 process 变体。

请注意,node_modules 不会使用默认配置进行转译,需要修改 transformIgnorePatterns 设置才能进行转译。

与之相关的还有我们传递的支持标志(见上面的 CallerTransformOptions),但这些标志应该在转换器内部使用,以确定它应该返回 ESM 还是 CJS,并且与同步与异步无关。

虽然不是必需的,但我们强烈建议也实现 getCacheKey,这样我们就可以避免浪费资源进行转译,而可以从磁盘读取其先前结果。可以使用 @jest/create-cache-key-function 来帮助实现它。

自定义转换器不必直接实现 Transformer 接口,可以选择导出 createTransformer,这是一个用于动态创建转换器的工厂函数。这样就可以在 Jest 配置中配置转换器。

注意

ECMAScript 模块 支持由传递的 supports* 选项指示。具体来说,supportsDynamicImport: true 表示转换器可以返回 import() 表达式,这在 ESM 和 CJS 中都受支持。如果 supportsStaticESM: true,则表示支持顶层 import 语句,代码将被解释为 ESM 而不是 CJS。有关差异的详细信息,请参阅 Node 文档

提示

确保 process{Async} 方法返回转换后的代码以及源映射,以便在代码覆盖率和测试错误中准确地报告行信息。内联源映射也适用,但速度较慢。

在开发转换器期间,使用 --no-cache 运行 Jest 可以帮助频繁地 删除缓存

示例

带有类型检查的 TypeScript

虽然 babel-jest 默认会转译 TypeScript 文件,但 Babel 不会验证类型。如果需要验证类型,可以使用 ts-jest

将图像转换为其路径

导入图像是在浏览器包中包含图像的一种方式,但它们不是有效的 JavaScript。在 Jest 中处理此问题的一种方法是将导入的值替换为其文件名。

fileTransformer.js
const path = require('path');

module.exports = {
process(sourceText, sourcePath, options) {
return {
code: `module.exports = ${JSON.stringify(path.basename(sourcePath))};`,
};
},
};
jest.config.js
module.exports = {
transform: {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/fileTransformer.js',
},
};