コード変換
Jestはプロジェクト内のコードをJavaScriptとして実行しますが、Node.jsで標準的にサポートされていない構文(JSX、TypeScript、Vueテンプレートなど)を使用する場合は、ブラウザ向けにビルドする場合と同様に、そのコードをプレーンなJavaScriptに変換する必要があります。
Jestはtransform
設定オプションを介してこれをサポートしています。
トランスフォーマーは、ソースファイルを変換するためのメソッドを提供するモジュールです。たとえば、まだNode.jsでサポートされていない新しい言語機能をモジュールやテストで使用したい場合、将来のバージョンのJavaScriptを現在のバージョンにトランスパイルするコードプリプロセッサをプラグインすることができます。
Jestは変換の結果をキャッシュし、変換されるファイルのソースや設定の変更など、いくつかの要因に基づいてその結果の無効化を試みます。
デフォルト値
Jestは、babel-jest
という1つのトランスフォーマーを標準で提供しています。これはプロジェクトのバベル設定を読み込み、/\.[jt]sx?$/
正規表現に一致する任意のファイル(つまり、任意の.js
、.jsx
、.ts
、または.tsx
ファイル)を変換します。さらに、babel-jest
はESモジュールのモックで説明されているモックホイスティングに必要なBabelプラグインを注入します。
追加のコードプリプロセッサと共に使用する場合、デフォルトのbabel-jest
トランスフォーマーを明示的に含めることを忘れないでください。
"transform": {
"\\.[jt]sx?$": "babel-jest",
"\\.css$": "some-css-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.dokyumento.jp/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には、CommonJS(require
)またはECMAScriptモジュール(import
- 静的および動的バージョンが存在します)を使用してコードをインポートする2つの方法があります。Jestは、(require
またはimport
が評価されたときなど)必要に応じてコード変換を通してファイルを渡します。このプロセス(「トランスパイル」とも呼ばれます)は、require
の場合には同期的に、import
またはimport()
(後者はCommonJSモジュールからも機能します)の場合には非同期的に発生する可能性があります。このため、インターフェースは非同期プロセスと同期プロセスの両方のメソッドのペアを公開しています:process{Async}
とgetCacheKey{Async}
です。後者は、process{Async}
を呼び出す必要があるかどうかを判断するために呼び出されます。
非同期トランスパイルは、processAsync
が実装されていない場合、同期のprocess
呼び出しにフォールバックできますが、同期トランスパイルは非同期のprocessAsync
呼び出しを使用できません。コードベースがESMのみの場合は、非同期バリアントを実装するだけで十分です。そうでない場合、require
(ESM内からのcreateRequire
を含む)を使用してコードがロードされる場合は、同期のprocess
バリアントを実装する必要があります。
デフォルトの設定ではnode_modules
はトランスパイルされないことに注意してください。そうするには、transformIgnorePatterns
設定を変更する必要があります。
これと関連しているのが、渡すsupportsフラグ(上記のCallerTransformOptions
を参照)ですが、それらはトランスフォーム内でESMまたはCJSを返す必要があるかどうかを判断するために使用され、同期と非同期には直接関係ありません。
必須ではありませんが、リソースを無駄にトランスパイルせずに、ディスクから以前の結果を読み取ることができるように、getCacheKey
も実装することを強くお勧めします。@jest/create-cache-key-function
を使用して実装できます。
カスタムトランスフォーマーがTransformer
インターフェースを直接実装する代わりに、トランスフォーマーを動的に作成するためのファクトリ関数であるcreateTransformer
をエクスポートすることもできます。これは、Jestの設定でトランスフォーマーの設定を可能にするためです。
ECMAScriptモジュールのサポートは、渡されたsupports*
オプションによって示されます。具体的には、supportsDynamicImport: true
は、トランスフォーマーがimport()
式を返すことができることを意味し、ESMとCJSの両方でサポートされています。supportsStaticESM: true
は、最上位レベルのimport
ステートメントがサポートされており、コードはESMとして解釈され、CJSとしては解釈されないことを意味します。相違点の詳細については、Node.jsのドキュメントを参照してください。
process{Async}
メソッドは、変換されたコードとともにソースマップを返すようにしてください。これにより、コードカバレッジとテストエラーで正確に行情報を報告できます。インラインソースマップも機能しますが、速度が遅くなります。
トランスフォーマーの開発中は、--no-cache
を使用してJestを実行し、頻繁にキャッシュを削除すると便利です。
例
型チェック付きTypeScript
babel-jest
はデフォルトでTypeScriptファイルをトランスパイルしますが、Babelは型を検証しません。これをしたい場合はts-jest
を使用できます。
画像をパスに変換
画像をインポートすると、ブラウザバンドルに含めることができますが、有効なJavaScriptではありません。Jestでこれを処理する1つの方法は、インポートされた値をファイル名に置き換えることです。
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',
},
};