|
| 1 | +import { Logger } from 'bs-logger' |
| 2 | +import stringifyJson from 'fast-json-stable-stringify' |
| 3 | +import { existsSync } from 'fs' |
| 4 | +import { stringify as stringifyJson5 } from 'json5' |
| 5 | +import { basename, join } from 'path' |
| 6 | +import { Arguments } from 'yargs' |
| 7 | + |
| 8 | +import { CliCommand } from '..' |
| 9 | +import { createJestPreset } from '../../config/create-jest-preset' |
| 10 | +import { backportJestConfig } from '../../util/backports' |
| 11 | + |
| 12 | +export const run: CliCommand = async (args: Arguments, logger: Logger) => { |
| 13 | + const file = args._[0] |
| 14 | + const filePath = join(process.cwd(), file) |
| 15 | + if (!existsSync(filePath)) { |
| 16 | + throw new Error(`Configuration file ${file} does not exists.`) |
| 17 | + } |
| 18 | + const name = basename(file) |
| 19 | + const isPackage = name === 'package.json' |
| 20 | + if (!/\.(js|json)$/.test(name)) { |
| 21 | + throw new TypeError(`Configuration file ${file} must be a JavaScript or JSON file.`) |
| 22 | + } |
| 23 | + let actualConfig: jest.InitialOptions = require(filePath) |
| 24 | + if (isPackage) { |
| 25 | + actualConfig = (actualConfig as any).jest |
| 26 | + } |
| 27 | + if (!actualConfig) actualConfig = {} |
| 28 | + |
| 29 | + // migrate |
| 30 | + // first we backport our options |
| 31 | + const migratedConfig = backportJestConfig(logger, actualConfig) |
| 32 | + // then we check if we can use `preset` |
| 33 | + if (!migratedConfig.preset && args.jestPreset) { |
| 34 | + migratedConfig.preset = 'ts-jest' |
| 35 | + } else if (!args.jestPreset && migratedConfig.preset === 'ts-jest') { |
| 36 | + delete migratedConfig.preset |
| 37 | + } |
| 38 | + const usesPreset = migratedConfig.preset === 'ts-jest' |
| 39 | + const presets = createJestPreset({ allowJs: args.allowJs }) |
| 40 | + |
| 41 | + // check the extensions |
| 42 | + if (migratedConfig.moduleFileExtensions && migratedConfig.moduleFileExtensions.length && usesPreset) { |
| 43 | + const presetValue = dedupSort(presets.moduleFileExtensions).join('::') |
| 44 | + const migratedValue = dedupSort(migratedConfig.moduleFileExtensions).join('::') |
| 45 | + if (presetValue === migratedValue) { |
| 46 | + delete migratedConfig.moduleFileExtensions |
| 47 | + } |
| 48 | + } |
| 49 | + // check the testMatch |
| 50 | + if (migratedConfig.testMatch && migratedConfig.testMatch.length && usesPreset) { |
| 51 | + const presetValue = dedupSort(presets.testMatch).join('::') |
| 52 | + const migratedValue = dedupSort(migratedConfig.testMatch).join('::') |
| 53 | + if (presetValue === migratedValue) { |
| 54 | + delete migratedConfig.testMatch |
| 55 | + } |
| 56 | + } |
| 57 | + |
| 58 | + // migrate the transform |
| 59 | + if (migratedConfig.transform) { |
| 60 | + Object.keys(migratedConfig.transform).forEach(key => { |
| 61 | + const val = (migratedConfig.transform as any)[key] |
| 62 | + if (typeof val === 'string' && /\/?ts-jest(?:\/preprocessor\.js)?$/.test(val)) { |
| 63 | + // tslint:disable-next-line:semicolon |
| 64 | + ;(migratedConfig.transform as any)[key] = 'ts-jest' |
| 65 | + } |
| 66 | + }) |
| 67 | + } |
| 68 | + // check if it's the same as the preset's one |
| 69 | + if ( |
| 70 | + usesPreset && |
| 71 | + migratedConfig.transform && |
| 72 | + stringifyJson(migratedConfig.transform) === stringifyJson(presets.transform) |
| 73 | + ) { |
| 74 | + delete migratedConfig.transform |
| 75 | + } |
| 76 | + |
| 77 | + // cleanup |
| 78 | + cleanupConfig(actualConfig) |
| 79 | + cleanupConfig(migratedConfig) |
| 80 | + const before = stringifyJson(actualConfig) |
| 81 | + const after = stringifyJson(migratedConfig) |
| 82 | + if (after === before) { |
| 83 | + process.stderr.write(` |
| 84 | +No migration needed for given Jest configuration |
| 85 | + `) |
| 86 | + return |
| 87 | + } |
| 88 | + |
| 89 | + const stringify = /\.json$/.test(file) ? JSON.stringify : stringifyJson5 |
| 90 | + const footNotes: string[] = [] |
| 91 | + |
| 92 | + // if we are using preset, inform the user that he might be able to remove some section(s) |
| 93 | + // we couldn't check for equality |
| 94 | + // if (usesPreset && migratedConfig.testMatch) { |
| 95 | + // footNotes.push(` |
| 96 | + // I couldn't check if your "testMatch" value is the same as mine which is: ${stringify( |
| 97 | + // presets.testMatch, |
| 98 | + // undefined, |
| 99 | + // ' ', |
| 100 | + // )} |
| 101 | + // If it is the case, you can safely remove the "testMatch" from what I've migrated. |
| 102 | + // `) |
| 103 | + // } |
| 104 | + if (usesPreset && migratedConfig.transform) { |
| 105 | + footNotes.push(` |
| 106 | +I couldn't check if your "transform" value is the same as mine which is: ${stringify( |
| 107 | + presets.transform, |
| 108 | + undefined, |
| 109 | + ' ', |
| 110 | + )} |
| 111 | +If it is the case, you can safely remove the "transform" from what I've migrated. |
| 112 | +`) |
| 113 | + } |
| 114 | + |
| 115 | + // output new config |
| 116 | + process.stderr.write(` |
| 117 | +Migrated Jest configuration: |
| 118 | +`) |
| 119 | + process.stdout.write(`${stringify(migratedConfig, undefined, ' ')}\n`) |
| 120 | + process.stderr.write(` |
| 121 | +${footNotes.join('\n')} |
| 122 | +`) |
| 123 | +} |
| 124 | + |
| 125 | +function cleanupConfig(config: jest.InitialOptions): void { |
| 126 | + if (config.globals) { |
| 127 | + if ((config as any).globals['ts-jest'] && Object.keys((config as any).globals['ts-jest']).length === 0) { |
| 128 | + delete (config as any).globals['ts-jest'] |
| 129 | + } |
| 130 | + if (Object.keys(config.globals).length === 0) { |
| 131 | + delete config.globals |
| 132 | + } |
| 133 | + } |
| 134 | + if (config.transform && Object.keys(config.transform).length === 0) { |
| 135 | + delete config.transform |
| 136 | + } |
| 137 | + if (config.moduleFileExtensions) { |
| 138 | + config.moduleFileExtensions = dedupSort(config.moduleFileExtensions) |
| 139 | + if (config.moduleFileExtensions.length === 0) delete config.moduleFileExtensions |
| 140 | + } |
| 141 | + if (config.testMatch) { |
| 142 | + config.testMatch = dedupSort(config.testMatch) |
| 143 | + if (config.testMatch.length === 0) delete config.testMatch |
| 144 | + } |
| 145 | +} |
| 146 | + |
| 147 | +function dedupSort(arr: any[]) { |
| 148 | + return arr |
| 149 | + .filter((s, i, a) => a.findIndex(e => s.toString() === e.toString()) === i) |
| 150 | + .sort((a, b) => (a.toString() > b.toString() ? 1 : a.toString() < b.toString() ? -1 : 0)) |
| 151 | +} |
| 152 | + |
| 153 | +export const help: CliCommand = async () => { |
| 154 | + process.stdout.write(` |
| 155 | +Usage: |
| 156 | + ts-jest config:migrate [options] <config-file> |
| 157 | +
|
| 158 | +Arguments: |
| 159 | + <config-file> Can be a js or json Jest config file. If it is a |
| 160 | + package.json file, the configuration will be read from |
| 161 | + the "jest" property. |
| 162 | +
|
| 163 | +Options: |
| 164 | + --allow-js TSJest will be used to process JS files as well |
| 165 | + --no-jest-preset Disable the use of Jest presets |
| 166 | +`) |
| 167 | +} |
0 commit comments