代码转换
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)
可以看出,只有process
或processAsync
是必须实现的,尽管我们强烈建议也实现getCacheKey
,这样我们就不会浪费资源来传输之前从磁盘中读取过的源文件。 你还可以使用@jest/create-cache-key-function
来帮助你实现它
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之间的具体差异信息
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中有一种解决方法是将它们的文件名替换成导入值
const path = require('path');
module.exports = {
process(src, filename, config, options) {
return `module.exports = ${JSON.stringify(path.basename(filename))};`;
},
};
module.exports = {
transform: {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/fileTransformer.js',
},
};