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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

create @jest/core package #7696

Merged
merged 3 commits into from Feb 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -42,6 +42,7 @@
- `[jest-snapshot]`: Migrate to TypeScript ([#7899](https://github.com/facebook/jest/pull/7899))
- `[@jest/transform]`: New package extracted from `jest-runtime` ([#7915](https://github.com/facebook/jest/pull/7915))
- `[babel-plugin-jest-hoist]`: Migrate to TypeScript ([#7898](https://github.com/facebook/jest/pull/7898))
- `[@jest/core]` Create new package, which is `jest-cli` minus `yargs` and `prompts` ([#7696](https://github.com/facebook/jest/pull/7696))

### Performance

Expand Down
6 changes: 3 additions & 3 deletions jest.config.js
Expand Up @@ -39,9 +39,9 @@ module.exports = {
'/packages/.*/build',
'/packages/.*/build-es5',
'/packages/.*/src/__tests__/setPrettyPrint.ts',
'/packages/jest-cli/src/__tests__/test_root',
'/packages/jest-cli/src/__tests__/__fixtures__/',
'/packages/jest-cli/src/lib/__tests__/fixtures/',
'/packages/jest-core/src/__tests__/test_root',
'/packages/jest-core/src/__tests__/__fixtures__/',
'/packages/jest-cli/src/init/__tests__/fixtures/',
'/packages/jest-haste-map/src/__tests__/haste_impl.js',
'/packages/jest-haste-map/src/__tests__/dependencyExtractor.js',
'/packages/jest-resolve-dependencies/src/__tests__/__fixtures__/',
Expand Down
34 changes: 2 additions & 32 deletions packages/jest-cli/package.json
Expand Up @@ -2,54 +2,24 @@
"name": "jest-cli",
"description": "Delightful JavaScript Testing.",
"version": "24.1.0",
"main": "build/jest.js",
"main": "build/index.js",
"dependencies": {
"@jest/reporters": "^24.1.0",
"@jest/transform": "^24.1.0",
"ansi-escapes": "^3.0.0",
"@jest/core": "^24.1.0",
"chalk": "^2.0.1",
"exit": "^0.1.2",
"glob": "^7.1.2",
"graceful-fs": "^4.1.15",
"import-local": "^2.0.0",
"is-ci": "^2.0.0",
"jest-changed-files": "^24.0.0",
"jest-config": "^24.1.0",
"jest-environment-jsdom": "^24.0.0",
"jest-get-type": "^24.0.0",
"jest-haste-map": "^24.0.0",
"jest-message-util": "^24.0.0",
"jest-regex-util": "^24.0.0",
"jest-resolve-dependencies": "^24.1.0",
"jest-runner": "^24.1.0",
"jest-runtime": "^24.1.0",
"jest-snapshot": "^24.1.0",
"jest-util": "^24.0.0",
"jest-validate": "^24.0.0",
"jest-watcher": "^24.0.0",
"micromatch": "^3.1.10",
"p-each-series": "^1.0.0",
"pirates": "^4.0.0",
"prompts": "^2.0.1",
"realpath-native": "^1.1.0",
"rimraf": "^2.5.4",
"string-length": "^2.0.0",
"strip-ansi": "^5.0.0",
"which": "^1.2.12",
"yargs": "^12.0.2"
},
"devDependencies": {
"@types/ansi-escapes": "^3.0.0",
"@types/exit": "^0.1.30",
"@types/glob": "^7.1.1",
"@types/graceful-fs": "^4.1.2",
"@types/is-ci": "^1.1.0",
"@types/micromatch": "^3.1.0",
"@types/prompts": "^1.2.0",
"@types/rimraf": "^2.0.2",
"@types/string-length": "^2.0.0",
"@types/strip-ansi": "^3.0.0",
"@types/which": "^1.3.1",
"@types/yargs": "^12.0.2"
},
"bin": {
Expand Down
279 changes: 42 additions & 237 deletions packages/jest-cli/src/cli/index.js
Expand Up @@ -12,34 +12,23 @@ import type {Argv} from 'types/Argv';
import type {GlobalConfig, Path} from 'types/Config';

import path from 'path';
import {Console, clearLine, createDirectory, preRunMessage} from 'jest-util';
import {clearLine} from 'jest-util';
import {validateCLIOptions} from 'jest-validate';
import {readConfigs, deprecationEntries} from 'jest-config';
import {deprecationEntries} from 'jest-config';
import {runCLI} from '@jest/core';
import * as args from './args';
import chalk from 'chalk';
import createContext from '../lib/create_context';
import exit from 'exit';
import getChangedFilesPromise from '../getChangedFilesPromise';
import {formatHandleErrors} from '../collectHandles';
import handleDeprecationWarnings from '../lib/handle_deprecation_warnings';
import runJest from '../runJest';
import Runtime from 'jest-runtime';
import TestWatcher from '../TestWatcher';
import watch from '../watch';
import pluralize from '../pluralize';
import yargs from 'yargs';
import rimraf from 'rimraf';
import {sync as realpath} from 'realpath-native';
import init from '../lib/init';
import logDebugMessages from '../lib/log_debug_messages';
import getVersion from '../version';
import init from '../init';

const {print: preRunMessagePrint} = preRunMessage;
import {version as VERSION} from '../../package.json';

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

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

export const runCLI = async (
argv: Argv,
projects: Array<Path>,
): Promise<{results: AggregatedResult, globalConfig: GlobalConfig}> => {
const realFs = require('fs');
const fs = require('graceful-fs');
fs.gracefulify(realFs);

let results;

// If we output a JSON object, we can't write anything to stdout, since
// it'll break the JSON structure and it won't be valid.
const outputStream =
argv.json || argv.useStderr ? process.stderr : process.stdout;

const {globalConfig, configs, hasDeprecationWarnings} = readConfigs(
argv,
projects,
);

if (argv.debug) {
logDebugMessages(globalConfig, configs, outputStream);
}

if (argv.showConfig) {
logDebugMessages(globalConfig, configs, process.stdout);
exit(0);
}

if (argv.clearCache) {
configs.forEach(config => {
rimraf.sync(config.cacheDirectory);
process.stdout.write(`Cleared ${config.cacheDirectory}\n`);
});

exit(0);
}

await _run(
globalConfig,
configs,
hasDeprecationWarnings,
outputStream,
(r: AggregatedResult) => (results = r),
);

if (argv.watch || argv.watchAll) {
// If in watch mode, return the promise that will never resolve.
// If the watch mode is interrupted, watch should handle the process
// shutdown.
return new Promise(() => {});
}

if (!results) {
throw new Error(
'AggregatedResult must be present after test run is complete',
);
}

const {openHandles} = results;

if (openHandles && openHandles.length) {
const formatted = formatHandleErrors(openHandles, configs[0]);

const openHandlesString = pluralize('open handle', formatted.length, 's');

const message =
chalk.red(
`\nJest has detected the following ${openHandlesString} potentially keeping Jest from exiting:\n\n`,
) + formatted.join('\n\n');

console.error(message);
}

return Promise.resolve({globalConfig, results});
};

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

// Only exit if needed
process.on('exit', () => {
if (typeof code === 'number' && code !== 0) {
process.exitCode = code;
}
});

if (globalConfig.forceExit) {
if (!globalConfig.detectOpenHandles) {
console.error(
chalk.red.bold('Force exiting Jest\n\n') +
chalk.red(
'Have you considered using `--detectOpenHandles` to detect ' +
'async operations that kept running after all tests finished?',
),
);
}

exit(code);
} else if (!globalConfig.detectOpenHandles) {
setTimeout(() => {
console.error(
chalk.red.bold(
'Jest did not exit one second after the test run has completed.\n\n',
) +
chalk.red(
'This usually means that there are asynchronous operations that ' +
"weren't stopped in your tests. Consider running Jest with " +
'`--detectOpenHandles` to troubleshoot this issue.',
),
);
// $FlowFixMe: `unref` exists in Node
}, 1000).unref();
}
};

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

const rawArgv: Argv | string[] = maybeArgv || process.argv.slice(2);
const argv: Argv = yargs(rawArgv)
Expand Down Expand Up @@ -234,108 +103,44 @@ const getProjectListFromCLIArgs = (argv, project: ?Path) => {
return projects;
};

const buildContextsAndHasteMaps = async (
configs,
globalConfig,
outputStream,
) => {
const hasteMapInstances = Array(configs.length);
const contexts = await Promise.all(
configs.map(async (config, index) => {
createDirectory(config.cacheDirectory);
const hasteMapInstance = Runtime.createHasteMap(config, {
console: new Console(outputStream, outputStream),
maxWorkers: globalConfig.maxWorkers,
resetCache: !config.cache,
watch: globalConfig.watch || globalConfig.watchAll,
watchman: globalConfig.watchman,
});
hasteMapInstances[index] = hasteMapInstance;
return createContext(config, await hasteMapInstance.build());
}),
);

return {contexts, hasteMapInstances};
};

const _run = async (
globalConfig,
configs,
hasDeprecationWarnings,
outputStream,
onComplete,
const readResultsAndExit = (
result: ?AggregatedResult,
globalConfig: GlobalConfig,
) => {
// Queries to hg/git can take a while, so we need to start the process
// as soon as possible, so by the time we need the result it's already there.
const changedFilesPromise = getChangedFilesPromise(globalConfig, configs);
const code = !result || result.success ? 0 : globalConfig.testFailureExitCode;

const {contexts, hasteMapInstances} = await buildContextsAndHasteMaps(
configs,
globalConfig,
outputStream,
);
// Only exit if needed
process.on('exit', () => {
if (typeof code === 'number' && code !== 0) {
process.exitCode = code;
}
});

globalConfig.watch || globalConfig.watchAll
? await runWatch(
contexts,
configs,
hasDeprecationWarnings,
globalConfig,
outputStream,
hasteMapInstances,
changedFilesPromise,
)
: await runWithoutWatch(
globalConfig,
contexts,
outputStream,
onComplete,
changedFilesPromise,
if (globalConfig.forceExit) {
if (!globalConfig.detectOpenHandles) {
console.error(
chalk.red.bold('Force exiting Jest\n\n') +
chalk.red(
'Have you considered using `--detectOpenHandles` to detect ' +
'async operations that kept running after all tests finished?',
),
);
};

const runWatch = async (
contexts,
configs,
hasDeprecationWarnings,
globalConfig,
outputStream,
hasteMapInstances,
changedFilesPromise,
) => {
if (hasDeprecationWarnings) {
try {
await handleDeprecationWarnings(outputStream, process.stdin);
return watch(globalConfig, contexts, outputStream, hasteMapInstances);
} catch (e) {
exit(0);
}
}

return watch(globalConfig, contexts, outputStream, hasteMapInstances);
};

const runWithoutWatch = async (
globalConfig,
contexts,
outputStream,
onComplete,
changedFilesPromise,
) => {
const startRun = async () => {
if (!globalConfig.listTests) {
preRunMessagePrint(outputStream);
}
return await runJest({
changedFilesPromise,
contexts,
failedTestsCache: null,
globalConfig,
onComplete,
outputStream,
startRun,
testWatcher: new TestWatcher({isWatchMode: false}),
});
};
return await startRun();
exit(code);
} else if (!globalConfig.detectOpenHandles) {
setTimeout(() => {
console.error(
chalk.red.bold(
'Jest did not exit one second after the test run has completed.\n\n',
) +
chalk.red(
'This usually means that there are asynchronous operations that ' +
"weren't stopped in your tests. Consider running Jest with " +
'`--detectOpenHandles` to troubleshoot this issue.',
),
);
// $FlowFixMe: `unref` exists in Node
}, 1000).unref();
}
};