跳转至主内容
Version: 27.x

代码转换

Jest在你的项目中以JavaScript的代码形式运行,但是如果你使用一些Node.js不支持的,却可以开箱即用的语法(如JSX,TypeScript中的类型,Vue模板等)那你就需要将代码转换为纯JavaScript,类似于上述语法在构建浏览器时将要做的事情。

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

转换器是为转换源文件提供同步功能的模块。 例如,如果希望能够在Node尚不支持的模块或测试中使用新的语言功能,可以插入许多编译器中的一个,这些编译器将JavaScript的未来版本编译为当前版本。

Jest将会缓存转换后的结果,并且试图让多方因素(比如正在转换的文件源和配置信息被修改等)造成的结果无效

默认值

Jest附带一个现成的转换器-babel Jest。 它将自动加载项目的Babel配置,并转换与以下正则表达式匹配的任何文件:/\.[jt]sx$/表示任何.js.jsx.ts.tsx文件。 此外,babel-jest还将会注入 ES Module mocking中所提到的Babel插件。

如果你覆盖了transform的配置选项,则babel-jest将不会生效,如果你还想要使用Babel的话,那么就得手动添加它。

编写自定义Transformers

你可以编写专属的Transformer, Transformer的API如下所示:

interface SyncTransformer<OptionType = unknown> {
/**
* Indicates if the transformer is capable of instrumenting the code for code coverage.
*
* If V8 coverage is _not_ active, and this is `true`, Jest will assume the code is instrumented.
* If V8 coverage is _not_ active, and this is `false`. Jest will instrument the code returned by this transformer using Babel.
*/
canInstrument?: boolean;
createTransformer?: (options?: OptionType) => SyncTransformer<OptionType>;

getCacheKey?: (
sourceText: string,
sourcePath: Config.Path,
options: TransformOptions<OptionType>,
) => string;

getCacheKeyAsync?: (
sourceText: string,
sourcePath: Config.Path,
options: TransformOptions<OptionType>,
) => Promise<string>;

process: (
sourceText: string,
sourcePath: Config.Path,
options: TransformOptions<OptionType>,
) => TransformedSource;

processAsync?: (
sourceText: string,
sourcePath: Config.Path,
options: TransformOptions<OptionType>,
) => Promise<TransformedSource>;
}

interface AsyncTransformer<OptionType = unknown> {
/**
* Indicates if the transformer is capable of instrumenting the code for code coverage.
*
* If V8 coverage is _not_ active, and this is `true`, Jest will assume the code is instrumented.
* If V8 coverage is _not_ active, and this is `false`. Jest will instrument the code returned by this transformer using Babel.
*/
canInstrument?: boolean;
createTransformer?: (options?: OptionType) => AsyncTransformer<OptionType>;

getCacheKey?: (
sourceText: string,
sourcePath: Config.Path,
options: TransformOptions<OptionType>,
) => string;

getCacheKeyAsync?: (
sourceText: string,
sourcePath: Config.Path,
options: TransformOptions<OptionType>,
) => Promise<string>;

process?: (
sourceText: string,
sourcePath: Config.Path,
options: TransformOptions<OptionType>,
) => TransformedSource;

processAsync: (
sourceText: string,
sourcePath: Config.Path,
options: TransformOptions<OptionType>,
) => Promise<TransformedSource>;
}

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

interface TransformOptions<OptionType> {
/**
* If a transformer does module resolution and reads files, it should populate `cacheFS` so that
* Jest avoids reading the same files again, improving performance. `cacheFS` stores entries of
* <file path, file contents>
*/
cacheFS: Map<string, string>;
config: Config.ProjectConfig;
/** A stringified version of the configuration - useful in cache busting */
configString: string;
instrument: boolean;
// names are copied from babel: https://babeljs.io/docs/en/options#caller
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://jestjs.io/docs/27.x/ecmascript-modules
*/
supportsStaticESM: boolean;
supportsTopLevelAwait: boolean;
/** the options passed through Jest's config by the user */
transformerConfig: OptionType;
}

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

// Config.ProjectConfig can be seen in code [here](https://github.com/jestjs/jest/blob/v26.6.3/packages/jest-types/src/Config.ts#L323)
// RawSourceMap comes from [`source-map`](https://github.com/mozilla/source-map/blob/0.6.1/source-map.d.ts#L6-L12)

可以看出,只有processprocessAsync是必须实现的,尽管我们强烈建议也实现getCacheKey,这样我们就不会浪费资源来传输之前从磁盘中读取过的源文件。 你还可以使用@jest/create-cache-key-function来帮助你实现它

note

ECMAScript module support is indicated by the passed in supports* options. 具体来说supportsDynamicImport: true 表示的是这个transformer可以返回ESM和CJS都支持的import()表达式。 而 supportsStaticESM: true 则表示的是支持最高级别的import语句,代码将被解释为ESM而不是CJS。 阅读 Node's docs了解ESM和CJS之间的具体差异信息

tip

Make sure TransformedSource contains a source map, so it is possible to report line information accurately in code coverage and test errors. Inline source maps also work but are slower.

例子

检查带有类型的TypeScript

babel-jest默认情况下会传输TypeScript文件,但Babel并不会对类型校验。 如果你需要校验类型你可以使用 ts-jest.

将图片转换为其路径

导入图像是将其包含在浏览器包中的一种方法,但它们不是有效的JavaScript。 在Jest中有一种解决方法是将它们的文件名替换成导入值

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

module.exports = {
process(src, filename, config, options) {
return `module.exports = ${JSON.stringify(path.basename(filename))};`;
},
};
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',
},
};