Aller au contenu principal
Version : 30.0

Transformation de code

Traduction Bêta Non Officielle

Cette page a été traduite par PageTurner AI (bêta). Non approuvée officiellement par le projet. Vous avez trouvé une erreur ? Signaler un problème →

Jest exécute le code de votre projet en JavaScript, mais si vous utilisez une syntaxe non prise en charge nativement par Node (comme JSX, TypeScript, ou les templates Vue), vous devrez transformer ce code en JavaScript standard, similairement à ce que vous feriez pour une construction destinée aux navigateurs.

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

Un transformateur est un module qui fournit une méthode pour transformer des fichiers sources. Par exemple, si vous souhaitez utiliser une nouvelle fonctionnalité de 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 transpilerait une version future de JavaScript vers une version actuelle.

Jest met en cache le résultat d'une transformation et tente d'invalider ce résultat en fonction de plusieurs facteurs, comme la source du fichier transformé et les changements de configuration.

Par défaut

Jest inclut un transformateur par défaut – babel-jest. Il chargera la configuration Babel de votre projet et transformera tout fichier correspondant à l'expression régulière /\.[jt]sx?$/ (c'est-à-dire tout fichier .js, .jsx, .ts ou .tsx). De plus, babel-jest injectera le plugin Babel nécessaire pour l'élévation des mocks mentionnée dans le mocking des modules ES.

note

Par défaut, babel-jest inclut babel-preset-jest. Vous pouvez désactiver ce comportement en spécifiant excludeJestPreset: true à babel-jest. Notez que cela empêchera également l'élévation de jest.mock, ce qui peut rompre vos tests.

"transform": {
"\\.[jt]sx?$": ["babel-jest", { "excludeJestPreset": true }],
}
astuce

N'oubliez pas d'inclure explicitement le transformateur babel-jest par défaut si vous souhaitez l'utiliser conjointement avec d'autres préprocesseurs de code :

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

Écrire des transformateurs personnalisés

Vous pouvez créer 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>;
/** 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>;
};
note

Les définitions ci-dessus ont été simplifiées par souci de concision. Le code complet est disponible dans le dépôt Jest sur GitHub (pensez à choisir le tag/commit correspondant à votre version de Jest).

Il existe plusieurs façons d'importer du code dans Jest - via Common JS (require) ou les modules ECMAScript (import, qui existe en versions statique et dynamique). Jest soumet les fichiers à une transformation de code à la demande (par exemple lorsqu'un require ou import est évalué). Ce processus, aussi appelé "transpilation", peut se produire de façon synchrone (dans le cas de require) ou asynchrone (dans le cas de import ou import(), ce dernier fonctionnant aussi depuis les modules Common JS). Pour cette raison, l'interface expose deux paires de méthodes pour les processus asynchrones et synchrones : process{Async} et getCacheKey{Async}. Cette dernière est appelée pour déterminer si nous devons appeler process{Async}.

La transpilation asynchrone peut revenir à l'appel synchrone process si processAsync n'est pas implémenté, mais la transpilation synchrone ne peut pas utiliser l'appel asynchrone processAsync. Si votre base de code est exclusivement en ESM, implémenter les variantes asynchrones suffit. Sinon, si du code est chargé via require (y compris via createRequire depuis un module ESM), vous devez implémenter la variante synchrone process.

Notez que node_modules n'est pas transpilé avec la configuration par défaut : le paramètre transformIgnorePatterns doit être modifié pour cela.

Ces indicateurs de support que nous transmettons (voir CallerTransformOptions ci-dessus) sont semi-liés à ce sujet, mais ils doivent être utilisés dans le transformateur pour déterminer s'il doit renvoyer du ESM ou du CJS, sans rapport direct avec le caractère synchrone ou asynchrone.

Bien que non obligatoire, nous recommandons vivement d'implémenter également getCacheKey pour éviter de gaspiller des ressources à transpiler quand on pourrait simplement relire le résultat précédent depuis le disque. Vous pouvez utiliser @jest/create-cache-key-function pour faciliter cette implémentation.

Au lieu de faire implémenter directement l'interface Transformer par votre transformateur personnalisé, vous pouvez choisir d'exporter createTransformer, une fonction factory pour créer dynamiquement des transformateurs. Cela permet d'inclure une configuration de transformateur dans votre configuration Jest.

note

Le support des modules ECMAScript est indiqué par les options supports* transmises. Plus précisément, supportsDynamicImport: true signifie que le transformateur peut renvoyer des expressions import(), supportées à la fois par ESM et CJS. Si supportsStaticESM: true, cela signifie que les instructions import de haut niveau sont supportées et que le code sera interprété comme ESM et non CJS. Consultez la documentation de Node pour les différences détaillées.

astuce

Assurez-vous que la méthode process{Async} renvoie une source map avec le code transformé, pour permettre un reporting précis des numéros de ligne dans la couverture de code et les erreurs de test. Les source maps inline fonctionnent aussi mais sont plus lentes.

Pendant le 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 types

Si babel-jest transpile par défaut les fichiers TypeScript, Babel ne vérifie pas les types. Pour cette fonctionnalité, utilisez ts-jest.

Transformation d'images en leur chemin

L'importation d'images permet de les inclure dans votre bundle navigateur, mais elles ne sont pas du JavaScript valide. Une façon de gérer cela 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',
},
};