Skip to content

Commit

Permalink
feat: support async createTransformer
Browse files Browse the repository at this point in the history
Simply wrap the only invocation of `createTransformer` inside an await 
expression to support async `createTransformer`.

Had to modify some tests in `babel-jest` to comply with the new return 
type of `createTransformer`.
  • Loading branch information
lachrist committed Jan 12, 2023
1 parent ee63afc commit dc75e92
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- `[jest-resolve]` Support subpath imports ([#13705](https://github.com/facebook/jest/pull/13705), [#13723](https://github.com/facebook/jest/pull/13723))
- `[jest-runtime]` Add `jest.isolateModulesAsync` for scoped module initialization of asynchronous functions ([#13680](https://github.com/facebook/jest/pull/13680))
- `[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

Expand Down
21 changes: 11 additions & 10 deletions packages/babel-jest/src/__tests__/getCacheKey.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<BabelTransformOptions>;

const processVersion = process.version;
const nodeEnv = process.env.NODE_ENV;
Expand Down Expand Up @@ -47,15 +48,15 @@ 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',
}));

const {createTransformer} =
require('../index') as typeof import('../index');

const newCacheKey = createTransformer().getCacheKey!(
const newCacheKey = (await createTransformer()).getCacheKey!(
sourceText,
sourcePath,
transformOptions,
Expand All @@ -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');

Expand All @@ -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,
Expand Down Expand Up @@ -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');

Expand All @@ -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,
Expand All @@ -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');

Expand All @@ -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,
Expand Down
9 changes: 5 additions & 4 deletions packages/babel-jest/src/__tests__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -28,7 +28,8 @@ jest.mock('../loadBabelConfig', () => {
};
});

const defaultBabelJestTransformer = babelJest.createTransformer();
const defaultBabelJestTransformer =
babelJest.createTransformer() as SyncTransformer<BabelTransformOptions>;

//Mock data for all the tests
const sourceString = `
Expand Down Expand Up @@ -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<string, string>(),
config: makeProjectConfig(),
Expand Down
4 changes: 3 additions & 1 deletion packages/jest-transform/src/ScriptTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,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' &&
Expand Down
25 changes: 25 additions & 0 deletions packages/jest-transform/src/__tests__/ScriptTransformer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,16 @@ jest.mock(
{virtual: true},
);

jest.mock(
'async-factory',
() => ({
async createTransformer() {
return {process: jest.fn().mockReturnValue({code: 'code'})};
},
}),
{virtual: true},
);

jest.mock(
'factory-for-async-preprocessor',
() => {
Expand Down Expand Up @@ -551,6 +561,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,
Expand Down
2 changes: 1 addition & 1 deletion packages/jest-transform/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ export type Transformer<TransformerConfig = unknown> =
export type TransformerCreator<
X extends Transformer<TransformerConfig>,
TransformerConfig = unknown,
> = (transformerConfig?: TransformerConfig) => X;
> = (transformerConfig?: TransformerConfig) => X | Promise<X>;

/**
* Instead of having your custom transformer implement the Transformer interface
Expand Down
4 changes: 2 additions & 2 deletions website/versioned_docs/version-29.3/CodeTransformation.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ type Transformer<TransformerConfig = unknown> =
type TransformerCreator<
X extends Transformer<TransformerConfig>,
TransformerConfig = unknown,
> = (transformerConfig?: TransformerConfig) => X;
> = (transformerConfig?: TransformerConfig) => X | Promise<X>;

type TransformerFactory<X extends Transformer> = {
createTransformer: TransformerCreator<X>;
Expand All @@ -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

Expand Down

0 comments on commit dc75e92

Please sign in to comment.