Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: migrate jest-cli to TypeScript #8024

Merged
merged 15 commits into from Mar 5, 2019
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -78,6 +78,7 @@
- `[jest-validate]`: Migrate to TypeScript ([#7991](https://github.com/facebook/jest/pull/7991))
- `[docs]`: Update CONTRIBUTING.md to add information about running jest with `jest-circus` locally ([#8013](https://github.com/facebook/jest/pull/8013)).
- `[@jest/core]`: Migrate to TypeScript ([#7998](https://github.com/facebook/jest/pull/7998))
- `[jest-cli]`: Migrate to TypeScript

### Performance

Expand Down
2 changes: 2 additions & 0 deletions packages/jest-cli/package.json
Expand Up @@ -3,8 +3,10 @@
"description": "Delightful JavaScript Testing.",
"version": "24.1.0",
"main": "build/index.js",
"types": "build/index.d.ts",
"dependencies": {
"@jest/core": "^24.1.0",
"@jest/types": "^24.1.0",
"chalk": "^2.0.1",
"exit": "^0.1.2",
"import-local": "^2.0.0",
Expand Down
Expand Up @@ -3,16 +3,14 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import type {Argv} from 'types/Argv';

import {Config} from '@jest/types';
import {isJSONString} from 'jest-config';
import isCI from 'is-ci';
import {Options} from 'yargs';

export const check = (argv: Argv) => {
export const check = (argv: Config.Argv) => {
if (argv.runInBand && argv.hasOwnProperty('maxWorkers')) {
throw new Error(
'Both --runInBand and --maxWorkers were specified, but these two ' +
Expand Down Expand Up @@ -69,7 +67,7 @@ export const usage =
'Usage: $0 [--config=<pathToConfigFile>] [TestPathPattern]';
export const docs = 'Documentation: https://jestjs.io/';

export const options = {
export const options: Record<keyof Config.Argv, Options> = {
all: {
default: undefined,
description:
Expand Down
Expand Up @@ -3,32 +3,25 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import type {AggregatedResult} from 'types/TestResult';
import type {Argv} from 'types/Argv';
import type {GlobalConfig, Path} from 'types/Config';

import path from 'path';
import {Config, TestResult} from '@jest/types';
import {clearLine} from 'jest-util';
import {validateCLIOptions} from 'jest-validate';
import {deprecationEntries} from 'jest-config';
import {runCLI} from '@jest/core';
import * as args from './args';
import chalk from 'chalk';
import exit from 'exit';
import yargs from 'yargs';
import {sync as realpath} from 'realpath-native';
import init from '../init';
import getVersion from '../version';
import * as args from './args';

import {version as VERSION} from '../../package.json';

export async function run(maybeArgv?: Argv, project?: Path) {
export async function run(maybeArgv?: Config.Argv, project?: Config.Path) {
try {
// $FlowFixMe:`allow reduced return
const argv: Argv = buildArgv(maybeArgv);
const argv: Config.Argv = buildArgv(maybeArgv);

if (argv.init) {
await init();
Expand All @@ -48,18 +41,23 @@ export async function run(maybeArgv?: Argv, project?: Path) {
}
}

export const buildArgv = (maybeArgv: ?Argv) => {
export const buildArgv = (maybeArgv?: Config.Argv): Config.Argv => {
const version =
VERSION + (__dirname.includes(`packages${path.sep}jest-cli`) ? '-dev' : '');

const rawArgv: Argv | string[] = maybeArgv || process.argv.slice(2);
const argv: Argv = yargs(rawArgv)
.usage(args.usage)
.version(version)
.alias('help', 'h')
.options(args.options)
.epilogue(args.docs)
.check(args.check).argv;
getVersion() +
(__dirname.includes(`packages${path.sep}jest-cli`) ? '-dev' : '');

const rawArgv: Config.Argv | Array<string> =
maybeArgv || process.argv.slice(2);
const argv =
maybeArgv ||
yargs(process.argv.slice(2))
.usage(args.usage)
.version(version)
.alias('help', 'h')
.options(args.options)
.epilogue(args.docs)
// @ts-ignore: it's unable to infer what arguments it contains
.check(args.check).argv;

validateCLIOptions(
argv,
Expand All @@ -71,16 +69,21 @@ export const buildArgv = (maybeArgv: ?Argv) => {
);

// strip dashed args
return Object.keys(argv).reduce((result, key) => {
if (!key.includes('-')) {
// $FlowFixMe:`allow reduced return
result[key] = argv[key];
}
return result;
}, {});
return Object.keys(argv).reduce(
(result, key) => {
if (!key.includes('-')) {
result[key] = argv[key];
}
return result;
},
{} as Config.Argv,
);
};

const getProjectListFromCLIArgs = (argv, project: ?Path) => {
const getProjectListFromCLIArgs = (
argv: Config.Argv,
project?: Config.Path,
) => {
const projects = argv.projects ? argv.projects : [];

if (project) {
Expand All @@ -104,8 +107,8 @@ const getProjectListFromCLIArgs = (argv, project: ?Path) => {
};

const readResultsAndExit = (
result: ?AggregatedResult,
globalConfig: GlobalConfig,
result: TestResult.AggregatedResult | null,
globalConfig: Config.GlobalConfig,
) => {
const code = !result || result.success ? 0 : globalConfig.testFailureExitCode;

Expand Down Expand Up @@ -140,7 +143,6 @@ const readResultsAndExit = (
'`--detectOpenHandles` to troubleshoot this issue.',
),
);
// $FlowFixMe: `unref` exists in Node
}, 1000).unref();
}
};
Expand Up @@ -3,8 +3,6 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

// TODO: remove exports for the next major
Expand Down
Expand Up @@ -3,8 +3,6 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

export const PACKAGE_JSON = 'package.json';
Expand Down
Expand Up @@ -3,32 +3,23 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

export class NotFoundPackageJsonError extends Error {
name: string;
message: string;

constructor(rootDir: string) {
super();
super(`Could not find a "package.json" file in ${rootDir}`);
this.name = '';
this.message = `Could not find a "package.json" file in ${rootDir}`;
Error.captureStackTrace(this, () => {});
}
}

export class MalformedPackageJsonError extends Error {
name: string;
message: string;

constructor(packageJsonPath: string) {
super();
this.name = '';
this.message =
super(
`There is malformed json in ${packageJsonPath}\n` +
'Fix it, and then run "jest --init"';
'Fix it, and then run "jest --init"',
);
this.name = '';
Error.captureStackTrace(this, () => {});
}
}
Expand Up @@ -3,15 +3,14 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import {Config} from '@jest/types';
import {defaults, descriptions} from 'jest-config';

const stringifyOption = (
option: string,
map: Object,
option: keyof Config.InitialOptions,
map: Partial<Config.InitialOptions>,
linePrefix: string = '',
): string => {
const optionDescription = ` // ${descriptions[option]}`;
Expand All @@ -32,7 +31,7 @@ const stringifyOption = (
);
};

const generateConfigFile = (results: {[string]: boolean}): string => {
const generateConfigFile = (results: {[key: string]: unknown}): string => {
const {coverage, clearMocks, environment} = results;

const overrides: Object = {};
Expand All @@ -55,15 +54,21 @@ const generateConfigFile = (results: {[string]: boolean}): string => {
});
}

const overrideKeys: Array<string> = Object.keys(overrides);
const overrideKeys = Object.keys(overrides) as Array<
keyof Config.InitialOptions
>;

const properties: Array<string> = [];

for (const option in descriptions) {
if (overrideKeys.includes(option)) {
properties.push(stringifyOption(option, overrides));
const opt = option as keyof typeof descriptions;

if (overrideKeys.includes(opt)) {
properties.push(stringifyOption(opt, overrides));
} else {
properties.push(stringifyOption(option, defaults, '// '));
properties.push(
stringifyOption(opt, defaults as Config.InitialOptions, '// '),
);
}
}

Expand Down
Expand Up @@ -3,26 +3,25 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import chalk from 'chalk';
import fs from 'fs';
import path from 'path';
import chalk from 'chalk';
import prompts from 'prompts';
import {sync as realpath} from 'realpath-native';
import defaultQuestions, {testScriptQuestion} from './questions';
import {NotFoundPackageJsonError, MalformedPackageJsonError} from './errors';
import {PACKAGE_JSON, JEST_CONFIG} from './constants';
import generateConfigFile from './generate_config_file';
import modifyPackageJson from './modify_package_json';
import {ProjectPackageJson} from './types';

type PromptsResults = {
clearMocks: boolean,
coverage: boolean,
environment: boolean,
scripts: boolean,
clearMocks: boolean;
coverage: boolean;
environment: boolean;
scripts: boolean;
};

export default async (rootDir: string = realpath(process.cwd())) => {
Expand All @@ -37,7 +36,7 @@ export default async (rootDir: string = realpath(process.cwd())) => {
const questions = defaultQuestions.slice(0);
let hasJestProperty: boolean = false;
let hasJestConfig: boolean = false;
let projectPackageJson: ?Object;
let projectPackageJson: ProjectPackageJson;

try {
projectPackageJson = JSON.parse(
Expand Down Expand Up @@ -89,6 +88,7 @@ export default async (rootDir: string = realpath(process.cwd())) => {

let promptAborted: boolean = false;

// @ts-ignore: Return type cannot be object - faulty typings
const results: PromptsResults = await prompts(questions, {
onCancel: () => {
promptAborted = true;
Expand Down
Expand Up @@ -3,17 +3,16 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import {ProjectPackageJson} from './types';

const modifyPackageJson = ({
projectPackageJson,
shouldModifyScripts,
hasJestProperty,
}: {
projectPackageJson: Object,
shouldModifyScripts: boolean,
projectPackageJson: ProjectPackageJson;
shouldModifyScripts: boolean;
}): string => {
if (shouldModifyScripts) {
projectPackageJson.scripts
Expand Down
Expand Up @@ -3,19 +3,11 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

type Question = {|
initial?: boolean | number,
message: string,
name: string,
type: string,
choices?: Array<{title: string, value: string}>,
|};
import {PromptObject} from 'prompts';

const defaultQuestions: Array<Question> = [
const defaultQuestions: Array<PromptObject> = [
{
choices: [
{title: 'node', value: 'node'},
Expand All @@ -42,7 +34,7 @@ const defaultQuestions: Array<Question> = [

export default defaultQuestions;

export const testScriptQuestion: Question = {
export const testScriptQuestion: PromptObject = {
initial: true,
message:
'Would you like to use Jest when running "test" script in "package.json"?',
Expand Down
13 changes: 13 additions & 0 deletions packages/jest-cli/src/init/types.ts
@@ -0,0 +1,13 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import {Config} from '@jest/types';

export type ProjectPackageJson = {
jest?: Partial<Config.GlobalConfig>;
SimenB marked this conversation as resolved.
Show resolved Hide resolved
scripts?: {[key: string]: string};
}