Skip to main content
Version: 29.7

代码转换


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

Jest 通过 transform 配置选项支持这一点。

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

Jest 将缓存转换的结果,并尝试根据多种因素使该结果无效,例如正在转换的文件的源和更改的配置。

默认值

Jest 附带一台开箱即用的转换器 – babel-jest。 它将加载项目的 Babel 配置并转换与 /\.[jt]sx?$/ RegExp 匹配的任何文件(换句话说,任何 .js.jsx.ts.tsx 文件)。 另外,babel-jest 将注入 ES 模块模拟 中谈到的模拟提升所需的 Babel 插件。

提示

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

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

编写自定义转换器

你可以编写自己的转换器。 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.nodejs.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>;
};
注意

为简洁起见,对上述定义进行了精简。 完整代码可以在 GitHub 上的 Jest 存储库 中找到(请记住为你的 Jest 版本选择正确的标签/提交)。

你可以通过多种方式将代码导入 Jest - 使用 Common JS (require) 或 ECMAScript 模块(import - 存在静态和动态版本)。 Jest 根据需要通过代码转换传递文件(例如,当评估 requireimport 时)。 此过程也称为 "transpilation",可能同步发生(对于 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 来帮助实现它。

你可以选择导出 createTransformer(一个用于动态创建转换器的工厂函数),而不是让自定义转换器直接实现 Transformer 接口。 这是为了允许在你的 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',
},
};