Skip to content

Commit c277858

Browse files
authoredDec 12, 2020
feat(presets): add 3 new presets to work with ESM (#2207)
BREAKING CHANGE - `create-jest-preset` util function signature has changed to ``` function createJestPreset(allowJs?: boolean, extraOptions?: Config.InitialOptions): TsJestPresets; ```
1 parent 753b5e4 commit c277858

17 files changed

+224
-162
lines changed
 

‎README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
<img src="./icon.png" align="right" title="ts-jest Logo" width="128" height="128">
1616

17-
It supports all features of TypeScript including type-checking. [Read more about Babel7 + `preset-typescript` **vs** TypeScript (and `ts-jest`)](https://kulshekhar.github.io/ts-jest/user/babel7-or-ts).
17+
It supports all features of TypeScript including type-checking. [Read more about Babel7 + `preset-typescript` **vs** TypeScript (and `ts-jest`)](https://kulshekhar.github.io/ts-jest/docs/babel7-or-ts).
1818

1919
---
2020

‎docs/docs/presets.md

+9-6
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,18 @@ id: presets
33
title: Presets
44
---
55

6-
### The 3 presets
6+
### The presets
77

8-
`ts-jest` comes with 3 presets, covering most of the project's base configuration:
8+
`ts-jest` comes with several presets, covering most of the project's base configuration:
99

1010
| Preset name | Description |
1111
|---|---|
12-
| `ts-jest/presets/default`<br/>or `ts-jest` | `ts-jest` will take care of `.ts` and `.tsx` files only, leaving JavaScript files as-is. |
13-
| `ts-jest/presets/js-with-ts` | TypeScript and JavaScript files (`.ts`, `.tsx`, `.js` and `.jsx`) will be handled by `ts-jest`.<br/>You'll need to set `allowJs` to `true` in your `tsconfig.json` file. |
14-
| `ts-jest/presets/js-with-babel` | TypeScript files will be handled by `ts-jest`, and JavaScript files will be handled by `babel-jest`. |
12+
| `ts-jest/presets/default`<br/>or `ts-jest` | TypeScript files (`.ts`, `.tsx`) will be transformed by `ts-jest` to **CommonJS** syntax, leaving JavaScript files (`.js`, `jsx`) as-is. |
13+
| `ts-jest/presets/default-esm`<br/> | TypeScript files (`.ts`, `.tsx`) will be transformed by `ts-jest` to **ESM** syntax, leaving JavaScript files (`.js`, `jsx`) as-is. |
14+
| `ts-jest/presets/js-with-ts` | TypeScript and JavaScript files (`.ts`, `.tsx`, `.js`, `.jsx`) will be transformed by `ts-jest` to **CommonJS** syntax.<br/>You'll need to set `allowJs` to `true` in your `tsconfig.json` file. |
15+
| `ts-jest/presets/js-with-ts-esm` | TypeScript and JavaScript files (`.ts`, `.tsx`, `.js`, `.jsx`, `.mjs`) will be transformed by `ts-jest` to **ESM** syntax.<br/>You'll need to set `allowJs` to `true` in your `tsconfig.json` file. |
16+
| `ts-jest/presets/js-with-babel` | TypeScript files (`.ts`, `.tsx`) will be transformed by `ts-jest` to **CommonJS** syntax, and JavaScript files (`.js`, `jsx`) will be transformed by `babel-jest`. |
17+
| `ts-jest/presets/js-with-babel-esm` | TypeScript files (`.ts`, `.tsx`) will be transformed by `ts-jest` to **ESM** syntax, and JavaScript files (`.js`, `jsx`, `.mjs`) will be transformed by `babel-jest`. |
1518

1619
### Basic usage
1720

@@ -27,7 +30,7 @@ module.exports = {
2730
};
2831
```
2932

30-
```js
33+
```json5
3134
// OR package.json
3235
{
3336
// [...]
+36-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,41 @@
11
// preset and utils should work all the time
22
import * as presets from 'ts-jest/presets'
3+
import { JS_EXT_TO_TREAT_AS_ESM, TS_EXT_TO_TREAT_AS_ESM } from 'ts-jest/dist/constants'
34

45
test('presets', () => {
5-
const presetKeys = ['transform']
6-
expect(Object.keys(presets.defaults)).toEqual(presetKeys)
7-
expect(Object.keys(presets.jsWithBabel)).toEqual(presetKeys)
8-
expect(Object.keys(presets.jsWithTs)).toEqual(presetKeys)
6+
expect(presets.defaults).toEqual({
7+
transform: {
8+
'^.+\\.tsx?$': 'ts-jest',
9+
},
10+
})
11+
expect(presets.defaultsESM).toEqual({
12+
extensionsToTreatAsEsm: [...TS_EXT_TO_TREAT_AS_ESM],
13+
transform: {
14+
'^.+\\.tsx?$': 'ts-jest',
15+
},
16+
})
17+
expect(presets.jsWithTs).toEqual({
18+
transform: {
19+
'^.+\\.[tj]sx?$': 'ts-jest',
20+
},
21+
})
22+
expect(presets.jsWithTsESM).toEqual({
23+
extensionsToTreatAsEsm: [...JS_EXT_TO_TREAT_AS_ESM, ...TS_EXT_TO_TREAT_AS_ESM],
24+
transform: {
25+
'^.+\\.m?[tj]sx?$': 'ts-jest',
26+
},
27+
})
28+
expect(presets.jsWithBabel).toEqual({
29+
transform: {
30+
'^.+\\.tsx?$': 'ts-jest',
31+
'^.+\\.jsx?$': 'babel-jest',
32+
},
33+
})
34+
expect(presets.jsWithBabelESM).toEqual({
35+
extensionsToTreatAsEsm: [...JS_EXT_TO_TREAT_AS_ESM, ...TS_EXT_TO_TREAT_AS_ESM],
36+
transform: {
37+
'^.+\\.tsx?$': 'ts-jest',
38+
'^.+\\.m?[j]sx?$': 'babel-jest',
39+
},
40+
})
941
})

‎e2e/__cases__/ts-jest-checks/index.spec.ts

-8
This file was deleted.

‎e2e/__tests__/__snapshots__/jest-presets.test.ts.snap

-35
This file was deleted.

‎e2e/__tests__/jest-presets.test.ts

+2-26
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,10 @@
1-
import { allValidPackageSets, PackageSets } from '../__helpers__/templates'
1+
import { PackageSets } from '../__helpers__/templates'
22
import { configureTestCase } from '../__helpers__/test-case'
33

4-
// 'ts-jest' is tested in almost all test cases
5-
// 'ts-jest/presets/default' is an alias of the above
6-
// 'ts-jest/presets/js-with-ts' is tested in allow-js.test.ts
7-
8-
describe('ts-jest/presets/js-with-babel', () => {
9-
const testCase = configureTestCase('preset-with-babel', { jestConfig: { preset: 'ts-jest/presets/js-with-babel' } })
10-
11-
testCase.runWithTemplates([PackageSets.default], 1, (runTest, { testLabel }) => {
12-
it(testLabel, () => {
13-
const result = runTest()
14-
expect(result.status).toBe(1)
15-
expect(result.stderr).toMatch(/(Couldn't|Cannot) find (preset|module) ["']@babel\/preset-env["']/)
16-
})
17-
})
18-
19-
testCase.runWithTemplates([PackageSets.babel7, PackageSets.babel7StringConfig], 0, (runTest, { testLabel }) => {
20-
it(testLabel, () => {
21-
const result = runTest()
22-
expect(result.status).toBe(0)
23-
expect(result).toMatchSnapshot()
24-
})
25-
})
26-
})
27-
284
describe('ts-jest all presets', () => {
295
const testCase = configureTestCase('presets')
306

31-
testCase.runWithTemplates(allValidPackageSets, 0, (runTest, { testLabel }) => {
7+
testCase.runWithTemplates([PackageSets.default], 0, (runTest, { testLabel }) => {
328
it(testLabel, () => {
339
const result = runTest()
3410
expect(result.status).toBe(0)

‎e2e/__tests__/ts-jest-checks.test.ts

-13
This file was deleted.

‎presets/default-esm/jest-preset.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('..').defaultsESM

‎presets/index.js

+21-9
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,32 @@
1+
const { JS_EXT_TO_TREAT_AS_ESM, TS_EXT_TO_TREAT_AS_ESM } = require('../dist/constants')
12
const { createJestPreset } = require('../dist/presets/create-jest-preset')
23

34
module.exports = {
45
get defaults() {
56
return createJestPreset()
67
},
8+
get defaultsESM() {
9+
return createJestPreset(false, { extensionsToTreatAsEsm: TS_EXT_TO_TREAT_AS_ESM })
10+
},
711
get jsWithTs() {
8-
return createJestPreset({ allowJs: true })
12+
return createJestPreset(true)
13+
},
14+
get jsWithTsESM() {
15+
return createJestPreset(true, { extensionsToTreatAsEsm: [...JS_EXT_TO_TREAT_AS_ESM, ...TS_EXT_TO_TREAT_AS_ESM] })
916
},
1017
get jsWithBabel() {
11-
return createJestPreset(
12-
{ allowJs: false },
13-
{
14-
transform: {
15-
'^.+\\.jsx?$': 'babel-jest',
16-
},
17-
}
18-
)
18+
return createJestPreset(false, {
19+
transform: {
20+
'^.+\\.jsx?$': 'babel-jest',
21+
},
22+
})
23+
},
24+
get jsWithBabelESM() {
25+
return createJestPreset(false, {
26+
extensionsToTreatAsEsm: [...JS_EXT_TO_TREAT_AS_ESM, ...TS_EXT_TO_TREAT_AS_ESM],
27+
transform: {
28+
'^.+\\.m?[j]sx?$': 'babel-jest',
29+
},
30+
})
1931
},
2032
}
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('..').jsWithBabelESM

‎presets/js-with-ts-esm/jest-preset.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('..').jsWithTsESM

‎src/constants.ts

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ export const LINE_FEED = '\n'
22
export const TS_TSX_REGEX = /\.tsx?$/
33
export const JS_JSX_REGEX = /\.jsx?$/
44
export const DECLARATION_TYPE_EXT = '.d.ts'
5+
// `extensionsToTreatAsEsm` only accepts `.ts`, `.tsx` and `.jsx`. `.js` and `.mjs` will throw error
6+
export const TS_EXT_TO_TREAT_AS_ESM = ['.ts', '.tsx']
7+
export const JS_EXT_TO_TREAT_AS_ESM = ['.jsx']
58
/**
69
* @internal
710
* See https://jestjs.io/docs/en/configuration#testmatch-arraystring
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`create-jest-preset should return correct preset 1`] = `
4+
Object {
5+
"transform": Object {
6+
"^.+\\\\.tsx?$": "ts-jest",
7+
},
8+
}
9+
`;
10+
11+
exports[`create-jest-preset should return correct preset 2`] = `
12+
Object {
13+
"transform": Object {
14+
"^.+\\\\.tsx?$": "ts-jest",
15+
},
16+
}
17+
`;
18+
19+
exports[`create-jest-preset should return correct preset 3`] = `
20+
Object {
21+
"transform": Object {
22+
"^.+\\\\.[tj]sx?$": "ts-jest",
23+
},
24+
}
25+
`;
26+
27+
exports[`create-jest-preset should return correct preset 4`] = `
28+
Object {
29+
"transform": Object {
30+
"^.+\\\\.[tj]sx?$": "ts-jest",
31+
},
32+
}
33+
`;
34+
35+
exports[`create-jest-preset should return correct preset 5`] = `
36+
Object {
37+
"transform": Object {
38+
"^.+\\\\.tsx?$": "ts-jest",
39+
},
40+
}
41+
`;
42+
43+
exports[`create-jest-preset should return correct preset 6`] = `
44+
Object {
45+
"moduleFileExtensions": Array [
46+
"bar",
47+
],
48+
"testMatch": Array [
49+
"foo",
50+
],
51+
"transform": Object {
52+
"^.+\\\\.tsx?$": "ts-jest",
53+
"foo": "bar",
54+
},
55+
}
56+
`;
57+
58+
exports[`create-jest-preset should return correct preset 7`] = `
59+
Object {
60+
"extensionsToTreatAsEsm": Array [
61+
".jsx",
62+
".ts",
63+
".tsx",
64+
],
65+
"moduleFileExtensions": Array [
66+
"bar",
67+
],
68+
"testMatch": Array [
69+
"foo",
70+
],
71+
"transform": Object {
72+
"^.+\\\\.m?[tj]sx?$": "ts-jest",
73+
"foo": "bar",
74+
},
75+
}
76+
`;
+39-40
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,46 @@
11
import { createJestPreset } from './create-jest-preset'
2+
import { JS_EXT_TO_TREAT_AS_ESM, TS_EXT_TO_TREAT_AS_ESM } from '../constants'
23

34
describe('create-jest-preset', () => {
4-
it('should return correct defaults when allowJs is false or not set', () => {
5-
const withoutJs = {
6-
transform: {
7-
'^.+\\.tsx?$': 'ts-jest',
8-
},
9-
}
10-
expect(createJestPreset()).toEqual(withoutJs)
11-
expect(createJestPreset({ allowJs: false })).toEqual(withoutJs)
12-
})
5+
const baseExtraOptions = {
6+
testMatch: ['foo'],
7+
moduleFileExtensions: ['bar'],
8+
transform: { foo: 'bar' },
9+
}
1310

14-
it('should return correct defaults when allowJs is true', () => {
15-
expect(createJestPreset({ allowJs: true })).toEqual({
16-
transform: {
17-
'^.+\\.[tj]sx?$': 'ts-jest',
11+
test.each([
12+
{
13+
allowJs: undefined,
14+
extraOptions: undefined,
15+
},
16+
{
17+
allowJs: false,
18+
extraOptions: undefined,
19+
},
20+
{
21+
allowJs: true,
22+
extraOptions: undefined,
23+
},
24+
{
25+
allowJs: true,
26+
extraOptions: {},
27+
},
28+
{
29+
allowJs: false,
30+
extraOptions: {},
31+
},
32+
{
33+
allowJs: false,
34+
extraOptions: baseExtraOptions,
35+
},
36+
{
37+
allowJs: true,
38+
extraOptions: {
39+
...baseExtraOptions,
40+
extensionsToTreatAsEsm: [...JS_EXT_TO_TREAT_AS_ESM, ...TS_EXT_TO_TREAT_AS_ESM],
1841
},
19-
})
20-
})
21-
22-
it('should be able to use a base config', () => {
23-
expect(createJestPreset(undefined, {})).toMatchInlineSnapshot(`
24-
Object {
25-
"transform": Object {
26-
"^.+\\\\.tsx?$": "ts-jest",
27-
},
28-
}
29-
`)
30-
expect(
31-
createJestPreset(undefined, { testMatch: ['foo'], moduleFileExtensions: ['bar'], transform: { foo: 'bar' } }),
32-
).toMatchInlineSnapshot(`
33-
Object {
34-
"moduleFileExtensions": Array [
35-
"bar",
36-
],
37-
"testMatch": Array [
38-
"foo",
39-
],
40-
"transform": Object {
41-
"^.+\\\\.tsx?$": "ts-jest",
42-
"foo": "bar",
43-
},
44-
}
45-
`)
42+
},
43+
])('should return correct preset', (data) => {
44+
expect(createJestPreset(data.allowJs, data.extraOptions)).toMatchSnapshot()
4645
})
4746
})

‎src/presets/create-jest-preset.ts

+12-13
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,23 @@ import { rootLogger } from '../utils/logger'
44

55
const logger = rootLogger.child({ namespace: 'jest-preset' })
66

7-
export type TsJestPresets = Pick<Config.InitialOptions, 'moduleFileExtensions' | 'transform' | 'testMatch'>
7+
export type TsJestPresets = Pick<
8+
Config.InitialOptions,
9+
'extensionsToTreatAsEsm' | 'moduleFileExtensions' | 'transform' | 'testMatch'
10+
>
811

9-
interface CreateJestPresetOptions {
10-
allowJs?: boolean
11-
}
12-
13-
export function createJestPreset(
14-
{ allowJs = false }: CreateJestPresetOptions = {},
15-
from: Config.InitialOptions = {},
16-
): TsJestPresets {
12+
export function createJestPreset(allowJs = false, extraOptions: Config.InitialOptions = {}): TsJestPresets {
1713
logger.debug({ allowJs }, 'creating jest presets', allowJs ? 'handling' : 'not handling', 'JavaScript files')
1814

15+
const { extensionsToTreatAsEsm, moduleFileExtensions, testMatch } = extraOptions
16+
1917
return {
18+
...(extensionsToTreatAsEsm ? { extensionsToTreatAsEsm } : undefined),
19+
...(moduleFileExtensions ? { moduleFileExtensions } : undefined),
2020
transform: {
21-
...from.transform,
22-
[allowJs ? '^.+\\.[tj]sx?$' : '^.+\\.tsx?$']: 'ts-jest',
21+
...extraOptions.transform,
22+
[allowJs ? (extensionsToTreatAsEsm?.length ? '^.+\\.m?[tj]sx?$' : '^.+\\.[tj]sx?$') : '^.+\\.tsx?$']: 'ts-jest',
2323
},
24-
...(from.testMatch ? { testMatch: from.testMatch } : undefined),
25-
...(from.moduleFileExtensions ? { moduleFileExtensions: from.moduleFileExtensions } : undefined),
24+
...(testMatch ? { testMatch } : undefined),
2625
}
2726
}

‎src/ts-jest-transformer.ts

+10-6
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ import { parse, stringify } from './utils/json'
1212
import { JsonableValue } from './utils/jsonable-value'
1313
import { rootLogger } from './utils/logger'
1414
import { Errors, interpolate } from './utils/messages'
15-
import type { CompilerInstance } from './types'
15+
import type { TsJestProjectConfig, TransformOptionsTsJest } from './types'
1616
import { sha1 } from './utils/sha1'
1717
import { VersionCheckers } from './utils/version-checkers'
1818

1919
interface CachedConfigSet {
2020
configSet: ConfigSet
21-
jestConfig: JsonableValue<Config.ProjectConfig>
21+
jestConfig: JsonableValue<TsJestProjectConfig>
2222
transformerCfgStr: string
23-
compiler: CompilerInstance
23+
compiler: TsJestCompiler
2424
}
2525

2626
export interface DepGraphInfo {
@@ -40,7 +40,7 @@ export class TsJestTransformer implements Transformer {
4040
/**
4141
* @internal
4242
*/
43-
private _compiler!: CompilerInstance
43+
private _compiler!: TsJestCompiler
4444
protected readonly _logger: Logger
4545
protected _tsResolvedModulesCachePath: string | undefined
4646
protected _transformCfgStr!: string
@@ -113,7 +113,11 @@ export class TsJestTransformer implements Transformer {
113113
/**
114114
* @public
115115
*/
116-
process(fileContent: string, filePath: Config.Path, transformOptions: TransformOptions): TransformedSource | string {
116+
process(
117+
fileContent: string,
118+
filePath: Config.Path,
119+
transformOptions: TransformOptionsTsJest,
120+
): TransformedSource | string {
117121
this._logger.debug({ fileName: filePath, transformOptions }, 'processing', filePath)
118122

119123
let result: string | TransformedSource
@@ -177,7 +181,7 @@ export class TsJestTransformer implements Transformer {
177181
*
178182
* @public
179183
*/
180-
getCacheKey(fileContent: string, filePath: string, transformOptions: TransformOptions): string {
184+
getCacheKey(fileContent: string, filePath: string, transformOptions: TransformOptionsTsJest): string {
181185
const configs = this._configsFor(transformOptions)
182186

183187
this._logger.debug({ fileName: filePath, transformOptions }, 'computing cache key for', filePath)

‎src/types.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import type { Transformer } from '@jest/transform'
1+
import type { Transformer, TransformOptions } from '@jest/transform'
2+
import type { Config } from '@jest/types'
23
import type * as _babel from 'babel__core'
34
import type * as _ts from 'typescript'
45

@@ -172,6 +173,16 @@ export interface TsJestConfig {
172173
stringifyContentPathRegex: string | undefined
173174
}
174175

176+
export interface TsJestProjectConfig extends Config.ProjectConfig {
177+
globals: {
178+
'ts-jest': TsJestGlobalOptions
179+
}
180+
}
181+
182+
export interface TransformOptionsTsJest extends TransformOptions {
183+
config: TsJestProjectConfig
184+
}
185+
175186
export type ResolvedModulesMap = Map<string, _ts.ResolvedModuleFull | undefined> | undefined
176187
/**
177188
* @internal

0 commit comments

Comments
 (0)
Please sign in to comment.