Skip to content

Commit f9cc3c0

Browse files
committedJun 27, 2024
feat(presets): introduce util functions to create presets
This change will allow users to easily extend existing presets to override certain options
1 parent 9c616f9 commit f9cc3c0

9 files changed

+568
-188
lines changed
 

‎src/cli/cli.spec.ts

+92-99
Original file line numberDiff line numberDiff line change
@@ -117,20 +117,21 @@ describe('config', () => {
117117
'--tsconfig',
118118
'tsconfig.test.json',
119119
'--jsdom',
120-
'--no-jest-preset',
120+
'--jest-preset',
121121
'--js',
122122
'ts',
123123
'--babel',
124124
]
125125

126-
it('should create a jest.config.json (without options)', async () => {
126+
it('should create a jest.config.js (without options)', async () => {
127127
fs.existsSync.mockImplementation((f) => f === FAKE_PKG)
128-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
129-
fs.readFileSync.mockImplementation((f): any => {
130-
if (f === FAKE_PKG) return JSON.stringify({ name: 'mock', version: '0.0.0-mock.0' })
131-
throw new Error('ENOENT')
132-
})
133-
expect.assertions(2)
128+
fs.readFileSync
129+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
130+
.mockImplementationOnce((f): any => {
131+
if (f === FAKE_PKG) return JSON.stringify({ name: 'mock', version: '0.0.0-mock.0' })
132+
throw new Error('ENOENT')
133+
})
134+
expect.assertions(3)
134135
const res = await runCli(...noOption)
135136

136137
expect(res).toEqual({
@@ -141,26 +142,26 @@ Jest configuration written to "${normalize('/foo/bar/jest.config.js')}".
141142
`,
142143
stdout: '',
143144
})
144-
expect(fs.writeFileSync.mock.calls).toEqual([
145-
[
146-
normalize('/foo/bar/jest.config.js'),
147-
`/** @type {import('ts-jest').JestConfigWithTsJest} */
148-
module.exports = {
149-
preset: 'ts-jest',
150-
testEnvironment: 'node',
151-
};`,
152-
],
153-
])
145+
expect(fs.writeFileSync.mock.calls[0][0]).toBe(normalize('/foo/bar/jest.config.js'))
146+
expect(fs.writeFileSync.mock.calls[0][1]).toMatchInlineSnapshot(`
147+
"/** @type {import('ts-jest').JestConfigWithTsJest} **/
148+
module.exports = {
149+
testEnvironment: 'node',
150+
transform: {
151+
'^.+.tsx?$': 'ts-jest',
152+
},
153+
};"
154+
`)
154155
})
155156

156-
it('should create a jest.config.foo.json (with all options set)', async () => {
157+
it('should create a jest.config.foo.js (with all options set)', async () => {
157158
fs.existsSync.mockImplementation((f) => f === FAKE_PKG)
158159
// eslint-disable-next-line @typescript-eslint/no-explicit-any
159-
fs.readFileSync.mockImplementation((f): any => {
160+
fs.readFileSync.mockImplementationOnce((f): any => {
160161
if (f === FAKE_PKG) return JSON.stringify({ name: 'mock', version: '0.0.0-mock.0' })
161162
throw new Error('ENOENT')
162163
})
163-
expect.assertions(2)
164+
expect.assertions(3)
164165
const res = await runCli(...fullOptions, 'jest.config.foo.js')
165166

166167
expect(res).toEqual({
@@ -171,29 +172,60 @@ Jest configuration written to "${normalize('/foo/bar/jest.config.foo.js')}".
171172
`,
172173
stdout: '',
173174
})
174-
expect(fs.writeFileSync.mock.calls).toEqual([
175-
[
176-
normalize('/foo/bar/jest.config.foo.js'),
177-
`const { jsWithTs: tsjPreset } = require('ts-jest/presets');
178-
179-
/** @type {import('ts-jest').JestConfigWithTsJest} */
180-
module.exports = {
181-
...tsjPreset,
182-
transform: {
183-
'^.+\\\\.[tj]sx?$': ['ts-jest', {
184-
tsconfig: 'tsconfig.test.json',
185-
babelConfig: true,
186-
}],
187-
},
188-
};`,
189-
],
190-
])
175+
expect(fs.writeFileSync.mock.calls[0][0]).toBe(normalize('/foo/bar/jest.config.foo.js'))
176+
expect(fs.writeFileSync.mock.calls[0][1]).toMatchInlineSnapshot(`
177+
"/** @type {import('ts-jest').JestConfigWithTsJest} **/
178+
module.exports = {
179+
testEnvironment: 'jsdom',
180+
transform: {
181+
'^.+.[tj]sx?$':
182+
[
183+
'ts-jest',
184+
{
185+
tsconfig: 'tsconfig.test.json'
186+
}
187+
]
188+
,
189+
},
190+
};"
191+
`)
192+
})
193+
194+
it('should create jest config with type "module" package.json', async () => {
195+
fs.existsSync.mockImplementation((f) => f === FAKE_PKG)
196+
fs.readFileSync
197+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
198+
.mockImplementationOnce((f): any => {
199+
if (f === FAKE_PKG) return JSON.stringify({ name: 'mock', version: '0.0.0-mock.0', type: 'module' })
200+
throw new Error('ENOENT')
201+
})
202+
expect.assertions(3)
203+
const res = await runCli(...noOption)
204+
205+
expect(res).toEqual({
206+
exitCode: 0,
207+
log: '',
208+
stderr: `
209+
Jest configuration written to "${normalize('/foo/bar/jest.config.js')}".
210+
`,
211+
stdout: '',
212+
})
213+
expect(fs.writeFileSync.mock.calls[0][0]).toBe(normalize('/foo/bar/jest.config.js'))
214+
expect(fs.writeFileSync.mock.calls[0][1]).toMatchInlineSnapshot(`
215+
"/** @type {import('ts-jest').JestConfigWithTsJest} **/
216+
export default {
217+
testEnvironment: 'node',
218+
transform: {
219+
'^.+.tsx?$': 'ts-jest',
220+
},
221+
};"
222+
`)
191223
})
192224

193225
it('should update package.json (without options)', async () => {
194226
fs.existsSync.mockImplementation((f) => f === FAKE_PKG)
195227
// eslint-disable-next-line @typescript-eslint/no-explicit-any
196-
fs.readFileSync.mockImplementation((f): any => {
228+
fs.readFileSync.mockImplementationOnce((f): any => {
197229
if (f === FAKE_PKG) return JSON.stringify({ name: 'mock', version: '0.0.0-mock.0' })
198230
throw new Error('ENOENT')
199231
})
@@ -225,12 +257,13 @@ Jest configuration written to "${normalize('/foo/bar/package.json')}".
225257

226258
it('should update package.json (with all options set)', async () => {
227259
fs.existsSync.mockImplementation((f) => f === FAKE_PKG)
228-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
229-
fs.readFileSync.mockImplementation((f): any => {
230-
if (f === FAKE_PKG) return JSON.stringify({ name: 'mock', version: '0.0.0-mock.0' })
231-
throw new Error('ENOENT')
232-
})
233-
expect.assertions(2)
260+
fs.readFileSync
261+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
262+
.mockImplementationOnce((f): any => {
263+
if (f === FAKE_PKG) return JSON.stringify({ name: 'mock', version: '0.0.0-mock.0' })
264+
throw new Error('ENOENT')
265+
})
266+
expect.assertions(3)
234267
const res = await runCli(...fullOptions, 'package.json')
235268

236269
expect(res).toEqual({
@@ -241,26 +274,16 @@ Jest configuration written to "${normalize('/foo/bar/package.json')}".
241274
`,
242275
stdout: '',
243276
})
244-
expect(fs.writeFileSync.mock.calls).toEqual([
245-
[
246-
normalize('/foo/bar/package.json'),
247-
`{
248-
"name": "mock",
249-
"version": "0.0.0-mock.0",
250-
"jest": {
251-
"transform": {
252-
"^.+\\\\.[tj]sx?$": [
253-
"ts-jest",
254-
{
255-
"tsconfig": "tsconfig.test.json",
256-
"babelConfig": true
257-
}
258-
]
259-
}
260-
}
261-
}`,
262-
],
263-
])
277+
expect(fs.writeFileSync.mock.calls[0][0]).toBe(normalize('/foo/bar/package.json'))
278+
expect(fs.writeFileSync.mock.calls[0][1]).toMatchInlineSnapshot(`
279+
"{
280+
"name": "mock",
281+
"version": "0.0.0-mock.0",
282+
"jest": {
283+
"preset": "ts-jest/presets/js-with-ts"
284+
}
285+
}"
286+
`)
264287
})
265288

266289
it('should output help', async () => {
@@ -289,46 +312,16 @@ Jest configuration written to "${normalize('/foo/bar/package.json')}".
289312
290313
Options:
291314
--force Discard any existing Jest config
292-
--js ts|babel Process .js files with ts-jest if 'ts' or with
315+
--js ts|babel Process '.js' files with ts-jest if 'ts' or with
293316
babel-jest if 'babel'
294-
--no-jest-preset Disable the use of Jest presets
317+
--jest-preset Toggle using preset
295318
--tsconfig <file> Path to the tsconfig.json file
296-
--babel Pipe babel-jest after ts-jest
297-
--jsdom Use jsdom as test environment instead of node
319+
--babel Enable using Babel to process 'js' resulted content from 'ts-jest' processing
320+
--jsdom Use 'jsdom' as test environment instead of 'node'
298321
",
299322
}
300323
`)
301324
})
302-
303-
it('should create jest config with type "module" package.json', async () => {
304-
fs.existsSync.mockImplementation((f) => f === FAKE_PKG)
305-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
306-
fs.readFileSync.mockImplementation((f): any => {
307-
if (f === FAKE_PKG) return JSON.stringify({ name: 'mock', version: '0.0.0-mock.0', type: 'module' })
308-
throw new Error('ENOENT')
309-
})
310-
expect.assertions(2)
311-
const res = await runCli(...noOption)
312-
313-
expect(res).toEqual({
314-
exitCode: 0,
315-
log: '',
316-
stderr: `
317-
Jest configuration written to "${normalize('/foo/bar/jest.config.js')}".
318-
`,
319-
stdout: '',
320-
})
321-
expect(fs.writeFileSync.mock.calls).toEqual([
322-
[
323-
normalize('/foo/bar/jest.config.js'),
324-
`/** @type {import('ts-jest').JestConfigWithTsJest} */
325-
export default {
326-
preset: 'ts-jest',
327-
testEnvironment: 'node',
328-
};`,
329-
],
330-
])
331-
})
332325
})
333326

334327
describe('migrate', () => {

‎src/cli/config/init.ts

+38-32
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
import { existsSync, readFileSync, writeFileSync } from 'fs'
88
import { basename, join } from 'path'
99

10+
import ejs from 'ejs'
1011
import { stringify as stringifyJson5 } from 'json5'
1112

1213
import type { CliCommand, CliCommandArgs } from '..'
14+
import { JEST_CONFIG_EJS_TEMPLATE, TS_JS_TRANSFORM_PATTERN, TS_TRANSFORM_PATTERN } from '../../constants'
1315
import type { JestConfigWithTsJest, TsJestTransformerOptions } from '../../types'
14-
import { type TsJestPresetDescriptor, defaults, jsWIthBabel, jsWithTs } from '../helpers/presets'
16+
import { type TsJestPresetDescriptor, defaults, jsWIthBabel, jsWithTs, JestPresetNames } from '../helpers/presets'
1517

1618
/**
1719
* @internal
@@ -107,34 +109,38 @@ export const run: CliCommand = async (args: CliCommandArgs /* , logger: Logger *
107109
}
108110
body = JSON.stringify({ ...pkgJson, jest: jestConfig }, undefined, ' ')
109111
} else {
110-
// js config
111-
const content: string[] = []
112-
if (!jestPreset) {
113-
content.push(`${preset.jsImport('tsjPreset')};`, '')
114-
}
115-
content.push(`/** @type {import('ts-jest').JestConfigWithTsJest} */`)
116-
const usesModules = pkgJson.type === 'module'
117-
content.push(usesModules ? 'export default {' : 'module.exports = {')
118-
119-
if (jestPreset) {
120-
content.push(` preset: '${preset.name}',`)
121-
} else {
122-
content.push(' ...tsjPreset,')
123-
}
124-
if (!jsdom) content.push(" testEnvironment: 'node',")
125-
126-
if (tsconfig || shouldPostProcessWithBabel) {
127-
content.push(' transform: {')
128-
content.push(" '^.+\\\\.[tj]sx?$': ['ts-jest', {")
129-
if (tsconfig) content.push(` tsconfig: ${stringifyJson5(tsconfig)},`)
130-
if (shouldPostProcessWithBabel) content.push(' babelConfig: true,')
131-
content.push(' }],')
132-
content.push(' },')
112+
let transformPattern = TS_TRANSFORM_PATTERN
113+
let transformValue = !tsconfig
114+
? `'ts-jest'`
115+
: `
116+
[
117+
'ts-jest',
118+
{
119+
tsconfig: ${stringifyJson5(tsconfig)}
120+
}
121+
]
122+
`
123+
if (preset.name === JestPresetNames.jsWithTs) {
124+
transformPattern = TS_JS_TRANSFORM_PATTERN
125+
} else if (preset.name === JestPresetNames.jsWIthBabel) {
126+
transformValue = !tsconfig
127+
? `'ts-jest'`
128+
: `
129+
[
130+
'ts-jest',
131+
{
132+
babelConfig: true,
133+
tsconfig: ${stringifyJson5(tsconfig)}
134+
}
135+
]
136+
`
133137
}
134-
content.push('};')
135-
136-
// join all together
137-
body = content.join('\n')
138+
body = ejs.render(JEST_CONFIG_EJS_TEMPLATE, {
139+
exportKind: pkgJson.type === 'module' ? 'export default' : 'module.exports =',
140+
testEnvironment: jsdom ? 'jsdom' : 'node',
141+
transformPattern,
142+
transformValue,
143+
})
138144
}
139145

140146
writeFileSync(filePath, body)
@@ -160,11 +166,11 @@ Arguments:
160166
161167
Options:
162168
--force Discard any existing Jest config
163-
--js ts|babel Process .js files with ts-jest if 'ts' or with
169+
--js ts|babel Process '.js' files with ts-jest if 'ts' or with
164170
babel-jest if 'babel'
165-
--no-jest-preset Disable the use of Jest presets
171+
--jest-preset Toggle using preset
166172
--tsconfig <file> Path to the tsconfig.json file
167-
--babel Pipe babel-jest after ts-jest
168-
--jsdom Use jsdom as test environment instead of node
173+
--babel Enable using Babel to process 'js' resulted content from 'ts-jest' processing
174+
--jsdom Use 'jsdom' as test environment instead of 'node'
169175
`)
170176
}

‎src/cli/index.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { LogContexts, type Logger } from 'bs-logger'
22
import type { Arguments } from 'yargs'
33
import yargsParser from 'yargs-parser'
44

5+
import type { TsJestTransformerOptions } from '../types'
56
import { rootLogger } from '../utils'
67

78
const VALID_COMMANDS = ['help', 'config:migrate', 'config:init']
@@ -11,7 +12,14 @@ const logger = rootLogger.child({ [LogContexts.namespace]: 'cli', [LogContexts.a
1112
/**
1213
* @internal
1314
*/
14-
export type CliCommandArgs = Omit<Arguments, '$0'> & { _: Array<string | number> }
15+
export type CliCommandArgs = Omit<Arguments, '$0'> & { _: Array<string | number> } & {
16+
jestPreset?: boolean
17+
force?: boolean
18+
tsconfig?: TsJestTransformerOptions['tsconfig']
19+
babel?: boolean
20+
jsdom?: boolean
21+
js?: 'ts' | 'babel'
22+
}
1523
/**
1624
* @internal
1725
*/

‎src/constants.ts

+16
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ export const DECLARATION_TYPE_EXT = '.d.ts'
33
export const JS_JSX_EXTENSIONS = ['.js', '.jsx']
44
export const TS_TSX_REGEX = /\.[cm]?tsx?$/
55
export const JS_JSX_REGEX = /\.[cm]?jsx?$/
6+
export const TS_TRANSFORM_PATTERN = '^.+.tsx?$'
7+
export const ESM_TS_TRANSFORM_PATTERN = '^.+\\.m?tsx?$'
8+
export const TS_JS_TRANSFORM_PATTERN = '^.+.[tj]sx?$'
9+
export const ESM_TS_JS_TRANSFORM_PATTERN = '^.+\\.m?[tj]sx?$'
10+
export const JS_TRANSFORM_PATTERN = '^.+.jsx?$'
11+
export const ESM_JS_TRANSFORM_PATTERN = '^.+\\.m?jsx?$'
612
// `extensionsToTreatAsEsm` will throw error with `.mjs`
713
export const TS_EXT_TO_TREAT_AS_ESM = ['.ts', '.tsx', '.mts']
814
export const JS_EXT_TO_TREAT_AS_ESM = ['.jsx']
@@ -11,3 +17,13 @@ export const JS_EXT_TO_TREAT_AS_ESM = ['.jsx']
1117
* See https://jestjs.io/docs/en/configuration#testmatch-arraystring
1218
*/
1319
export const DEFAULT_JEST_TEST_MATCH = ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)']
20+
/**
21+
* @internal
22+
*/
23+
export const JEST_CONFIG_EJS_TEMPLATE = `/** @type {import('ts-jest').JestConfigWithTsJest} **/
24+
<%= exportKind %> {
25+
testEnvironment: '<%= testEnvironment %>',
26+
transform: {
27+
'<%= transformPattern %>': <%- transformValue %>,
28+
},
29+
};`

‎src/presets/__snapshots__/create-jest-preset.spec.ts.snap

+124-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,123 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`create-jest-preset should return correct preset 1`] = `
3+
exports[`create-jest-preset CJS presets createLegacyDefaultPreset should return preset config 1`] = `
4+
{
5+
"transform": {
6+
"^.+.tsx?$": [
7+
"ts-jest",
8+
{
9+
"tsconfig": "tsconfig.spec.json",
10+
},
11+
],
12+
},
13+
}
14+
`;
15+
16+
exports[`create-jest-preset CJS presets createLegacyJsWithTsPreset should return preset config 1`] = `
17+
{
18+
"transform": {
19+
"^.+.[tj]sx?$": [
20+
"ts-jest",
21+
{
22+
"isolatedModules": true,
23+
"tsconfig": "tsconfig.spec.json",
24+
},
25+
],
26+
},
27+
}
28+
`;
29+
30+
exports[`create-jest-preset CJS presets createLegacyWithBabelPreset should return preset config 1`] = `
31+
{
32+
"transform": {
33+
"^.+.jsx?$": [
34+
"babel-jest",
35+
{
36+
"babelrc": true,
37+
},
38+
],
39+
"^.+.tsx?$": [
40+
"ts-jest",
41+
{
42+
"babelConfig": true,
43+
"tsconfig": "tsconfig.spec.json",
44+
},
45+
],
46+
},
47+
}
48+
`;
49+
50+
exports[`create-jest-preset ESM presets createLegacyDefaultEsmPreset should return preset config 1`] = `
51+
{
52+
"extensionsToTreatAsEsm": [
53+
".jsx",
54+
".ts",
55+
".tsx",
56+
".mts",
57+
],
58+
"transform": {
59+
"^.+\\.m?tsx?$": [
60+
"ts-jest",
61+
{
62+
"tsconfig": "tsconfig.spec.json",
63+
"useESM": true,
64+
},
65+
],
66+
},
67+
}
68+
`;
69+
70+
exports[`create-jest-preset ESM presets createLegacyJsWithBabelEsmPreset should return preset config 1`] = `
71+
{
72+
"extensionsToTreatAsEsm": [
73+
".jsx",
74+
".ts",
75+
".tsx",
76+
".mts",
77+
],
78+
"transform": {
79+
"^.+\\.m?jsx?$": [
80+
"babel-jest",
81+
{
82+
"babelrc": true,
83+
},
84+
],
85+
"^.+\\.m?tsx?$": [
86+
"ts-jest",
87+
{
88+
"babelConfig": {
89+
"babelrc": true,
90+
},
91+
"tsconfig": "tsconfig.spec.json",
92+
"useESM": true,
93+
},
94+
],
95+
},
96+
}
97+
`;
98+
99+
exports[`create-jest-preset ESM presets createLegacyJsWithTsEsmPreset should return preset config 1`] = `
100+
{
101+
"extensionsToTreatAsEsm": [
102+
".jsx",
103+
".ts",
104+
".tsx",
105+
".mts",
106+
],
107+
"transform": {
108+
"^.+\\.m?[tj]sx?$": [
109+
"ts-jest",
110+
{
111+
"isolatedModules": true,
112+
"tsconfig": "tsconfig.spec.json",
113+
"useESM": true,
114+
},
115+
],
116+
},
117+
}
118+
`;
119+
120+
exports[`create-jest-preset createJestPreset should return correct preset 1`] = `
4121
{
5122
"transform": {
6123
"^.+\\.tsx?$": [
@@ -11,7 +128,7 @@ exports[`create-jest-preset should return correct preset 1`] = `
11128
}
12129
`;
13130

14-
exports[`create-jest-preset should return correct preset 2`] = `
131+
exports[`create-jest-preset createJestPreset should return correct preset 2`] = `
15132
{
16133
"transform": {
17134
"^.+\\.tsx?$": [
@@ -22,7 +139,7 @@ exports[`create-jest-preset should return correct preset 2`] = `
22139
}
23140
`;
24141

25-
exports[`create-jest-preset should return correct preset 3`] = `
142+
exports[`create-jest-preset createJestPreset should return correct preset 3`] = `
26143
{
27144
"transform": {
28145
"^.+\\.[tj]sx?$": [
@@ -33,7 +150,7 @@ exports[`create-jest-preset should return correct preset 3`] = `
33150
}
34151
`;
35152

36-
exports[`create-jest-preset should return correct preset 4`] = `
153+
exports[`create-jest-preset createJestPreset should return correct preset 4`] = `
37154
{
38155
"transform": {
39156
"^.+\\.[tj]sx?$": [
@@ -44,7 +161,7 @@ exports[`create-jest-preset should return correct preset 4`] = `
44161
}
45162
`;
46163

47-
exports[`create-jest-preset should return correct preset 5`] = `
164+
exports[`create-jest-preset createJestPreset should return correct preset 5`] = `
48165
{
49166
"transform": {
50167
"^.+\\.tsx?$": [
@@ -55,7 +172,7 @@ exports[`create-jest-preset should return correct preset 5`] = `
55172
}
56173
`;
57174

58-
exports[`create-jest-preset should return correct preset 6`] = `
175+
exports[`create-jest-preset createJestPreset should return correct preset 6`] = `
59176
{
60177
"moduleFileExtensions": [
61178
"bar",
@@ -73,7 +190,7 @@ exports[`create-jest-preset should return correct preset 6`] = `
73190
}
74191
`;
75192

76-
exports[`create-jest-preset should return correct preset 7`] = `
193+
exports[`create-jest-preset createJestPreset should return correct preset 7`] = `
77194
{
78195
"extensionsToTreatAsEsm": [
79196
".jsx",
+129-47
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,136 @@
11
import { JS_EXT_TO_TREAT_AS_ESM, TS_EXT_TO_TREAT_AS_ESM } from '../constants'
22

3-
import { createJestPreset } from './create-jest-preset'
3+
import {
4+
createJestPreset,
5+
createLegacyDefaultPreset,
6+
createLegacyWithBabelPreset,
7+
createLegacyJsWithTsPreset,
8+
createLegacyDefaultEsmPreset,
9+
createLegacyJsWithTsEsmPreset,
10+
createLegacyWithBabelEsmPreset,
11+
} from './create-jest-preset'
412

513
describe('create-jest-preset', () => {
6-
const baseExtraOptions = {
7-
testMatch: ['foo'],
8-
moduleFileExtensions: ['bar'],
9-
transform: { foo: 'bar' },
10-
}
11-
12-
test.each([
13-
{
14-
legacy: true,
15-
allowJs: undefined,
16-
extraOptions: undefined,
17-
},
18-
{
19-
legacy: false,
20-
allowJs: false,
21-
extraOptions: undefined,
22-
},
23-
{
24-
legacy: true,
25-
allowJs: true,
26-
extraOptions: undefined,
27-
},
28-
{
29-
legacy: false,
30-
allowJs: true,
31-
extraOptions: {},
32-
},
33-
{
34-
legacy: true,
35-
allowJs: false,
36-
extraOptions: {},
37-
},
38-
{
39-
legacy: false,
40-
allowJs: false,
41-
extraOptions: baseExtraOptions,
42-
},
43-
{
44-
legacy: true,
45-
allowJs: true,
46-
extraOptions: {
47-
...baseExtraOptions,
48-
extensionsToTreatAsEsm: [...JS_EXT_TO_TREAT_AS_ESM, ...TS_EXT_TO_TREAT_AS_ESM],
14+
describe('createJestPreset', () => {
15+
const baseExtraOptions = {
16+
testMatch: ['foo'],
17+
moduleFileExtensions: ['bar'],
18+
transform: { foo: 'bar' },
19+
}
20+
21+
test.each([
22+
{
23+
legacy: true,
24+
allowJs: undefined,
25+
extraOptions: undefined,
26+
},
27+
{
28+
legacy: false,
29+
allowJs: false,
30+
extraOptions: undefined,
31+
},
32+
{
33+
legacy: true,
34+
allowJs: true,
35+
extraOptions: undefined,
36+
},
37+
{
38+
legacy: false,
39+
allowJs: true,
40+
extraOptions: {},
4941
},
50-
},
51-
])('should return correct preset', (data) => {
52-
expect(createJestPreset(data.legacy, data.allowJs, data.extraOptions)).toMatchSnapshot()
42+
{
43+
legacy: true,
44+
allowJs: false,
45+
extraOptions: {},
46+
},
47+
{
48+
legacy: false,
49+
allowJs: false,
50+
extraOptions: baseExtraOptions,
51+
},
52+
{
53+
legacy: true,
54+
allowJs: true,
55+
extraOptions: {
56+
...baseExtraOptions,
57+
extensionsToTreatAsEsm: [...JS_EXT_TO_TREAT_AS_ESM, ...TS_EXT_TO_TREAT_AS_ESM],
58+
},
59+
},
60+
])('should return correct preset', (data) => {
61+
expect(createJestPreset(data.legacy, data.allowJs, data.extraOptions)).toMatchSnapshot()
62+
})
63+
})
64+
65+
describe('CJS presets', () => {
66+
describe('createLegacyDefaultPreset', () => {
67+
it('should return preset config', () => {
68+
expect(
69+
createLegacyDefaultPreset({
70+
tsconfig: 'tsconfig.spec.json',
71+
}),
72+
).toMatchSnapshot()
73+
})
74+
})
75+
76+
describe('createLegacyJsWithTsPreset', () => {
77+
it('should return preset config', () => {
78+
expect(
79+
createLegacyJsWithTsPreset({
80+
tsconfig: 'tsconfig.spec.json',
81+
isolatedModules: true,
82+
}),
83+
).toMatchSnapshot()
84+
})
85+
})
86+
87+
describe('createLegacyWithBabelPreset', () => {
88+
it('should return preset config', () => {
89+
expect(
90+
createLegacyWithBabelPreset({
91+
tsconfig: 'tsconfig.spec.json',
92+
babelConfig: {
93+
babelrc: true,
94+
},
95+
}),
96+
).toMatchSnapshot()
97+
})
98+
})
99+
})
100+
101+
describe('ESM presets', () => {
102+
describe('createLegacyDefaultEsmPreset', () => {
103+
it('should return preset config', () => {
104+
expect(
105+
createLegacyDefaultEsmPreset({
106+
tsconfig: 'tsconfig.spec.json',
107+
}),
108+
).toMatchSnapshot()
109+
})
110+
})
111+
112+
describe('createLegacyJsWithTsEsmPreset', () => {
113+
it('should return preset config', () => {
114+
expect(
115+
createLegacyJsWithTsEsmPreset({
116+
tsconfig: 'tsconfig.spec.json',
117+
isolatedModules: true,
118+
}),
119+
).toMatchSnapshot()
120+
})
121+
})
122+
123+
describe('createLegacyJsWithBabelEsmPreset', () => {
124+
it('should return preset config', () => {
125+
expect(
126+
createLegacyWithBabelEsmPreset({
127+
tsconfig: 'tsconfig.spec.json',
128+
babelConfig: {
129+
babelrc: true,
130+
},
131+
}),
132+
).toMatchSnapshot()
133+
})
134+
})
53135
})
54136
})

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

+137-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,30 @@
11
import type { Config } from '@jest/types'
22

3-
import type { TsJestPresets, TsJestTransformerOptions } from '../types'
3+
import {
4+
TS_EXT_TO_TREAT_AS_ESM,
5+
JS_EXT_TO_TREAT_AS_ESM,
6+
TS_TRANSFORM_PATTERN,
7+
TS_JS_TRANSFORM_PATTERN,
8+
JS_TRANSFORM_PATTERN,
9+
ESM_TS_TRANSFORM_PATTERN,
10+
ESM_TS_JS_TRANSFORM_PATTERN,
11+
ESM_JS_TRANSFORM_PATTERN,
12+
} from '../constants'
13+
import type {
14+
BabelConfig,
15+
DefaultPreset,
16+
JsWithBabelPreset,
17+
JsWithTsPreset,
18+
TsJestPresets,
19+
TsJestTransformerOptions,
20+
} from '../types'
421
import { rootLogger } from '../utils'
522

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

25+
/**
26+
* @deprecated use other functions below instead
27+
*/
828
export function createJestPreset(
929
legacy = false,
1030
allowJs = false,
@@ -28,3 +48,119 @@ export function createJestPreset(
2848
},
2949
}
3050
}
51+
52+
export function createLegacyDefaultPreset(
53+
tsJestTransformOptions: Omit<TsJestTransformerOptions, 'useESM' | 'babelConfig'> = {},
54+
): DefaultPreset {
55+
logger.debug('creating default legacy CJS Jest preset')
56+
57+
return {
58+
transform: {
59+
[TS_TRANSFORM_PATTERN]: ['ts-jest', tsJestTransformOptions],
60+
},
61+
}
62+
}
63+
64+
export function createLegacyJsWithTsPreset(
65+
tsJestTransformOptions: Omit<TsJestTransformerOptions, 'useESM' | 'babelConfig'> = {},
66+
): JsWithTsPreset {
67+
logger.debug('creating legacy Js with Ts CJS Jest preset')
68+
69+
return {
70+
transform: {
71+
[TS_JS_TRANSFORM_PATTERN]: ['ts-jest', tsJestTransformOptions],
72+
},
73+
}
74+
}
75+
76+
export function createLegacyWithBabelPreset(
77+
tsJestTransformOptions: Omit<TsJestTransformerOptions, 'useESM'> = {},
78+
): JsWithBabelPreset {
79+
logger.debug('creating legacy JS with Babel CJS Jest preset')
80+
81+
const babelConfig = tsJestTransformOptions.babelConfig
82+
83+
return {
84+
transform: {
85+
[JS_TRANSFORM_PATTERN]: ['babel-jest', typeof babelConfig === 'object' ? babelConfig : {}],
86+
[TS_TRANSFORM_PATTERN]: [
87+
'ts-jest',
88+
{
89+
...tsJestTransformOptions,
90+
babelConfig: true,
91+
},
92+
],
93+
},
94+
}
95+
}
96+
97+
export function createLegacyDefaultEsmPreset(tsJestTransformOptions: Omit<TsJestTransformerOptions, 'useESM'> = {}): {
98+
extensionsToTreatAsEsm: string[]
99+
transform: {
100+
[ESM_TS_TRANSFORM_PATTERN]: ['ts-jest', { useESM: true } & typeof tsJestTransformOptions]
101+
}
102+
} {
103+
logger.debug('creating default legacy ESM Jest preset')
104+
105+
return {
106+
extensionsToTreatAsEsm: [...JS_EXT_TO_TREAT_AS_ESM, ...TS_EXT_TO_TREAT_AS_ESM],
107+
transform: {
108+
[ESM_TS_TRANSFORM_PATTERN]: [
109+
'ts-jest',
110+
{
111+
...tsJestTransformOptions,
112+
useESM: true,
113+
},
114+
],
115+
},
116+
}
117+
}
118+
119+
export function createLegacyJsWithTsEsmPreset(tsJestTransformOptions: Omit<TsJestTransformerOptions, 'useESM'> = {}): {
120+
extensionsToTreatAsEsm: string[]
121+
transform: {
122+
[ESM_TS_JS_TRANSFORM_PATTERN]: ['ts-jest', { useESM: true } & typeof tsJestTransformOptions]
123+
}
124+
} {
125+
logger.debug('creating Js with Ts legacy ESM Jest preset')
126+
127+
return {
128+
extensionsToTreatAsEsm: [...JS_EXT_TO_TREAT_AS_ESM, ...TS_EXT_TO_TREAT_AS_ESM],
129+
transform: {
130+
[ESM_TS_JS_TRANSFORM_PATTERN]: [
131+
'ts-jest',
132+
{
133+
...tsJestTransformOptions,
134+
useESM: true,
135+
},
136+
],
137+
},
138+
}
139+
}
140+
141+
export function createLegacyWithBabelEsmPreset(tsJestTransformOptions: Omit<TsJestTransformerOptions, 'useESM'> = {}): {
142+
extensionsToTreatAsEsm: string[]
143+
transform: {
144+
[ESM_JS_TRANSFORM_PATTERN]: ['babel-jest', babelConfig: BabelConfig]
145+
[ESM_TS_TRANSFORM_PATTERN]: ['ts-jest', { useESM: true } & typeof tsJestTransformOptions]
146+
}
147+
} {
148+
logger.debug('creating JS with Babel legacy ESM Jest preset')
149+
150+
const babelConfig = tsJestTransformOptions.babelConfig
151+
152+
return {
153+
extensionsToTreatAsEsm: [...JS_EXT_TO_TREAT_AS_ESM, ...TS_EXT_TO_TREAT_AS_ESM],
154+
transform: {
155+
[ESM_JS_TRANSFORM_PATTERN]: ['babel-jest', typeof babelConfig === 'object' ? babelConfig : {}],
156+
[ESM_TS_TRANSFORM_PATTERN]: [
157+
'ts-jest',
158+
{
159+
...tsJestTransformOptions,
160+
useESM: true,
161+
babelConfig,
162+
},
163+
],
164+
},
165+
}
166+
}

‎src/types.ts

+21
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type * as babelJest from 'babel-jest'
44
import type * as _babel from 'babel__core'
55
import type * as _ts from 'typescript'
66

7+
import { JS_TRANSFORM_PATTERN, TS_JS_TRANSFORM_PATTERN, TS_TRANSFORM_PATTERN } from './constants'
78
import type { ConfigSet } from './legacy/config/config-set'
89
import type { RawCompilerOptions } from './raw-compiler-options'
910

@@ -203,6 +204,9 @@ export interface JestConfigWithTsJest extends Omit<Config.InitialOptions, 'trans
203204
}
204205
}
205206

207+
/**
208+
* @deprecated use `DefaultPreset`/`JsWithTsPreset`/`JsWithBabelPreset` instead
209+
*/
206210
export type TsJestPresets = Pick<
207211
JestConfigWithTsJest,
208212
'extensionsToTreatAsEsm' | 'moduleFileExtensions' | 'transform' | 'testMatch'
@@ -248,3 +252,20 @@ export interface TsJestAstTransformer {
248252
after: AstTransformerDesc[]
249253
afterDeclarations: AstTransformerDesc[]
250254
}
255+
256+
export type DefaultPreset = {
257+
transform: {
258+
[TS_TRANSFORM_PATTERN]: ['ts-jest', Omit<TsJestTransformerOptions, 'useESM' | 'babelConfig'>]
259+
}
260+
}
261+
export type JsWithTsPreset = {
262+
transform: {
263+
[TS_JS_TRANSFORM_PATTERN]: ['ts-jest', Omit<TsJestTransformerOptions, 'useESM' | 'babelConfig'>]
264+
}
265+
}
266+
export type JsWithBabelPreset = {
267+
transform: {
268+
[JS_TRANSFORM_PATTERN]: ['babel-jest', babelConfig: BabelConfig]
269+
[TS_TRANSFORM_PATTERN]: ['ts-jest', Omit<TsJestTransformerOptions, 'useESM'>]
270+
}
271+
}

‎src/utils/messages.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ export const enum Deprecations {
4242
GlobalsTsJestConfigOption = 'Define `ts-jest` config under `globals` is deprecated. Please do\n' +
4343
'transform: {\n' +
4444
" <transform_regex>: ['ts-jest', { /* ts-jest config goes here in Jest */ }],\n" +
45-
'},',
45+
'},\n' +
46+
'See more at https://kulshekhar.github.io/ts-jest/docs/getting-started/presets#advanced',
4647
}
4748

4849
/**

0 commit comments

Comments
 (0)
Please sign in to comment.