From 86f3c8937503d5bded0f936e671f51ec77022f2e Mon Sep 17 00:00:00 2001 From: Laurent Christophe Date: Tue, 17 Jan 2023 07:38:23 +0100 Subject: [PATCH] feat: support async createTransformer (#13762) --- CHANGELOG.md | 1 + .../src/__tests__/getCacheKey.test.ts | 21 ++++++++------- packages/babel-jest/src/__tests__/index.ts | 9 ++++--- .../jest-transform/src/ScriptTransformer.ts | 4 ++- .../src/__tests__/ScriptTransformer.test.ts | 27 +++++++++++++++++++ packages/jest-transform/src/types.ts | 2 +- .../version-29.3/CodeTransformation.md | 4 +-- 7 files changed, 50 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 650d0ac50d2a..2bf6b21d61d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - `[jest-runtime]` Add `jest.isolateModulesAsync` for scoped module initialization of asynchronous functions ([#13680](https://github.com/facebook/jest/pull/13680)) - `[jest-runtime]` Add `jest.isEnvironmentTornDown` function ([#13698](https://github.com/facebook/jest/pull/13698)) - `[jest-test-result]` Added `skipped` and `focused` status to `FormattedTestResult` ([#13700](https://github.com/facebook/jest/pull/13700)) +- `[jest-transform]` Support for asynchronous `createTransformer` ([#13762](https://github.com/facebook/jest/pull/13762)) ### Fixes diff --git a/packages/babel-jest/src/__tests__/getCacheKey.test.ts b/packages/babel-jest/src/__tests__/getCacheKey.test.ts index e61e4a3053c5..5e7e9e871999 100644 --- a/packages/babel-jest/src/__tests__/getCacheKey.test.ts +++ b/packages/babel-jest/src/__tests__/getCacheKey.test.ts @@ -6,10 +6,11 @@ */ import type {TransformOptions as BabelTransformOptions} from '@babel/core'; -import type {TransformOptions} from '@jest/transform'; +import type {SyncTransformer, TransformOptions} from '@jest/transform'; import babelJest from '../index'; -const {getCacheKey} = babelJest.createTransformer(); +const {getCacheKey} = + babelJest.createTransformer() as SyncTransformer; const processVersion = process.version; const nodeEnv = process.env.NODE_ENV; @@ -47,7 +48,7 @@ describe('getCacheKey', () => { expect(oldCacheKey).toHaveLength(32); }); - test('if `THIS_FILE` value is changing', () => { + test('if `THIS_FILE` value is changing', async () => { jest.doMock('graceful-fs', () => ({ readFileSync: () => 'new this file', })); @@ -55,7 +56,7 @@ describe('getCacheKey', () => { const {createTransformer} = require('../index') as typeof import('../index'); - const newCacheKey = createTransformer().getCacheKey!( + const newCacheKey = (await createTransformer()).getCacheKey!( sourceText, sourcePath, transformOptions, @@ -64,7 +65,7 @@ describe('getCacheKey', () => { expect(oldCacheKey).not.toEqual(newCacheKey); }); - test('if `babelOptions.options` value is changing', () => { + test('if `babelOptions.options` value is changing', async () => { jest.doMock('../loadBabelConfig', () => { const babel = require('@babel/core') as typeof import('@babel/core'); @@ -79,7 +80,7 @@ describe('getCacheKey', () => { const {createTransformer} = require('../index') as typeof import('../index'); - const newCacheKey = createTransformer().getCacheKey!( + const newCacheKey = (await createTransformer()).getCacheKey!( sourceText, sourcePath, transformOptions, @@ -117,7 +118,7 @@ describe('getCacheKey', () => { expect(oldCacheKey).not.toEqual(newCacheKey); }); - test('if `babelOptions.config` value is changing', () => { + test('if `babelOptions.config` value is changing', async () => { jest.doMock('../loadBabelConfig', () => { const babel = require('@babel/core') as typeof import('@babel/core'); @@ -132,7 +133,7 @@ describe('getCacheKey', () => { const {createTransformer} = require('../index') as typeof import('../index'); - const newCacheKey = createTransformer().getCacheKey!( + const newCacheKey = (await createTransformer()).getCacheKey!( sourceText, sourcePath, transformOptions, @@ -141,7 +142,7 @@ describe('getCacheKey', () => { expect(oldCacheKey).not.toEqual(newCacheKey); }); - test('if `babelOptions.babelrc` value is changing', () => { + test('if `babelOptions.babelrc` value is changing', async () => { jest.doMock('../loadBabelConfig', () => { const babel = require('@babel/core') as typeof import('@babel/core'); @@ -156,7 +157,7 @@ describe('getCacheKey', () => { const {createTransformer} = require('../index') as typeof import('../index'); - const newCacheKey = createTransformer().getCacheKey!( + const newCacheKey = (await createTransformer()).getCacheKey!( sourceText, sourcePath, transformOptions, diff --git a/packages/babel-jest/src/__tests__/index.ts b/packages/babel-jest/src/__tests__/index.ts index 9903c2b773b0..e0bda219a20e 100644 --- a/packages/babel-jest/src/__tests__/index.ts +++ b/packages/babel-jest/src/__tests__/index.ts @@ -10,7 +10,7 @@ import type { TransformOptions as BabelTransformOptions, } from '@babel/core'; import {makeProjectConfig} from '@jest/test-utils'; -import type {TransformOptions} from '@jest/transform'; +import type {SyncTransformer, TransformOptions} from '@jest/transform'; import babelJest, {createTransformer} from '../index'; import {loadPartialConfig} from '../loadBabelConfig'; @@ -28,7 +28,8 @@ jest.mock('../loadBabelConfig', () => { }; }); -const defaultBabelJestTransformer = babelJest.createTransformer(); +const defaultBabelJestTransformer = + babelJest.createTransformer() as SyncTransformer; //Mock data for all the tests const sourceString = ` @@ -163,8 +164,8 @@ describe('caller option correctly merges from defaults and options', () => { }); }); -test('can pass null to createTransformer', () => { - const transformer = createTransformer(); +test('can pass null to createTransformer', async () => { + const transformer = await createTransformer(); transformer.process(sourceString, 'dummy_path.js', { cacheFS: new Map(), config: makeProjectConfig(), diff --git a/packages/jest-transform/src/ScriptTransformer.ts b/packages/jest-transform/src/ScriptTransformer.ts index 19230b04e355..7e800a6b7210 100644 --- a/packages/jest-transform/src/ScriptTransformer.ts +++ b/packages/jest-transform/src/ScriptTransformer.ts @@ -283,7 +283,9 @@ class ScriptTransformer { throw new Error(makeInvalidTransformerError(transformPath)); } if (isTransformerFactory(transformer)) { - transformer = transformer.createTransformer(transformerConfig); + transformer = await transformer.createTransformer( + transformerConfig, + ); } if ( typeof transformer.process !== 'function' && diff --git a/packages/jest-transform/src/__tests__/ScriptTransformer.test.ts b/packages/jest-transform/src/__tests__/ScriptTransformer.test.ts index 3a6807de86f7..9db2f688f702 100644 --- a/packages/jest-transform/src/__tests__/ScriptTransformer.test.ts +++ b/packages/jest-transform/src/__tests__/ScriptTransformer.test.ts @@ -234,6 +234,18 @@ jest.mock( {virtual: true}, ); +jest.mock( + 'async-factory', + () => ({ + createTransformer() { + return Promise.resolve({ + process: jest.fn().mockReturnValue({code: 'code'}), + }); + }, + }), + {virtual: true}, +); + jest.mock( 'factory-for-async-preprocessor', () => { @@ -551,6 +563,21 @@ describe('ScriptTransformer', () => { ).toBeDefined(); }); + it('handle async createTransformer', async () => { + config = { + ...config, + transform: [['\\.js$', 'async-factory', {}]], + }; + const scriptTransformer = await createScriptTransformer(config); + expect( + await scriptTransformer.transformSourceAsync( + 'sample.js', + '', + getTransformOptions(false), + ), + ).toBeDefined(); + }); + it('throws an error if createTransformer returns object without `process` method', async () => { config = { ...config, diff --git a/packages/jest-transform/src/types.ts b/packages/jest-transform/src/types.ts index e08b9e43b5f3..c92dc453a4a4 100644 --- a/packages/jest-transform/src/types.ts +++ b/packages/jest-transform/src/types.ts @@ -149,7 +149,7 @@ export type Transformer = export type TransformerCreator< X extends Transformer, TransformerConfig = unknown, -> = (transformerConfig?: TransformerConfig) => X; +> = (transformerConfig?: TransformerConfig) => X | Promise; /** * Instead of having your custom transformer implement the Transformer interface diff --git a/website/versioned_docs/version-29.3/CodeTransformation.md b/website/versioned_docs/version-29.3/CodeTransformation.md index 8b712c7e51c9..8907f2269623 100644 --- a/website/versioned_docs/version-29.3/CodeTransformation.md +++ b/website/versioned_docs/version-29.3/CodeTransformation.md @@ -125,7 +125,7 @@ type Transformer = type TransformerCreator< X extends Transformer, TransformerConfig = unknown, -> = (transformerConfig?: TransformerConfig) => X; +> = (transformerConfig?: TransformerConfig) => X | Promise; type TransformerFactory = { createTransformer: TransformerCreator; @@ -146,7 +146,7 @@ Semi-related to this are the supports flags we pass (see `CallerTransformOptions 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`](https://www.npmjs.com/package/@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. +Instead of having your custom transformer implement the `Transformer` interface directly, you can choose to export `createTransformer`, a possibly asynchronous factory function to dynamically create transformers. This is to allow having a transformer config in your jest config. :::note