diff --git a/README.md b/README.md
index e725425892..0c4be82539 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@
-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).
+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).
---
diff --git a/docs/docs/presets.md b/docs/docs/presets.md
index 79545db037..74c371821a 100644
--- a/docs/docs/presets.md
+++ b/docs/docs/presets.md
@@ -3,15 +3,18 @@ id: presets
title: Presets
---
-### The 3 presets
+### The presets
-`ts-jest` comes with 3 presets, covering most of the project's base configuration:
+`ts-jest` comes with several presets, covering most of the project's base configuration:
| Preset name | Description |
|---|---|
-| `ts-jest/presets/default`
or `ts-jest` | `ts-jest` will take care of `.ts` and `.tsx` files only, leaving JavaScript files as-is. |
-| `ts-jest/presets/js-with-ts` | TypeScript and JavaScript files (`.ts`, `.tsx`, `.js` and `.jsx`) will be handled by `ts-jest`.
You'll need to set `allowJs` to `true` in your `tsconfig.json` file. |
-| `ts-jest/presets/js-with-babel` | TypeScript files will be handled by `ts-jest`, and JavaScript files will be handled by `babel-jest`. |
+| `ts-jest/presets/default`
or `ts-jest` | TypeScript files (`.ts`, `.tsx`) will be transformed by `ts-jest` to **CommonJS** syntax, leaving JavaScript files (`.js`, `jsx`) as-is. |
+| `ts-jest/presets/default-esm`
| TypeScript files (`.ts`, `.tsx`) will be transformed by `ts-jest` to **ESM** syntax, leaving JavaScript files (`.js`, `jsx`) as-is. |
+| `ts-jest/presets/js-with-ts` | TypeScript and JavaScript files (`.ts`, `.tsx`, `.js`, `.jsx`) will be transformed by `ts-jest` to **CommonJS** syntax.
You'll need to set `allowJs` to `true` in your `tsconfig.json` file. |
+| `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.
You'll need to set `allowJs` to `true` in your `tsconfig.json` file. |
+| `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`. |
+| `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`. |
### Basic usage
@@ -27,7 +30,7 @@ module.exports = {
};
```
-```js
+```json5
// OR package.json
{
// [...]
diff --git a/e2e/__cases__/presets/ts-jest-presets.spec.ts b/e2e/__cases__/presets/ts-jest-presets.spec.ts
index a3359d9fc0..0012429072 100644
--- a/e2e/__cases__/presets/ts-jest-presets.spec.ts
+++ b/e2e/__cases__/presets/ts-jest-presets.spec.ts
@@ -1,9 +1,41 @@
// preset and utils should work all the time
import * as presets from 'ts-jest/presets'
+import { JS_EXT_TO_TREAT_AS_ESM, TS_EXT_TO_TREAT_AS_ESM } from 'ts-jest/dist/constants'
test('presets', () => {
- const presetKeys = ['transform']
- expect(Object.keys(presets.defaults)).toEqual(presetKeys)
- expect(Object.keys(presets.jsWithBabel)).toEqual(presetKeys)
- expect(Object.keys(presets.jsWithTs)).toEqual(presetKeys)
+ expect(presets.defaults).toEqual({
+ transform: {
+ '^.+\\.tsx?$': 'ts-jest',
+ },
+ })
+ expect(presets.defaultsESM).toEqual({
+ extensionsToTreatAsEsm: [...TS_EXT_TO_TREAT_AS_ESM],
+ transform: {
+ '^.+\\.tsx?$': 'ts-jest',
+ },
+ })
+ expect(presets.jsWithTs).toEqual({
+ transform: {
+ '^.+\\.[tj]sx?$': 'ts-jest',
+ },
+ })
+ expect(presets.jsWithTsESM).toEqual({
+ extensionsToTreatAsEsm: [...JS_EXT_TO_TREAT_AS_ESM, ...TS_EXT_TO_TREAT_AS_ESM],
+ transform: {
+ '^.+\\.m?[tj]sx?$': 'ts-jest',
+ },
+ })
+ expect(presets.jsWithBabel).toEqual({
+ transform: {
+ '^.+\\.tsx?$': 'ts-jest',
+ '^.+\\.jsx?$': 'babel-jest',
+ },
+ })
+ expect(presets.jsWithBabelESM).toEqual({
+ extensionsToTreatAsEsm: [...JS_EXT_TO_TREAT_AS_ESM, ...TS_EXT_TO_TREAT_AS_ESM],
+ transform: {
+ '^.+\\.tsx?$': 'ts-jest',
+ '^.+\\.m?[j]sx?$': 'babel-jest',
+ },
+ })
})
diff --git a/e2e/__cases__/ts-jest-checks/index.spec.ts b/e2e/__cases__/ts-jest-checks/index.spec.ts
deleted file mode 100644
index afda5cec8a..0000000000
--- a/e2e/__cases__/ts-jest-checks/index.spec.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { readFileSync } from 'fs'
-import { ConfigSet } from 'ts-jest/dist/config/config-set'
-
-it('should have digest and versions', () => {
- const cs = new ConfigSet(Object.create(null))
- expect(cs.tsJestDigest).toHaveLength(40)
- expect(cs.tsJestDigest).toBe(readFileSync(require.resolve('ts-jest/.ts-jest-digest'), 'utf8'))
-})
diff --git a/e2e/__tests__/__snapshots__/jest-presets.test.ts.snap b/e2e/__tests__/__snapshots__/jest-presets.test.ts.snap
deleted file mode 100644
index 1429b99dc6..0000000000
--- a/e2e/__tests__/__snapshots__/jest-presets.test.ts.snap
+++ /dev/null
@@ -1,35 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`ts-jest/presets/js-with-babel should pass using template "with-babel-7" 1`] = `
- √ jest
- ↳ exit code: 0
- ===[ STDOUT ]===================================================================
-
- ===[ STDERR ]===================================================================
- PASS ./main.spec.js
- √ spread
-
- Test Suites: 1 passed, 1 total
- Tests: 1 passed, 1 total
- Snapshots: 0 total
- Time: XXs
- Ran all test suites.
- ================================================================================
-`;
-
-exports[`ts-jest/presets/js-with-babel should pass using template "with-babel-7-string-config" 1`] = `
- √ jest
- ↳ exit code: 0
- ===[ STDOUT ]===================================================================
-
- ===[ STDERR ]===================================================================
- PASS ./main.spec.js
- √ spread
-
- Test Suites: 1 passed, 1 total
- Tests: 1 passed, 1 total
- Snapshots: 0 total
- Time: XXs
- Ran all test suites.
- ================================================================================
-`;
diff --git a/e2e/__tests__/jest-presets.test.ts b/e2e/__tests__/jest-presets.test.ts
index d8d7667f44..b1ded2aaf2 100644
--- a/e2e/__tests__/jest-presets.test.ts
+++ b/e2e/__tests__/jest-presets.test.ts
@@ -1,34 +1,10 @@
-import { allValidPackageSets, PackageSets } from '../__helpers__/templates'
+import { PackageSets } from '../__helpers__/templates'
import { configureTestCase } from '../__helpers__/test-case'
-// 'ts-jest' is tested in almost all test cases
-// 'ts-jest/presets/default' is an alias of the above
-// 'ts-jest/presets/js-with-ts' is tested in allow-js.test.ts
-
-describe('ts-jest/presets/js-with-babel', () => {
- const testCase = configureTestCase('preset-with-babel', { jestConfig: { preset: 'ts-jest/presets/js-with-babel' } })
-
- testCase.runWithTemplates([PackageSets.default], 1, (runTest, { testLabel }) => {
- it(testLabel, () => {
- const result = runTest()
- expect(result.status).toBe(1)
- expect(result.stderr).toMatch(/(Couldn't|Cannot) find (preset|module) ["']@babel\/preset-env["']/)
- })
- })
-
- testCase.runWithTemplates([PackageSets.babel7, PackageSets.babel7StringConfig], 0, (runTest, { testLabel }) => {
- it(testLabel, () => {
- const result = runTest()
- expect(result.status).toBe(0)
- expect(result).toMatchSnapshot()
- })
- })
-})
-
describe('ts-jest all presets', () => {
const testCase = configureTestCase('presets')
- testCase.runWithTemplates(allValidPackageSets, 0, (runTest, { testLabel }) => {
+ testCase.runWithTemplates([PackageSets.default], 0, (runTest, { testLabel }) => {
it(testLabel, () => {
const result = runTest()
expect(result.status).toBe(0)
diff --git a/e2e/__tests__/ts-jest-checks.test.ts b/e2e/__tests__/ts-jest-checks.test.ts
deleted file mode 100644
index 99a0554378..0000000000
--- a/e2e/__tests__/ts-jest-checks.test.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { allValidPackageSets } from '../__helpers__/templates'
-import { configureTestCase } from '../__helpers__/test-case'
-
-describe('ts-jest internal checks test', () => {
- const testCase = configureTestCase('ts-jest-checks')
-
- testCase.runWithTemplates(allValidPackageSets, 0, (runTest, { testLabel }) => {
- it(testLabel, () => {
- const result = runTest()
- expect(result.status).toBe(0)
- })
- })
-})
diff --git a/presets/default-esm/jest-preset.js b/presets/default-esm/jest-preset.js
new file mode 100644
index 0000000000..caa8bf6070
--- /dev/null
+++ b/presets/default-esm/jest-preset.js
@@ -0,0 +1 @@
+module.exports = require('..').defaultsESM
diff --git a/presets/index.js b/presets/index.js
index 2d67bb225e..07eedbbca6 100644
--- a/presets/index.js
+++ b/presets/index.js
@@ -1,20 +1,32 @@
+const { JS_EXT_TO_TREAT_AS_ESM, TS_EXT_TO_TREAT_AS_ESM } = require('../dist/constants')
const { createJestPreset } = require('../dist/presets/create-jest-preset')
module.exports = {
get defaults() {
return createJestPreset()
},
+ get defaultsESM() {
+ return createJestPreset(false, { extensionsToTreatAsEsm: TS_EXT_TO_TREAT_AS_ESM })
+ },
get jsWithTs() {
- return createJestPreset({ allowJs: true })
+ return createJestPreset(true)
+ },
+ get jsWithTsESM() {
+ return createJestPreset(true, { extensionsToTreatAsEsm: [...JS_EXT_TO_TREAT_AS_ESM, ...TS_EXT_TO_TREAT_AS_ESM] })
},
get jsWithBabel() {
- return createJestPreset(
- { allowJs: false },
- {
- transform: {
- '^.+\\.jsx?$': 'babel-jest',
- },
- }
- )
+ return createJestPreset(false, {
+ transform: {
+ '^.+\\.jsx?$': 'babel-jest',
+ },
+ })
+ },
+ get jsWithBabelESM() {
+ return createJestPreset(false, {
+ extensionsToTreatAsEsm: [...JS_EXT_TO_TREAT_AS_ESM, ...TS_EXT_TO_TREAT_AS_ESM],
+ transform: {
+ '^.+\\.m?[j]sx?$': 'babel-jest',
+ },
+ })
},
}
diff --git a/presets/js-with-babel-esm/jest-preset.js b/presets/js-with-babel-esm/jest-preset.js
new file mode 100644
index 0000000000..9598209f1d
--- /dev/null
+++ b/presets/js-with-babel-esm/jest-preset.js
@@ -0,0 +1 @@
+module.exports = require('..').jsWithBabelESM
diff --git a/presets/js-with-ts-esm/jest-preset.js b/presets/js-with-ts-esm/jest-preset.js
new file mode 100644
index 0000000000..5573ca60e2
--- /dev/null
+++ b/presets/js-with-ts-esm/jest-preset.js
@@ -0,0 +1 @@
+module.exports = require('..').jsWithTsESM
diff --git a/src/constants.ts b/src/constants.ts
index 1c19aeb993..1c33a9d9e4 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -2,6 +2,9 @@ export const LINE_FEED = '\n'
export const TS_TSX_REGEX = /\.tsx?$/
export const JS_JSX_REGEX = /\.jsx?$/
export const DECLARATION_TYPE_EXT = '.d.ts'
+// `extensionsToTreatAsEsm` only accepts `.ts`, `.tsx` and `.jsx`. `.js` and `.mjs` will throw error
+export const TS_EXT_TO_TREAT_AS_ESM = ['.ts', '.tsx']
+export const JS_EXT_TO_TREAT_AS_ESM = ['.jsx']
/**
* @internal
* See https://jestjs.io/docs/en/configuration#testmatch-arraystring
diff --git a/src/presets/__snapshots__/create-jest-preset.spec.ts.snap b/src/presets/__snapshots__/create-jest-preset.spec.ts.snap
new file mode 100644
index 0000000000..2f8d12dbd7
--- /dev/null
+++ b/src/presets/__snapshots__/create-jest-preset.spec.ts.snap
@@ -0,0 +1,76 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`create-jest-preset should return correct preset 1`] = `
+Object {
+ "transform": Object {
+ "^.+\\\\.tsx?$": "ts-jest",
+ },
+}
+`;
+
+exports[`create-jest-preset should return correct preset 2`] = `
+Object {
+ "transform": Object {
+ "^.+\\\\.tsx?$": "ts-jest",
+ },
+}
+`;
+
+exports[`create-jest-preset should return correct preset 3`] = `
+Object {
+ "transform": Object {
+ "^.+\\\\.[tj]sx?$": "ts-jest",
+ },
+}
+`;
+
+exports[`create-jest-preset should return correct preset 4`] = `
+Object {
+ "transform": Object {
+ "^.+\\\\.[tj]sx?$": "ts-jest",
+ },
+}
+`;
+
+exports[`create-jest-preset should return correct preset 5`] = `
+Object {
+ "transform": Object {
+ "^.+\\\\.tsx?$": "ts-jest",
+ },
+}
+`;
+
+exports[`create-jest-preset should return correct preset 6`] = `
+Object {
+ "moduleFileExtensions": Array [
+ "bar",
+ ],
+ "testMatch": Array [
+ "foo",
+ ],
+ "transform": Object {
+ "^.+\\\\.tsx?$": "ts-jest",
+ "foo": "bar",
+ },
+}
+`;
+
+exports[`create-jest-preset should return correct preset 7`] = `
+Object {
+ "extensionsToTreatAsEsm": Array [
+ ".jsx",
+ ".ts",
+ ".tsx",
+ ],
+ "moduleFileExtensions": Array [
+ "bar",
+ ],
+ "testMatch": Array [
+ "foo",
+ ],
+ "transform": Object {
+ "^.+\\\\.m?[tj]sx?$": "ts-jest",
+ "foo": "bar",
+ },
+}
+`;
diff --git a/src/presets/create-jest-preset.spec.ts b/src/presets/create-jest-preset.spec.ts
index a906c9e46c..49d8b7530c 100644
--- a/src/presets/create-jest-preset.spec.ts
+++ b/src/presets/create-jest-preset.spec.ts
@@ -1,47 +1,46 @@
import { createJestPreset } from './create-jest-preset'
+import { JS_EXT_TO_TREAT_AS_ESM, TS_EXT_TO_TREAT_AS_ESM } from '../constants'
describe('create-jest-preset', () => {
- it('should return correct defaults when allowJs is false or not set', () => {
- const withoutJs = {
- transform: {
- '^.+\\.tsx?$': 'ts-jest',
- },
- }
- expect(createJestPreset()).toEqual(withoutJs)
- expect(createJestPreset({ allowJs: false })).toEqual(withoutJs)
- })
+ const baseExtraOptions = {
+ testMatch: ['foo'],
+ moduleFileExtensions: ['bar'],
+ transform: { foo: 'bar' },
+ }
- it('should return correct defaults when allowJs is true', () => {
- expect(createJestPreset({ allowJs: true })).toEqual({
- transform: {
- '^.+\\.[tj]sx?$': 'ts-jest',
+ test.each([
+ {
+ allowJs: undefined,
+ extraOptions: undefined,
+ },
+ {
+ allowJs: false,
+ extraOptions: undefined,
+ },
+ {
+ allowJs: true,
+ extraOptions: undefined,
+ },
+ {
+ allowJs: true,
+ extraOptions: {},
+ },
+ {
+ allowJs: false,
+ extraOptions: {},
+ },
+ {
+ allowJs: false,
+ extraOptions: baseExtraOptions,
+ },
+ {
+ allowJs: true,
+ extraOptions: {
+ ...baseExtraOptions,
+ extensionsToTreatAsEsm: [...JS_EXT_TO_TREAT_AS_ESM, ...TS_EXT_TO_TREAT_AS_ESM],
},
- })
- })
-
- it('should be able to use a base config', () => {
- expect(createJestPreset(undefined, {})).toMatchInlineSnapshot(`
-Object {
- "transform": Object {
- "^.+\\\\.tsx?$": "ts-jest",
- },
-}
-`)
- expect(
- createJestPreset(undefined, { testMatch: ['foo'], moduleFileExtensions: ['bar'], transform: { foo: 'bar' } }),
- ).toMatchInlineSnapshot(`
-Object {
- "moduleFileExtensions": Array [
- "bar",
- ],
- "testMatch": Array [
- "foo",
- ],
- "transform": Object {
- "^.+\\\\.tsx?$": "ts-jest",
- "foo": "bar",
- },
-}
-`)
+ },
+ ])('should return correct preset', (data) => {
+ expect(createJestPreset(data.allowJs, data.extraOptions)).toMatchSnapshot()
})
})
diff --git a/src/presets/create-jest-preset.ts b/src/presets/create-jest-preset.ts
index 0b48c79eac..28be1528af 100644
--- a/src/presets/create-jest-preset.ts
+++ b/src/presets/create-jest-preset.ts
@@ -4,24 +4,23 @@ import { rootLogger } from '../utils/logger'
const logger = rootLogger.child({ namespace: 'jest-preset' })
-export type TsJestPresets = Pick
+export type TsJestPresets = Pick<
+ Config.InitialOptions,
+ 'extensionsToTreatAsEsm' | 'moduleFileExtensions' | 'transform' | 'testMatch'
+>
-interface CreateJestPresetOptions {
- allowJs?: boolean
-}
-
-export function createJestPreset(
- { allowJs = false }: CreateJestPresetOptions = {},
- from: Config.InitialOptions = {},
-): TsJestPresets {
+export function createJestPreset(allowJs = false, extraOptions: Config.InitialOptions = {}): TsJestPresets {
logger.debug({ allowJs }, 'creating jest presets', allowJs ? 'handling' : 'not handling', 'JavaScript files')
+ const { extensionsToTreatAsEsm, moduleFileExtensions, testMatch } = extraOptions
+
return {
+ ...(extensionsToTreatAsEsm ? { extensionsToTreatAsEsm } : undefined),
+ ...(moduleFileExtensions ? { moduleFileExtensions } : undefined),
transform: {
- ...from.transform,
- [allowJs ? '^.+\\.[tj]sx?$' : '^.+\\.tsx?$']: 'ts-jest',
+ ...extraOptions.transform,
+ [allowJs ? (extensionsToTreatAsEsm?.length ? '^.+\\.m?[tj]sx?$' : '^.+\\.[tj]sx?$') : '^.+\\.tsx?$']: 'ts-jest',
},
- ...(from.testMatch ? { testMatch: from.testMatch } : undefined),
- ...(from.moduleFileExtensions ? { moduleFileExtensions: from.moduleFileExtensions } : undefined),
+ ...(testMatch ? { testMatch } : undefined),
}
}
diff --git a/src/ts-jest-transformer.ts b/src/ts-jest-transformer.ts
index da267c47af..5faf00fddb 100644
--- a/src/ts-jest-transformer.ts
+++ b/src/ts-jest-transformer.ts
@@ -12,15 +12,15 @@ import { parse, stringify } from './utils/json'
import { JsonableValue } from './utils/jsonable-value'
import { rootLogger } from './utils/logger'
import { Errors, interpolate } from './utils/messages'
-import type { CompilerInstance } from './types'
+import type { TsJestProjectConfig, TransformOptionsTsJest } from './types'
import { sha1 } from './utils/sha1'
import { VersionCheckers } from './utils/version-checkers'
interface CachedConfigSet {
configSet: ConfigSet
- jestConfig: JsonableValue
+ jestConfig: JsonableValue
transformerCfgStr: string
- compiler: CompilerInstance
+ compiler: TsJestCompiler
}
export interface DepGraphInfo {
@@ -40,7 +40,7 @@ export class TsJestTransformer implements Transformer {
/**
* @internal
*/
- private _compiler!: CompilerInstance
+ private _compiler!: TsJestCompiler
protected readonly _logger: Logger
protected _tsResolvedModulesCachePath: string | undefined
protected _transformCfgStr!: string
@@ -113,7 +113,11 @@ export class TsJestTransformer implements Transformer {
/**
* @public
*/
- process(fileContent: string, filePath: Config.Path, transformOptions: TransformOptions): TransformedSource | string {
+ process(
+ fileContent: string,
+ filePath: Config.Path,
+ transformOptions: TransformOptionsTsJest,
+ ): TransformedSource | string {
this._logger.debug({ fileName: filePath, transformOptions }, 'processing', filePath)
let result: string | TransformedSource
@@ -177,7 +181,7 @@ export class TsJestTransformer implements Transformer {
*
* @public
*/
- getCacheKey(fileContent: string, filePath: string, transformOptions: TransformOptions): string {
+ getCacheKey(fileContent: string, filePath: string, transformOptions: TransformOptionsTsJest): string {
const configs = this._configsFor(transformOptions)
this._logger.debug({ fileName: filePath, transformOptions }, 'computing cache key for', filePath)
diff --git a/src/types.ts b/src/types.ts
index d83634f6f1..2dbaa54b9a 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1,4 +1,5 @@
-import type { Transformer } from '@jest/transform'
+import type { Transformer, TransformOptions } from '@jest/transform'
+import type { Config } from '@jest/types'
import type * as _babel from 'babel__core'
import type * as _ts from 'typescript'
@@ -172,6 +173,16 @@ export interface TsJestConfig {
stringifyContentPathRegex: string | undefined
}
+export interface TsJestProjectConfig extends Config.ProjectConfig {
+ globals: {
+ 'ts-jest': TsJestGlobalOptions
+ }
+}
+
+export interface TransformOptionsTsJest extends TransformOptions {
+ config: TsJestProjectConfig
+}
+
export type ResolvedModulesMap = Map | undefined
/**
* @internal