Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support async createTransformer #13762

Merged
merged 1 commit into from Jan 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
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
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
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
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
27 changes: 27 additions & 0 deletions packages/jest-transform/src/__tests__/ScriptTransformer.test.ts
Expand Up @@ -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',
() => {
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion packages/jest-transform/src/types.ts
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
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