代码转换
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 根据需要通过代码转换传递文件(例如,当评估 require
或 import
时)。 此过程也称为 "transpilation",可能同步发生(对于 require
),也可能异步发生(对于 import
或 import()
,后者也适用于 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 中处理它的一种方法是将导入的值替换为其文件名。
const path = require('path');
module.exports = {
process(sourceText, sourcePath, options) {
return {
code: `module.exports = ${JSON.stringify(path.basename(sourcePath))};`,
};
},
};
module.exports = {
transform: {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/fileTransformer.js',
},
};