Aller au contenu principal
Version : 29.3

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 est livré avec un transformateur prêt à l'emploi : 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. Le code complet peut être trouvé dans le dépôt de Jest sur GitHub (n'oubliez pas de choisir le bon tag/commit pour votre version de 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}. Puisque la transformation asynchrone peut se faire de manière synchrone sans problème, il est possible pour le cas asynchrone de « retomber » sur la variante synchrone, mais pas le contraire.

Ainsi, si votre base de code est ESM, il suffit d'implémenter les variantes asynchrones. Sinon, si un code est chargé via require (y compris createRequire à partir de ESM), alors vous devez implémenter la variante synchrone. Sachez que node_modules n'est pas transpilée avec la configuration par défaut.

Semi-relié à cela sont les flags de support que nous passons (voir CallerTransformOptions ci-dessus), mais ceux-ci doivent être utilisés à l'intérieur de la transformation pour déterminer si elle doit retourner ESM ou CJS, et n'a pas de rapport direct avec sync vs async

Bien que cela ne soit pas obligatoire, nous recommandons fortement d'implémenter getCacheKey également, afin de ne pas gaspiller des ressources à la transpilation alors que nous aurions pu lire son résultat précédent sur le disque. Vous pouvez utiliser @jest/create-cache-key-function pour vous aider à l'implémenter.

Au lieu que votre transformateur personnalisé implémente directement l'interface Transformer, vous pouvez choisir d'exporter createTransformer, une fonction de fabrique pour créer dynamiquement des transformateurs. Ceci permet d'avoir une configuration de transformateur dans votre configuration jest.

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',
},
};