Aller au contenu principal
Version : Suivant

Transformation de code

Jest exécute le code de votre projet en tant que JavaScript, mais si vous utilisez une syntaxe qui n'est pas prise en charge par Node (comme JSX, TypeScript, les templates Vue), vous devrez transformer ce code en JavaScript ordinaire, comme vous le feriez lors de la création pour les navigateurs.

Jest prend en charge ceci via l'option de configuration transform.

Un transformateur est un module qui fournit une méthode pour transformer les fichiers source. Par exemple, si vous souhaitez pouvoir utiliser une nouvelle fonctionnalité du langage dans vos modules ou tests qui n'est pas encore prise en charge par Node, vous pourriez intégrer un préprocesseur de code qui transposerait une future version de JavaScript dans une version actuelle.

Jest mettra en cache le résultat d'une transformation et tentera d'invalider ce résultat en fonction d'un certain nombre de facteurs, comme la source du fichier transformé et la modification de la configuration.

Defaults

Jest ships with one transformer out of the box – babel-jest. Il chargera la configuration Babel de votre projet et transformera tout fichier correspondant à la RegExp suivante /\.[jt]sx?$/ (signifiant tout fichier .js, .jsx, .ts ou .tsx). En outre, babel-jest injectera le plugin Babel nécessaire pour le montage de simulation dont on parle dans Simulation module ES.

astuce

N'oubliez pas d'inclure explicitement le transformateur par défaut babel-jest, si vous souhaitez l'utiliser en parallèle avec des préprocesseurs de code supplémentaires :

"transform": {
"\\.[jt]sx?$": "babel-jest",
"\\.css$": "some-css-transformer",
}

Écriture de transformateurs personnalisés

Vous pouvez écrire votre propre transformateur. L'API d'un transformateur est la suivante :

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://jestjs.io/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>;
/** Configuration de Jest du projet en cours d'exécution. */
config: ProjectConfig;
/** Version stringifiée de `config` - utile pour la suppression du cache. */
configString: string;
/** Configuration du transformateur passée par l'utilisateur via l'option `transform`. */
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>;
};
remarque

Les définitions ci-dessus ont été réduites par souci de concision. Full code can be found in Jest repo on GitHub (remember to choose the right tag/commit for your version of Jest).

Il y a deux façons d'importer du code dans Jest - en utilisant Common JS (require) ou des Modules ECMAScript (import - qui existe dans les versions statiques et dynamiques). Jest passe les fichiers à travers la transformation de code à la demande (par exemple quand un require ou un import est évalué). Ce processus, également appelé « transpilation », peut se dérouler de manière synchrone (dans le cas de require), ou asynchrone (dans le cas de import ou import(), ce dernier fonctionnant également à partir de modules Common JS). Pour cette raison, l'interface expose les deux paires de méthodes pour des processus asynchrones et synchrones : process{Async} et getCacheKey{Async}. Ce dernier est appelé pour savoir si nous devons appeler process{Async}.

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. Vous pouvez utiliser @jest/create-cache-key-function pour vous aider à l'implémenter.

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.

remarque

ECMAScript module support is indicated by the passed in supports* options. Spécifiquement supportsDynamicImport : true signifie que le transformateur peut retourner des expressions import() , qui sont prises en charge par ESM et CJS. Si supportsStaticESM : true cela signifie que les instructions de premier niveau import sont prises en charge et que le code sera interprété comme ESM et non comme CJS. Consultez la documentation de Node pour plus de détails sur les différences.

astuce

Assurez-vous que la méthode process{Async} renvoie la source map avec le code transformé, pour qu'il soit possible de reporter les informations de ligne avec précision dans la couverture de code et les erreurs de test. Les source maps en ligne fonctionnent également mais sont plus lentes.

Lors du développement d'un transformateur, il peut être utile d'exécuter Jest avec --no-cache pour supprimer fréquemment le cache.

Exemples

TypeScript avec vérification de type

Alors que babel-jest transpile par défaut les fichiers TypeScript, Babel ne vérifie pas les types. Si vous le souhaitez, vous pouvez utiliser ts-jest.

Transformation des images vers leur chemin

L'importation d'images est un moyen de les inclure dans le paquet de votre navigateur, mais elles ne sont pas du JavaScript valide. Une façon de le gérer dans Jest est de remplacer la valeur importée par son nom de fichier.

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

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