コードの変換
Jest はプロジェクト内のコードを JavaScript として実行しますが、Node でサポートされていない構文 (JSX、TypeScript、Vue のテンプレート) を使用している場合は、ブラウザ用にビルドする場合と同様に、コードをプレーンな JavaScript に変換する必要があります。
Jest は、このような 変換を transform
設定オプションによりサポートしています。
transformer はソースファイルを変換するメソッドを提供するモジュールです。 たとえば、まだ Node ではサポートされていない新しい言語の機能をモジュールやテストで使用したい場合、将来の JavaScript のバージョンを現在のバージョンにトランスパイルするコードプリプロセッサをプラグインするかもしれません。
Jestは変換の結果をキャッシュし、変換されるファイルのソースや設定の変更など、多くの要因に基づいてその結果を無効にしようとします。
デフォルト
Jest は、1つの transformer – babel-jest
を初めから同梱しています。 これはプロジェクトの Babel 設定を読み込み、/\.[jt]sx?$/
RegExp にマッチするすべてのファイル (言い換えると、すべての .js
、.jsx
、.ts
、 .tsx
ファイル) を変換します。 さらに、babel-jest
は ES Module moking で説明した mock hoisting に必要な Babel プラグインを注入します。
追加のコードプリプロセッサと併用したい場合、次のように、デフォルトの babel-jest
transformer を明示的に含めることを忘れないでください。
"transform": {
"\\.[jt]sx?$": "babel-jest",
"\\.css$": "some-css-transformer",
}
カスタムの transformer を書く
You can write your own transformer. The API of a transformer is as follows:
interface TransformOptions<TransformerConfig = unknown> {
supportsDynamicImport: boolean;
supportsExportNamespaceFrom: boolean;
/**
* 値は次の通りです:
* - `false` - Jest を Node の ESM フラグ `--experimental-vm-modules` なしで実行する場合
* - `true` - ファイル拡張子が [extensionsToTreatAsEsm](Configuration.md#extensionstotreatasesm-arraystring) で定義されている場合
* そして、Jest は Node ESM フラグ `--experimental-vm-modules` を使用して実行されます
*
* 詳細については https://jestjs.io/docs/next/ecmascript-modules を参照してください
*/
supportsStaticESM: boolean;
supportsTopLevelAwait: boolean;
instrument: boolean;
/** 性能改善のために `jest-runtime` が利用するキャッシュされたファイルシステム */
cacheFS: Map<string, string>;
/** 現在実行中のプロジェクトの Jest の設定 */
config: ProjectConfig;
/** 文字列化されたバージョンの `config` - キャッシュの破棄に便利です。 */
configString: string;
/** ユーザーによって `transform` オプションに渡される Transformer の設定 */
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>;
};
The definitions above were trimmed down for brevity. Full code can be found in Jest repo on GitHub (remember to choose the right tag/commit for your version of Jest).
Jest にコードをインポートする方法はいくつかあります。Common JS (require
) または ECMAScript モジュール (import
- 静的および動的バージョンに存在する) を使用してください。 Jest passes files through code transformation on demand (for instance when a require
or import
is evaluated). This process, also known as "transpilation", might happen synchronously (in the case of require
), or asynchronously (in the case of import
or import()
, the latter of which also works from Common JS modules). For this reason, the interface exposes both pairs of methods for asynchronous and synchronous processes: process{Async}
and getCacheKey{Async}
. The latter is called to figure out if we need to call process{Async}
at all.
Asynchronous transpilation can fall back to the synchronous process
call if processAsync
is unimplemented, but synchronous transpilation cannot use the asynchronous processAsync
call. If your codebase is ESM only, implementing the async variants are sufficient. Otherwise, if any code is loaded through require
(including createRequire
from within ESM), then you need to implement the synchronous process
variant.
Be aware that node_modules
is not transpiled with default config, the transformIgnorePatterns
setting must be modified in order to do so.
Semi-related to this are the supports flags we pass (see CallerTransformOptions
above), but those should be used within the transform to figure out if it should return ESM or CJS, and has no direct bearing on sync vs async
Though not required, we highly recommend implementing getCacheKey
as well, so we do not waste resources transpiling when we could have read its previous result from disk. You can use @jest/create-cache-key-function
to help implement it.
Instead of having your custom transformer implement the Transformer
interface directly, you can choose to export createTransformer
, a factory function to dynamically create transformers. This is to allow having a transformer config in your jest config.
ECMAScript モジュール サポートは、supports*
オプションに渡された値で示されます。 Specifically supportsDynamicImport: true
means the transformer can return import()
expressions, which is supported by both ESM and CJS. If supportsStaticESM: true
it means top level import
statements are supported and the code will be interpreted as ESM and not CJS. See Node's docs for details on the differences.
Make sure process{Async}
method returns source map alongside with transformed code, so it is possible to report line information accurately in code coverage and test errors. Inline source maps also work but are slower.
During the development of a transformer it can be useful to run Jest with --no-cache
to frequently delete cache.
コーディング例
型チェック付きTypeScript
babel-jest
はデフォルトでTypeScriptファイルをトランスパイルしますが、Babelはタイプを検証しません。 そうしたい場合は、 ts-jest
を使うことができます。
画像をパスに変換する
画像のインポートは、ブラウザのバンドルに含める1つの方法ですが、有効な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',
},
};