Skip to content

Commit 2e092b0

Browse files
authored
feat(@jest/transform)!: require process() and processAsync() methods to always return structured data (#12638)
1 parent c4d4389 commit 2e092b0

File tree

32 files changed

+301
-153
lines changed

32 files changed

+301
-153
lines changed
Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
diff --git a/jest/preprocessor.js b/jest/preprocessor.js
2-
index 5920c0a6f23c056f27366fabf32dd13c6f86465b..2658e52f9127ac58849e7f830f6342d8b683672c 100644
2+
index f5e34763d840193e243a974e448b619f8f635095..cc6e05ab4c9c25a3d257379a1bf19c21fb9ef5ca 100644
33
--- a/jest/preprocessor.js
44
+++ b/jest/preprocessor.js
5-
@@ -64,8 +64,6 @@ module.exports = {
5+
@@ -38,7 +38,7 @@ module.exports = {
6+
sourceType: 'script',
7+
...nodeOptions,
8+
ast: false,
9+
- }).code;
10+
+ });
11+
}
12+
13+
const {ast} = transformer.transform({
14+
@@ -66,8 +66,6 @@ module.exports = {
615
[require('@babel/plugin-transform-flow-strip-types')],
716
[
817
require('@babel/plugin-proposal-class-properties'),
@@ -11,3 +20,12 @@ index 5920c0a6f23c056f27366fabf32dd13c6f86465b..2658e52f9127ac58849e7f830f6342d8
1120
],
1221
[require('@babel/plugin-transform-computed-properties')],
1322
[require('@babel/plugin-transform-destructuring')],
23+
@@ -112,7 +110,7 @@ module.exports = {
24+
sourceMaps: true,
25+
},
26+
src,
27+
- ).code;
28+
+ );
29+
},
30+
31+
getCacheKey: (createCacheKeyFunction([

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
- `[jest-runtime]` [**BREAKING**] `Runtime.createHasteMap` now returns a promise ([#12008](https://github.com/facebook/jest/pull/12008))
4444
- `[jest-runtime]` Calling `jest.resetModules` function will clear FS and transform cache ([#12531](https://github.com/facebook/jest/pull/12531))
4545
- `[@jest/schemas]` New module for JSON schemas for Jest's config ([#12384](https://github.com/facebook/jest/pull/12384))
46+
- `[jest-transform]` [**BREAKING**] Make it required for `process()` and `processAsync()` methods to always return structured data ([#12638](https://github.com/facebook/jest/pull/12638))
4647
- `[jest-test-result]` Add duration property to JSON test output ([#12518](https://github.com/facebook/jest/pull/12518))
4748
- `[jest-watcher]` [**BREAKING**] Make `PatternPrompt` class to take `entityName` as third constructor parameter instead of `this._entityName` ([#12591](https://github.com/facebook/jest/pull/12591))
4849
- `[jest-worker]` [**BREAKING**] Allow only absolute `workerPath` ([#12343](https://github.com/facebook/jest/pull/12343))

docs/CodeTransformation.md

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,36 @@ id: code-transformation
33
title: Code Transformation
44
---
55

6-
Jest runs the code in your project as JavaScript, but if you use some syntax not supported by Node.js out of the box (such as JSX, types from TypeScript, Vue templates etc.) then you'll need to transform that code into plain JavaScript, similar to what you would do when building for browsers.
6+
Jest runs the code in your project as JavaScript, but if you use some syntax not supported by Node out of the box (such as JSX, TypeScript, Vue templates) then you'll need to transform that code into plain JavaScript, similar to what you would do when building for browsers.
77

8-
Jest supports this via the [`transform` configuration option](Configuration.md#transform-objectstring-pathtotransformer--pathtotransformer-object).
8+
Jest supports this via the [`transform`](Configuration.md#transform-objectstring-pathtotransformer--pathtotransformer-object) configuration option.
99

10-
A transformer is a module that provides a synchronous function for transforming source files. For example, if you wanted to be able to use a new language feature in your modules or tests that aren't yet supported by Node, you might plug in one of many compilers that compile a future version of JavaScript to a current one.
10+
A transformer is a module that provides a method for transforming source files. For example, if you wanted to be able to use a new language feature in your modules or tests that aren't yet supported by Node, you might plug in a code preprocessor that would transpile a future version of JavaScript to a current one.
1111

1212
Jest will cache the result of a transformation and attempt to invalidate that result based on a number of factors, such as the source of the file being transformed and changing configuration.
1313

1414
## Defaults
1515

16-
Jest ships with one transformer out of the box - `babel-jest`. It will automatically load your project's Babel configuration and transform any file matching the following RegEx: `/\.[jt]sx?$/` meaning any `.js`, `.jsx`, `.ts` and `.tsx` file. In addition, `babel-jest` will inject the Babel plugin necessary for mock hoisting talked about in [ES Module mocking](ManualMocks.md#using-with-es-module-imports).
16+
Jest ships with one transformer out of the box – [`babel-jest`](https://github.com/facebook/jest/tree/main/packages/babel-jest#setup). It will load your project's Babel configuration and transform any file matching the `/\.[jt]sx?$/` RegExp (in other words, any `.js`, `.jsx`, `.ts` or `.tsx` file). In addition, `babel-jest` will inject the Babel plugin necessary for mock hoisting talked about in [ES Module mocking](ManualMocks.md#using-with-es-module-imports).
1717

18-
If you override the `transform` configuration option `babel-jest` will no longer be active, and you'll need to add it manually if you wish to use Babel.
18+
:::tip
19+
20+
Remember to include the default `babel-jest` transformer explicitly, if you wish to use it alongside with additional code preprocessors:
21+
22+
```json
23+
"transform": {
24+
"\\.[jt]sx?$": "babel-jest",
25+
"\\.css$": "some-css-transformer",
26+
}
27+
```
28+
29+
:::
1930

2031
## Writing custom transformers
2132

2233
You can write your own transformer. The API of a transformer is as follows:
2334

2435
```ts
25-
// This version of the interface you are seeing on the website has been trimmed down for brevity
26-
// For the full definition, see `packages/jest-transform/src/types.ts` in https://github.com/facebook/jest
27-
// (taking care in choosing the right tag/commit for your version of Jest)
28-
2936
interface TransformOptions<OptionType = unknown> {
3037
supportsDynamicImport: boolean;
3138
supportsExportNamespaceFrom: boolean;
@@ -41,6 +48,11 @@ interface TransformOptions<OptionType = unknown> {
4148
transformerConfig: OptionType;
4249
}
4350

51+
type TransformedSource = {
52+
code: string;
53+
map?: RawSourceMap | string | null;
54+
};
55+
4456
interface SyncTransformer<OptionType = unknown> {
4557
canInstrument?: boolean;
4658

@@ -111,6 +123,12 @@ type TransformerFactory<X extends Transformer> = {
111123
};
112124
```
113125

126+
:::note
127+
128+
The definitions above were trimmed down for brevity. Full code can be found in [Jest repo on GitHub](https://github.com/facebook/jest/blob/main/packages/jest-transform/src/types.ts) (remember to choose the right tag/commit for your version of Jest).
129+
130+
:::
131+
114132
There are a couple of ways you can import code into Jest - using Common JS (`require`) or ECMAScript Modules (`import` - which exists in static and dynamic versions). Jest passes files through code transformation on demand (for instance when a `require` or `import` is evaluated). This process, also known as "transpilation", might happen _synchronously_ (in the case of `require`), or _asynchronously_ (in the case of `import` or `import()`, the latter of which also works from Common JS modules). For this reason, the interface exposes both pairs of methods for asynchronous and synchronous processes: `process{Async}` and `getCacheKey{Async}`. The latter is called to figure out if we need to call `process{Async}` at all. Since async transformation can happen synchronously without issue, it's possible for the async case to "fall back" to the sync variant, but not vice versa.
115133

116134
So if your code base is ESM only implementing the async variants is sufficient. Otherwise, if any code is loaded through `require` (including `createRequire` from within ESM), then you need to implement the synchronous variant. Be aware that `node_modules` is not transpiled with default config.
@@ -125,7 +143,9 @@ Note that [ECMAScript module](ECMAScriptModules.md) support is indicated by the
125143

126144
:::tip
127145

128-
Make sure `TransformedSource` contains a source map, so it is possible to report line information accurately in code coverage and test errors. Inline source maps also work but are slower.
146+
Make sure `process{Async}` method returns source map alongside with transformed code, so it is possible to report line information accurately in code coverage and test errors. Inline source maps also work but are slower.
147+
148+
During the development of a transformer it can be useful to run Jest with `--no-cache` to frequently [delete cache](Troubleshooting.md#caching-issues).
129149

130150
:::
131151

@@ -143,8 +163,10 @@ Importing images is a way to include them in your browser bundle, but they are n
143163
const path = require('path');
144164

145165
module.exports = {
146-
process(src, filename, config, options) {
147-
return `module.exports = ${JSON.stringify(path.basename(filename))};`;
166+
process(sourceText, sourcePath, options) {
167+
return {
168+
code: `module.exports = ${JSON.stringify(path.basename(sourcePath))};`,
169+
};
148170
},
149171
};
150172
```

docs/Configuration.md

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,27 +1564,26 @@ Default timeout of a test in milliseconds.
15641564

15651565
Default: `{"\\.[jt]sx?$": "babel-jest"}`
15661566

1567-
A map from regular expressions to paths to transformers. A transformer is a module that provides a synchronous function for transforming source files. For example, if you wanted to be able to use a new language feature in your modules or tests that aren't yet supported by node, you might plug in one of many compilers that compile a future version of JavaScript to a current one. Example: see the [examples/typescript](https://github.com/facebook/jest/blob/main/examples/typescript/package.json#L16) example or the [webpack tutorial](Webpack.md).
1567+
A map from regular expressions to paths to transformers. Optionally, a tuple with configuration options can be passed as second argument: `{filePattern: ['path-to-transformer', {options}]}`. For example, here is how you can configure `babel-jest` for non-default behavior: `{'\\.js$': ['babel-jest', {rootMode: 'upward'}]}`.
15681568

1569-
Examples of such compilers include:
1569+
Jest runs the code of your project as JavaScript, hence a transformer is needed if you use some syntax not supported by Node out of the box (such as JSX, TypeScript, Vue templates). By default, Jest will use [`babel-jest`](https://github.com/facebook/jest/tree/main/packages/babel-jest#setup) transformer, which will load your project's Babel configuration and transform any file matching the `/\.[jt]sx?$/` RegExp (in other words, any `.js`, `.jsx`, `.ts` or `.tsx` file). In addition, `babel-jest` will inject the Babel plugin necessary for mock hoisting talked about in [ES Module mocking](ManualMocks.md#using-with-es-module-imports).
15701570

1571-
- [Babel](https://babeljs.io/)
1572-
- [TypeScript](http://www.typescriptlang.org/)
1573-
- To build your own please visit the [Custom Transformer](CodeTransformation.md#writing-custom-transformers) section
1574-
1575-
You can pass configuration to a transformer like `{filePattern: ['path-to-transformer', {options}]}` For example, to configure babel-jest for non-default behavior, `{"\\.js$": ['babel-jest', {rootMode: "upward"}]}`
1571+
See the [Code Transformation](CodeTransformation.md) section for more details and instructions on building your own transformer.
15761572

15771573
:::tip
15781574

1579-
A transformer is only run once per file unless the file has changed. During the development of a transformer it can be useful to run Jest with `--no-cache` to frequently [delete Jest's cache](Troubleshooting.md#caching-issues).
1580-
1581-
When adding additional code transformers, this will overwrite the default config and `babel-jest` is no longer automatically loaded. If you want to use it to compile JavaScript or TypeScript, it has to be explicitly defined by adding `{"\\.[jt]sx?$": "babel-jest"}` to the transform property. See [babel-jest plugin](https://github.com/facebook/jest/tree/main/packages/babel-jest#setup).
1575+
Keep in mind that a transformer only runs once per file unless the file has changed.
15821576

1583-
:::
1577+
Remember to include the default `babel-jest` transformer explicitly, if you wish to use it alongside with additional code preprocessors:
15841578

1585-
A transformer must be an object with at least a `process` function, and it's also recommended to include a `getCacheKey` function. If your transformer is written in ESM you should have a default export with that object.
1579+
```json
1580+
"transform": {
1581+
"\\.[jt]sx?$": "babel-jest",
1582+
"\\.css$": "some-css-transformer",
1583+
}
1584+
```
15861585

1587-
If the tests are written using [native ESM](ECMAScriptModules.md) the transformer can export `processAsync` and `getCacheKeyAsync` instead or in addition to the synchronous variants.
1586+
:::
15881587

15891588
### `transformIgnorePatterns` \[array&lt;string&gt;]
15901589

docs/Webpack.md

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -83,16 +83,16 @@ Then all your className lookups on the styles object will be returned as-is (e.g
8383
}
8484
```
8585

86-
> Notice that Proxy is enabled in Node 6 by default. If you are not on Node 6 yet, make sure you invoke Jest using `node --harmony_proxies node_modules/.bin/jest`.
87-
8886
If `moduleNameMapper` cannot fulfill your requirements, you can use Jest's [`transform`](Configuration.md#transform-objectstring-pathtotransformer--pathtotransformer-object) config option to specify how assets are transformed. For example, a transformer that returns the basename of a file (such that `require('logo.jpg');` returns `'logo'`) can be written as:
8987

9088
```js title="fileTransformer.js"
9189
const path = require('path');
9290

9391
module.exports = {
94-
process(src, filename, config, options) {
95-
return `module.exports = ${JSON.stringify(path.basename(filename))};`;
92+
process(sourceText, sourcePath, options) {
93+
return {
94+
code: `module.exports = ${JSON.stringify(path.basename(sourcePath))};`,
95+
};
9696
},
9797
};
9898
```
@@ -112,16 +112,19 @@ module.exports = {
112112

113113
We've told Jest to ignore files matching a stylesheet or image extension, and instead, require our mock files. You can adjust the regular expression to match the file types your webpack config handles.
114114

115-
_Note: if you are using babel-jest with additional code preprocessors, you have to explicitly define babel-jest as a transformer for your JavaScript code to map `.js` files to the babel-jest module._
115+
:::tip
116+
117+
Remember to include the default `babel-jest` transformer explicitly, if you wish to use it alongside with additional code preprocessors:
116118

117119
```json
118120
"transform": {
119-
"\\.js$": "babel-jest",
120-
"\\.css$": "custom-transformer",
121-
...
121+
"\\.[jt]sx?$": "babel-jest",
122+
"\\.css$": "some-css-transformer",
122123
}
123124
```
124125

126+
:::
127+
125128
### Configuring Jest to find our files
126129

127130
Now that Jest knows how to process our files, we need to tell it how to _find_ them. For webpack's `modulesDirectories`, and `extensions` options there are direct analogs in Jest's `moduleDirectories` and `moduleFileExtensions` options.
@@ -186,8 +189,7 @@ That's it! webpack is a complex and flexible tool, so you may have to make some
186189

187190
webpack 2 offers native support for ES modules. However, Jest runs in Node, and thus requires ES modules to be transpiled to CommonJS modules. As such, if you are using webpack 2, you most likely will want to configure Babel to transpile ES modules to CommonJS modules only in the `test` environment.
188191

189-
```json
190-
// .babelrc
192+
```json title=".babelrc"
191193
{
192194
"presets": [["env", {"modules": false}]],
193195

@@ -203,8 +205,7 @@ webpack 2 offers native support for ES modules. However, Jest runs in Node, and
203205
204206
If you use dynamic imports (`import('some-file.js').then(module => ...)`), you need to enable the `dynamic-import-node` plugin.
205207

206-
```json
207-
// .babelrc
208+
```json title=".babelrc"
208209
{
209210
"presets": [["env", {"modules": false}]],
210211

e2e/__tests__/dependencyClash.test.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,12 @@ test('does not require project modules from inside node_modules', () => {
6464
if (!threw) {
6565
throw new Error('It used the wrong invariant module!');
6666
}
67-
return script.replace(
68-
'INVALID CODE FRAGMENT THAT WILL BE REMOVED BY THE TRANSFORMER',
69-
''
70-
);
67+
return {
68+
code: script.replace(
69+
'INVALID CODE FRAGMENT THAT WILL BE REMOVED BY THE TRANSFORMER',
70+
'',
71+
),
72+
};
7173
},
7274
};
7375
`,

e2e/__tests__/multiProjectRunner.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ test('Does transform files with the corresponding project transformer', () => {
450450
};`,
451451
'project1/transformer.js': `
452452
module.exports = {
453-
process: () => 'module.exports = "PROJECT1";',
453+
process: () => ({code: 'module.exports = "PROJECT1";'}),
454454
getCacheKey: () => 'PROJECT1_CACHE_KEY',
455455
}
456456
`,
@@ -465,7 +465,7 @@ test('Does transform files with the corresponding project transformer', () => {
465465
};`,
466466
'project2/transformer.js': `
467467
module.exports = {
468-
process: () => 'module.exports = "PROJECT2";',
468+
process: () => ({code: 'module.exports = "PROJECT2";'}),
469469
getCacheKey: () => 'PROJECT2_CACHE_KEY',
470470
}
471471
`,

e2e/coverage-provider-v8/esm-with-custom-transformer/typescriptPreprocessor.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ export default {
2121

2222
return {code: outputText, map: sourceMapText};
2323
}
24-
return sourceText;
24+
return {code: sourceText};
2525
},
2626
};

e2e/coverage-provider-v8/no-sourcemap/cssTransform.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@
77

88
module.exports = {
99
getCacheKey: () => 'cssTransform',
10-
process: () => 'module.exports = {};',
10+
process: () => ({code: 'module.exports = {};'}),
1111
};

e2e/coverage-remapping/typescriptPreprocessor.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ module.exports = {
2222
map: JSON.parse(result.sourceMapText),
2323
};
2424
}
25-
return src;
25+
return {code: src};
2626
},
2727
};

0 commit comments

Comments
 (0)