-
Notifications
You must be signed in to change notification settings - Fork 453
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(cli): adds a cli tool to migrate old config
TSJest migrates the config on the fly to stay compatible with older versions, but it generates warning. This adds a cli tool which users can call to migrate their configuraton easily without opening an issue :D
- Loading branch information
Showing
12 changed files
with
361 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
#!/usr/bin/env node | ||
|
||
require('./dist/cli') |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
import { Logger } from 'bs-logger' | ||
import stringifyJson from 'fast-json-stable-stringify' | ||
import { existsSync } from 'fs' | ||
import { stringify as stringifyJson5 } from 'json5' | ||
import { basename, join } from 'path' | ||
import { Arguments } from 'yargs' | ||
|
||
import { CliCommand } from '..' | ||
import { createJestPreset } from '../../config/create-jest-preset' | ||
import { backportJestConfig } from '../../util/backports' | ||
|
||
export const run: CliCommand = async (args: Arguments, logger: Logger) => { | ||
const file = args._[0] | ||
const filePath = join(process.cwd(), file) | ||
if (!existsSync(filePath)) { | ||
throw new Error(`Configuration file ${file} does not exists.`) | ||
} | ||
const name = basename(file) | ||
const isPackage = name === 'package.json' | ||
if (!/\.(js|json)$/.test(name)) { | ||
throw new TypeError(`Configuration file ${file} must be a JavaScript or JSON file.`) | ||
} | ||
let actualConfig: jest.InitialOptions = require(filePath) | ||
if (isPackage) { | ||
actualConfig = (actualConfig as any).jest | ||
} | ||
if (!actualConfig) actualConfig = {} | ||
|
||
// migrate | ||
// first we backport our options | ||
const migratedConfig = backportJestConfig(logger, actualConfig) | ||
// then we check if we can use `preset` | ||
if (!migratedConfig.preset && args.jestPreset) { | ||
migratedConfig.preset = 'ts-jest' | ||
} else if (!args.jestPreset && migratedConfig.preset === 'ts-jest') { | ||
delete migratedConfig.preset | ||
} | ||
const usesPreset = migratedConfig.preset === 'ts-jest' | ||
const presets = createJestPreset({ allowJs: args.allowJs }) | ||
|
||
// check the extensions | ||
if (migratedConfig.moduleFileExtensions && migratedConfig.moduleFileExtensions.length && usesPreset) { | ||
const presetValue = dedupSort(presets.moduleFileExtensions).join('::') | ||
const migratedValue = dedupSort(migratedConfig.moduleFileExtensions).join('::') | ||
if (presetValue === migratedValue) { | ||
delete migratedConfig.moduleFileExtensions | ||
} | ||
} | ||
// check the testMatch | ||
if (migratedConfig.testMatch && migratedConfig.testMatch.length && usesPreset) { | ||
const presetValue = dedupSort(presets.testMatch).join('::') | ||
const migratedValue = dedupSort(migratedConfig.testMatch).join('::') | ||
if (presetValue === migratedValue) { | ||
delete migratedConfig.testMatch | ||
} | ||
} | ||
|
||
// migrate the transform | ||
if (migratedConfig.transform) { | ||
Object.keys(migratedConfig.transform).forEach(key => { | ||
const val = (migratedConfig.transform as any)[key] | ||
if (typeof val === 'string' && /\/?ts-jest(?:\/preprocessor\.js)?$/.test(val)) { | ||
// tslint:disable-next-line:semicolon | ||
;(migratedConfig.transform as any)[key] = 'ts-jest' | ||
} | ||
}) | ||
} | ||
// check if it's the same as the preset's one | ||
if ( | ||
usesPreset && | ||
migratedConfig.transform && | ||
stringifyJson(migratedConfig.transform) === stringifyJson(presets.transform) | ||
) { | ||
delete migratedConfig.transform | ||
} | ||
|
||
// cleanup | ||
cleanupConfig(actualConfig) | ||
cleanupConfig(migratedConfig) | ||
const before = stringifyJson(actualConfig) | ||
const after = stringifyJson(migratedConfig) | ||
if (after === before) { | ||
process.stderr.write(` | ||
No migration needed for given Jest configuration | ||
`) | ||
return | ||
} | ||
|
||
const stringify = /\.json$/.test(file) ? JSON.stringify : stringifyJson5 | ||
const footNotes: string[] = [] | ||
|
||
// if we are using preset, inform the user that he might be able to remove some section(s) | ||
// we couldn't check for equality | ||
// if (usesPreset && migratedConfig.testMatch) { | ||
// footNotes.push(` | ||
// I couldn't check if your "testMatch" value is the same as mine which is: ${stringify( | ||
// presets.testMatch, | ||
// undefined, | ||
// ' ', | ||
// )} | ||
// If it is the case, you can safely remove the "testMatch" from what I've migrated. | ||
// `) | ||
// } | ||
if (usesPreset && migratedConfig.transform) { | ||
footNotes.push(` | ||
I couldn't check if your "transform" value is the same as mine which is: ${stringify( | ||
presets.transform, | ||
undefined, | ||
' ', | ||
)} | ||
If it is the case, you can safely remove the "transform" from what I've migrated. | ||
`) | ||
} | ||
|
||
// output new config | ||
process.stderr.write(` | ||
Migrated Jest configuration: | ||
`) | ||
process.stdout.write(`${stringify(migratedConfig, undefined, ' ')}\n`) | ||
process.stderr.write(` | ||
${footNotes.join('\n')} | ||
`) | ||
} | ||
|
||
function cleanupConfig(config: jest.InitialOptions): void { | ||
if (config.globals) { | ||
if ((config as any).globals['ts-jest'] && Object.keys((config as any).globals['ts-jest']).length === 0) { | ||
delete (config as any).globals['ts-jest'] | ||
} | ||
if (Object.keys(config.globals).length === 0) { | ||
delete config.globals | ||
} | ||
} | ||
if (config.transform && Object.keys(config.transform).length === 0) { | ||
delete config.transform | ||
} | ||
if (config.moduleFileExtensions) { | ||
config.moduleFileExtensions = dedupSort(config.moduleFileExtensions) | ||
if (config.moduleFileExtensions.length === 0) delete config.moduleFileExtensions | ||
} | ||
if (config.testMatch) { | ||
config.testMatch = dedupSort(config.testMatch) | ||
if (config.testMatch.length === 0) delete config.testMatch | ||
} | ||
} | ||
|
||
function dedupSort(arr: any[]) { | ||
return arr | ||
.filter((s, i, a) => a.findIndex(e => s.toString() === e.toString()) === i) | ||
.sort((a, b) => (a.toString() > b.toString() ? 1 : a.toString() < b.toString() ? -1 : 0)) | ||
} | ||
|
||
export const help: CliCommand = async () => { | ||
process.stdout.write(` | ||
Usage: | ||
ts-jest config:migrate [options] <config-file> | ||
Arguments: | ||
<config-file> Can be a js or json Jest config file. If it is a | ||
package.json file, the configuration will be read from | ||
the "jest" property. | ||
Options: | ||
--allow-js TSJest will be used to process JS files as well | ||
--no-jest-preset Disable the use of Jest presets | ||
`) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { Arguments } from 'yargs' | ||
|
||
export const run = async (_: Arguments) => { | ||
process.stdout.write(` | ||
Usage: | ||
ts-jest command [options] [...args] | ||
Commands: | ||
help [command] Show this help, or help about a command | ||
config:migrate Migrates a given Jest configuration | ||
Example: | ||
ts-jest help config:migrate | ||
`) | ||
} | ||
|
||
export { run as help } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { LogContexts, Logger } from 'bs-logger' | ||
import { Arguments } from 'yargs' | ||
import yargsParser from 'yargs-parser' | ||
|
||
import { rootLogger } from '../util/logger' | ||
|
||
const VALID_COMMANDS = ['help', 'config:migrate'] | ||
|
||
// tslint:disable-next-line:prefer-const | ||
let [, , ...args] = process.argv | ||
|
||
const logger = rootLogger.child({ [LogContexts.namespace]: 'cli', [LogContexts.application]: 'ts-jest' }) | ||
|
||
const parsedArgv = yargsParser(args, { | ||
boolean: ['dryRun', 'jestPreset', 'allowJs', 'diff'], | ||
count: ['verbose'], | ||
alias: { verbose: ['v'] }, | ||
default: { dryRun: false, jestPreset: true, allowJs: false, verbose: 0, diff: false }, | ||
}) | ||
let command = parsedArgv._.shift() as string | ||
const isHelp = command === 'help' | ||
if (isHelp) command = parsedArgv._.shift() as string | ||
|
||
if (!VALID_COMMANDS.includes(command)) command = 'help' | ||
|
||
export type CliCommand = (argv: Arguments, logger: Logger) => Promise<void> | ||
|
||
// tslint:disable-next-line:no-var-requires | ||
const { run, help }: { run: CliCommand; help: CliCommand } = require(`./${command.replace(/:/g, '/')}`) | ||
|
||
const cmd = isHelp && command !== 'help' ? help : run | ||
|
||
cmd(parsedArgv, logger).then( | ||
() => { | ||
process.exit(0) | ||
}, | ||
(err: Error) => { | ||
logger.fatal(err.message) | ||
process.exit(1) | ||
}, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.