diff --git a/docs/user/config/stringifyContentPathRegex.md b/docs/user/config/stringifyContentPathRegex.md index 9b5c669a24..1d72192fa6 100644 --- a/docs/user/config/stringifyContentPathRegex.md +++ b/docs/user/config/stringifyContentPathRegex.md @@ -18,16 +18,17 @@ In the `jest.config.js` version, you could do as in the `package.json` version o ```js // jest.config.js -const { jestPreset } = require('ts-jest'); +// Here `defaults` can be replaced with any other preset +const { defaults: tsjPreset } = require('ts-jest/presets'); module.exports = { // [...] moduleFileExtensions: [ - ...jestPreset.moduleFileExtensions, + ...tsjPreset.moduleFileExtensions, 'html' ], transform: { - ...jestPreset.transform, + ...tsjPreset.transform, '\\.html$': 'ts-jest' } globals: { diff --git a/docs/user/install.md b/docs/user/install.md index f228fd1c52..fd7b369ae8 100644 --- a/docs/user/install.md +++ b/docs/user/install.md @@ -55,7 +55,7 @@ yarn ts-jest config:init This will create a basic Jest configuration file which will make Jest know about your `.ts` files and handle them correctly. You can also use the `jest --init` command (prefixed with either `npx` or `yarn` depending on what you're using) to have more options related to Jest. -However, answer `no` to the Jest question about whether or not to enable Typescript. Instead, add the line: `preset: "ts-jest"` to the the `jest.config.js` file afterwards. +However, answer `no` to the Jest question about whether or not to enable Typescript. Instead, add the line: `preset: "ts-jest"` to the `jest.config.js` file afterwards. ### Customizing diff --git a/docs/user/react-native/index.md b/docs/user/react-native/index.md index a99ddde213..9b6f8dbfff 100644 --- a/docs/user/react-native/index.md +++ b/docs/user/react-native/index.md @@ -25,13 +25,13 @@ In the same way that you moved Babel config, move Jest config from `jest` key of ```js // jest.config.js -const { jestPreset: tsJest } = require('ts-jest'); +const { defaults: tsjPreset } = require('ts-jest/presets'); module.exports = { - ...tsJest, + ...tsjPreset, preset: 'react-native', transform: { - ...tsJest.transform, + ...tsjPreset.transform, '\\.js$': '/node_modules/react-native/jest/preprocessor.js', }, globals: { diff --git a/src/cli/config/init.ts b/src/cli/config/init.ts index 8c9797dffd..0745092643 100644 --- a/src/cli/config/init.ts +++ b/src/cli/config/init.ts @@ -10,7 +10,7 @@ import { basename, join } from 'path' import { Arguments } from 'yargs' import { CliCommand } from '..' -import { createJestPreset } from '../../config/create-jest-preset' +import { TsJestPresetDescriptor, defaults, jsWIthBabel, jsWithTs } from '../helpers/presets' /** * @internal @@ -24,12 +24,28 @@ export const run: CliCommand = async (args: Arguments /* , logger: Logger */) => const pkgFile = isPackage ? filePath : join(process.cwd(), 'package.json') const hasPackage = isPackage || existsSync(pkgFile) // read config - const { allowJs = false, jestPreset = true, tsconfig: askedTsconfig, babel = false, force, jsdom } = args + const { jestPreset = true, tsconfig: askedTsconfig, force, jsdom } = args const tsconfig = askedTsconfig === 'tsconfig.json' ? undefined : askedTsconfig - const hasPresetVar = allowJs || !jestPreset // read package const pkgJson = hasPackage ? JSON.parse(readFileSync(pkgFile, 'utf8')) : {} + // auto js/babel + let { js, babel } = args + if (js != null || babel != null) { + if (js == null) js = babel ? 'babel' : undefined + else if (babel == null) babel = js === 'babel' + } + + // preset + let preset: TsJestPresetDescriptor | undefined + if (js === 'babel') { + preset = jsWIthBabel + } else if (js === 'ts') { + preset = jsWithTs + } else { + preset = defaults + } + if (isPackage && !exists) { throw new Error(`File ${file} does not exists.`) } else if (!isPackage && exists && !force) { @@ -52,7 +68,7 @@ export const run: CliCommand = async (args: Arguments /* , logger: Logger */) => if (isPackage) { // package.json config - const base: any = hasPresetVar ? createJestPreset({ allowJs }) : { preset: 'ts-jest' } + const base: any = jestPreset ? { preset: preset.name } : { ...preset.value } if (!jsdom) base.testEnvironment = 'node' if (tsconfig || babel) { const tsJestConf: any = {} @@ -64,14 +80,14 @@ export const run: CliCommand = async (args: Arguments /* , logger: Logger */) => } else { // js config const content = [] - if (hasPresetVar) { - content.push(`const tsJest = require('ts-jest').createJestPreset({ allowJs: ${allowJs} });`, '') + if (!jestPreset) { + content.push(`${preset.jsImport('tsjPreset')};`, '') } content.push('module.exports = {') - if (hasPresetVar) { - content.push(` ...tsJest,`) + if (jestPreset) { + content.push(` preset: '${preset.name}',`) } else { - content.push(` preset: 'ts-jest',`) + content.push(` ...tsjPreset,`) } if (!jsdom) content.push(` testEnvironment: 'node',`) @@ -113,10 +129,11 @@ Arguments: Options: --force Discard any existing Jest config - --allow-js ts-jest will be used to process JS files as well + --js ts|babel Process .js files with ts-jest if 'ts' or with + babel-jest if 'babel' --no-jest-preset Disable the use of Jest presets --tsconfig Path to the tsconfig.json file - --babel Call BabelJest after ts-jest + --babel Pipe babel-jest after ts-jest --jsdom Use jsdom as test environment instead of node `) } diff --git a/src/cli/config/migrate.ts b/src/cli/config/migrate.ts index dfd4cd1d7a..4066ed2153 100644 --- a/src/cli/config/migrate.ts +++ b/src/cli/config/migrate.ts @@ -6,10 +6,8 @@ import { basename, resolve } from 'path' import { Arguments } from 'yargs' import { CliCommand } from '..' -import { TsJestPresets } from '../../config/create-jest-preset' import { backportJestConfig } from '../../util/backports' - -const DEFAULT_PRESET = 'ts-jest/presets/default' +import { JestPresetNames, TsJestPresetDescriptor, allPresets, defaults } from '../helpers/presets' /** * @internal @@ -36,12 +34,14 @@ export const run: CliCommand = async (args: Arguments /*, logger: Logger*/) => { // migrate // first we backport our options const migratedConfig = backportJestConfig(nullLogger, actualConfig) - let presetName: string | undefined + let presetName: JestPresetNames | undefined + let preset: TsJestPresetDescriptor | undefined // then we check if we can use `preset` if (!migratedConfig.preset && args.jestPreset) { // find the best preset - if (args.allowJs) presetName = 'ts-jest/presets/js-with-ts' - else { + if (args.js) { + presetName = args.js === 'babel' ? JestPresetNames.jsWIthBabel : JestPresetNames.jsWithTs + } else { // try to detect what transformer the js extensions would target const jsTransformers = Object.keys(migratedConfig.transform || {}).reduce( (list, pattern) => { @@ -59,49 +59,48 @@ export const run: CliCommand = async (args: Arguments /*, logger: Logger*/) => { const jsWithTs = jsTransformers.includes('ts-jest') const jsWithBabel = jsTransformers.includes('babel-jest') if (jsWithBabel && !jsWithTs) { - presetName = 'ts-jest/presets/js-with-babel' + presetName = JestPresetNames.jsWIthBabel } else if (jsWithTs && !jsWithBabel) { - presetName = 'ts-jest/presets/js-with-ts' + presetName = JestPresetNames.jsWithTs } else { // sounds like js files are NOT handled, or handled with a unknown transformer, so we do not need to handle it - presetName = DEFAULT_PRESET + presetName = JestPresetNames.default } } // ensure we are using a preset - presetName = presetName || DEFAULT_PRESET - migratedConfig.preset = presetName + presetName = presetName || JestPresetNames.default + preset = allPresets[presetName] footNotes.push( - `Detected preset '${presetName.replace( - /^ts-jest\/presets\//, - '', - )}' as the best matching preset for your configuration.\nVisit https://kulshekhar.github.io/ts-jest/user/config/#jest-preset for more information about presets.\n`, + `Detected preset '${preset.label}' as the best matching preset for your configuration. +Visit https://kulshekhar.github.io/ts-jest/user/config/#jest-preset for more information about presets. +`, ) } else if (migratedConfig.preset && migratedConfig.preset.startsWith('ts-jest')) { if (args.jestPreset === false) { delete migratedConfig.preset } else { - presetName = migratedConfig.preset + preset = (allPresets as any)[migratedConfig.preset] || defaults } } - const presets: TsJestPresets | undefined = presetName - ? require(`../../../${presetName.replace(/^ts-jest\//, '')}/jest-preset`) - : undefined + + // enforce the correct name + if (preset) migratedConfig.preset = preset.name // check the extensions - if (migratedConfig.moduleFileExtensions && migratedConfig.moduleFileExtensions.length && presets) { - const presetValue = dedupSort(presets.moduleFileExtensions).join('::') + if (migratedConfig.moduleFileExtensions && migratedConfig.moduleFileExtensions.length && preset) { + const presetValue = dedupSort(preset.value.moduleFileExtensions).join('::') const migratedValue = dedupSort(migratedConfig.moduleFileExtensions).join('::') if (presetValue === migratedValue) { delete migratedConfig.moduleFileExtensions } } // there is a testRegex, remove our testMatch - if (migratedConfig.testRegex && presets) { + if (migratedConfig.testRegex && preset) { migratedConfig.testMatch = null as any } // check the testMatch - else if (migratedConfig.testMatch && migratedConfig.testMatch.length && presets) { - const presetValue = dedupSort(presets.testMatch).join('::') + else if (migratedConfig.testMatch && migratedConfig.testMatch.length && preset) { + const presetValue = dedupSort(preset.value.testMatch).join('::') const migratedValue = dedupSort(migratedConfig.testMatch).join('::') if (presetValue === migratedValue) { delete migratedConfig.testMatch @@ -120,9 +119,9 @@ export const run: CliCommand = async (args: Arguments /*, logger: Logger*/) => { } // check if it's the same as the preset's one if ( - presets && + preset && migratedConfig.transform && - stringifyJson(migratedConfig.transform) === stringifyJson(presets.transform) + stringifyJson(migratedConfig.transform) === stringifyJson(preset.value.transform) ) { delete migratedConfig.transform } @@ -154,10 +153,10 @@ No migration needed for given Jest configuration // If it is the case, you can safely remove the "testMatch" from what I've migrated. // `) // } - if (presets && migratedConfig.transform) { + if (preset && migratedConfig.transform) { footNotes.push(` I couldn't check if your "transform" value is the same as mine which is: ${stringify( - presets.transform, + preset.value.transform, undefined, ' ', )} @@ -197,7 +196,7 @@ function cleanupConfig(config: jest.InitialOptions): void { config.testMatch = dedupSort(config.testMatch) if (config.testMatch.length === 0) delete config.testMatch } - if (config.preset === DEFAULT_PRESET) config.preset = 'ts-jest' + if (config.preset === JestPresetNames.default) config.preset = defaults.name } function dedupSort(arr: any[]) { @@ -220,7 +219,8 @@ Arguments: the "jest" property. Options: - --allow-js ts-jest will be used to process JS files as well + --js ts|babel Process .js files with ts-jest if 'ts' or with + babel-jest if 'babel' --no-jest-preset Disable the use of Jest presets `) } diff --git a/src/cli/helpers/presets.ts b/src/cli/helpers/presets.ts new file mode 100644 index 0000000000..8dd85c170e --- /dev/null +++ b/src/cli/helpers/presets.ts @@ -0,0 +1,55 @@ +import { TsJestPresets } from '../../types' + +/** @internal */ +export enum JestPresetNames { + default = 'ts-jest/presets/default', + jsWithTs = 'ts-jest/presets/js-with-ts', + jsWIthBabel = 'ts-jest/presets/js-with-babel', +} + +/** @internal */ +export interface TsJestPresetDescriptor { + name: string + fullName: string + label: string + jsVarName: string + value: TsJestPresets + isDefault: boolean + jsImport(varName?: string): string +} + +const definePreset = (fullName: string): TsJestPresetDescriptor => ({ + fullName, + get name() { + return this.isDefault ? 'ts-jest' : fullName + }, + get label() { + return fullName.split('/').pop()! + }, + get jsVarName() { + return this.isDefault + ? 'defaults' + : fullName + .split('/') + .pop()! + .replace(/\-([a-z])/g, (_, l) => l.toUpperCase()) + }, + get value() { + return require(`../../../${fullName.replace(/^ts-jest\//, '')}/jest-preset`) + }, + jsImport(varName = 'tsjPreset') { + return `const { ${this.jsVarName}: ${varName} } = require('${this.fullName}')` + }, + get isDefault() { + return fullName === JestPresetNames.default + }, +}) + +/** @internal */ +export const allPresets: Record = {} as any +/** @internal */ +export const defaults = (allPresets[JestPresetNames.default] = definePreset(JestPresetNames.default)) +/** @internal */ +export const jsWithTs = (allPresets[JestPresetNames.jsWithTs] = definePreset(JestPresetNames.jsWithTs)) +/** @internal */ +export const jsWIthBabel = (allPresets[JestPresetNames.jsWIthBabel] = definePreset(JestPresetNames.jsWIthBabel)) diff --git a/src/cli/index.ts b/src/cli/index.ts index 06f3421e14..4e54badd85 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -20,8 +20,21 @@ async function cli(args: string[]): Promise { count: ['verbose'], alias: { verbose: ['v'] }, default: { jestPreset: true, verbose: 0 }, + coerce: { + js(val: string) { + const res = val.trim().toLowerCase() + if (!['babel', 'ts'].includes(res)) throw new Error(`The 'js' option must be 'babel' or 'ts', given: '${val}'.`) + return res + }, + }, }) + // deprecated + if (parsedArgv.allowJs != null) { + if (parsedArgv.js) throw new Error(`The 'allowJs' and 'js' options cannot be set together.`) + parsedArgv.js = parsedArgv.allowJs ? 'ts' : undefined + } + let command = parsedArgv._.shift() as string const isHelp = command === 'help' if (isHelp) command = parsedArgv._.shift() as string