From 25133b363989eaf2afcdb51e3c40d34712103101 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 26 Feb 2019 12:09:21 +0100 Subject: [PATCH] chore: migrate jest-config to TypeScript (#7944) --- packages/jest-config/package.json | 2 + .../src/{Defaults.js => Defaults.ts} | 11 +- .../src/{Deprecated.js => Deprecated.ts} | 14 +- .../src/{Descriptions.js => Descriptions.ts} | 10 +- ...nErrors.js => ReporterValidationErrors.ts} | 12 +- .../src/{ValidConfig.js => ValidConfig.ts} | 15 +- .../{Defaults.test.js => Defaults.test.ts} | 0 ...xWorkers.test.js => getMaxWorkers.test.ts} | 3 + ...{readConfig.test.js => readConfig.test.ts} | 1 + ...eadConfigs.test.js => readConfigs.test.ts} | 1 + ...Path.test.js => resolveConfigPath.test.ts} | 2 - ...etFromArgv.test.js => setFromArgv.test.ts} | 17 +- ...attern.test.js => validatePattern.test.ts} | 0 .../src/{constants.js => constants.ts} | 2 - ...CacheDirectory.js => getCacheDirectory.ts} | 7 +- .../{getMaxWorkers.js => getMaxWorkers.ts} | 17 +- .../jest-config/src/{index.js => index.ts} | 63 ++-- .../src/{normalize.js => normalize.ts} | 347 ++++++++++-------- ...tDir.js => readConfigFileAndSetRootDir.ts} | 9 +- ...olveConfigPath.js => resolveConfigPath.ts} | 22 +- .../src/{setFromArgv.js => setFromArgv.ts} | 13 +- .../jest-config/src/{utils.js => utils.ts} | 71 ++-- ...{validatePattern.js => validatePattern.ts} | 4 +- packages/jest-config/tsconfig.json | 17 + packages/jest-runner/src/runTest.ts | 8 +- packages/jest-runner/tsconfig.json | 1 + packages/jest-runtime/src/cli/index.ts | 3 +- packages/jest-runtime/tsconfig.json | 3 +- 28 files changed, 377 insertions(+), 298 deletions(-) rename packages/jest-config/src/{Defaults.js => Defaults.ts} (94%) rename packages/jest-config/src/{Deprecated.js => Deprecated.ts} (86%) rename packages/jest-config/src/{Descriptions.js => Descriptions.ts} (97%) rename packages/jest-config/src/{ReporterValidationErrors.js => ReporterValidationErrors.ts} (91%) rename packages/jest-config/src/{ValidConfig.js => ValidConfig.ts} (93%) rename packages/jest-config/src/__tests__/{Defaults.test.js => Defaults.test.ts} (100%) rename packages/jest-config/src/__tests__/{getMaxWorkers.test.js => getMaxWorkers.test.ts} (91%) rename packages/jest-config/src/__tests__/{readConfig.test.js => readConfig.test.ts} (95%) rename packages/jest-config/src/__tests__/{readConfigs.test.js => readConfigs.test.ts} (94%) rename packages/jest-config/src/__tests__/{resolveConfigPath.test.js => resolveConfigPath.test.ts} (99%) rename packages/jest-config/src/__tests__/{setFromArgv.test.js => setFromArgv.test.ts} (82%) rename packages/jest-config/src/__tests__/{validatePattern.test.js => validatePattern.test.ts} (100%) rename packages/jest-config/src/{constants.js => constants.ts} (97%) rename packages/jest-config/src/{getCacheDirectory.js => getCacheDirectory.ts} (91%) rename packages/jest-config/src/{getMaxWorkers.js => getMaxWorkers.ts} (60%) rename packages/jest-config/src/{index.js => index.ts} (92%) rename packages/jest-config/src/{normalize.js => normalize.ts} (75%) rename packages/jest-config/src/{readConfigFileAndSetRootDir.js => readConfigFileAndSetRootDir.ts} (90%) rename packages/jest-config/src/{resolveConfigPath.js => resolveConfigPath.ts} (87%) rename packages/jest-config/src/{setFromArgv.js => setFromArgv.ts} (86%) rename packages/jest-config/src/{utils.js => utils.ts} (77%) rename packages/jest-config/src/{validatePattern.js => validatePattern.ts} (84%) create mode 100644 packages/jest-config/tsconfig.json diff --git a/packages/jest-config/package.json b/packages/jest-config/package.json index fb97cfa3117a..a3886c4bcaf2 100644 --- a/packages/jest-config/package.json +++ b/packages/jest-config/package.json @@ -8,8 +8,10 @@ }, "license": "MIT", "main": "build/index.js", + "types": "build/index.d.ts", "dependencies": { "@babel/core": "^7.1.0", + "@jest/types": "^24.1.0", "babel-jest": "^24.1.0", "chalk": "^2.0.1", "glob": "^7.1.1", diff --git a/packages/jest-config/src/Defaults.js b/packages/jest-config/src/Defaults.ts similarity index 94% rename from packages/jest-config/src/Defaults.js rename to packages/jest-config/src/Defaults.ts index 3ef1d484ba24..b25ff9c149c0 100644 --- a/packages/jest-config/src/Defaults.js +++ b/packages/jest-config/src/Defaults.ts @@ -3,19 +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 type {DefaultOptions} from 'types/Config'; - +import {Config} from '@jest/types'; import {replacePathSepForRegex} from 'jest-regex-util'; import {NODE_MODULES} from './constants'; import getCacheDirectory from './getCacheDirectory'; const NODE_MODULES_REGEXP = replacePathSepForRegex(NODE_MODULES); -export default ({ +const defaultOptions: Config.DefaultOptions = { automock: false, bail: 0, browser: false, @@ -83,4 +80,6 @@ export default ({ watch: false, watchPathIgnorePatterns: [], watchman: true, -}: DefaultOptions); +}; + +export default defaultOptions; diff --git a/packages/jest-config/src/Deprecated.js b/packages/jest-config/src/Deprecated.ts similarity index 86% rename from packages/jest-config/src/Deprecated.js rename to packages/jest-config/src/Deprecated.ts index cc14996e5ad6..8ad752842d2a 100644 --- a/packages/jest-config/src/Deprecated.js +++ b/packages/jest-config/src/Deprecated.ts @@ -3,14 +3,12 @@ * * 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 prettyFormat from 'pretty-format'; -const format = (value: mixed) => prettyFormat(value, {min: true}); +const format = (value: unknown) => prettyFormat(value, {min: true}); export default { mapCoverage: () => ` Option ${chalk.bold( @@ -20,7 +18,7 @@ export default { Please update your configuration.`, preprocessorIgnorePatterns: (options: { - preprocessorIgnorePatterns: Array, + preprocessorIgnorePatterns: Array; }) => ` Option ${chalk.bold( '"preprocessorIgnorePatterns"', )} was replaced by ${chalk.bold( @@ -37,7 +35,7 @@ export default { Please update your configuration.`, scriptPreprocessor: (options: { - scriptPreprocessor: string, + scriptPreprocessor: string; }) => ` Option ${chalk.bold( '"scriptPreprocessor"', )} was replaced by ${chalk.bold( @@ -53,8 +51,8 @@ export default { Please update your configuration.`, - setupTestFrameworkScriptFile: (options: { - setupTestFrameworkScriptFile: Array, + setupTestFrameworkScriptFile: (_options: { + setupTestFrameworkScriptFile: Array; }) => ` Option ${chalk.bold( '"setupTestFrameworkScriptFile"', )} was replaced by configuration ${chalk.bold( @@ -64,7 +62,7 @@ export default { Please update your configuration.`, testPathDirs: (options: { - testPathDirs: Array, + testPathDirs: Array; }) => ` Option ${chalk.bold('"testPathDirs"')} was replaced by ${chalk.bold( '"roots"', )}. diff --git a/packages/jest-config/src/Descriptions.js b/packages/jest-config/src/Descriptions.ts similarity index 97% rename from packages/jest-config/src/Descriptions.js rename to packages/jest-config/src/Descriptions.ts index 44c4e4d6c116..e9088b6849e3 100644 --- a/packages/jest-config/src/Descriptions.js +++ b/packages/jest-config/src/Descriptions.ts @@ -3,11 +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 */ -export default ({ +import {Config} from '@jest/types'; + +const descriptions: {[key in keyof Config.InitialOptions]: string} = { automock: 'All imported modules in your tests should be mocked automatically', bail: 'Stop running tests after `n` failures', browser: 'Respect "browser" field in package.json when resolving modules', @@ -91,4 +91,6 @@ export default ({ watchPathIgnorePatterns: 'An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode', watchman: 'Whether to use watchman for file crawling', -}: {[string]: string}); +}; + +export default descriptions; diff --git a/packages/jest-config/src/ReporterValidationErrors.js b/packages/jest-config/src/ReporterValidationErrors.ts similarity index 91% rename from packages/jest-config/src/ReporterValidationErrors.js rename to packages/jest-config/src/ReporterValidationErrors.ts index 144b5f4ea950..0ded7dc3ef29 100644 --- a/packages/jest-config/src/ReporterValidationErrors.js +++ b/packages/jest-config/src/ReporterValidationErrors.ts @@ -6,8 +6,8 @@ * @flow */ -import type {ReporterConfig} from 'types/Config'; - +import {Config} from '@jest/types'; +// @ts-ignore: Not migrated to TS import {ValidationError} from 'jest-validate'; import chalk from 'chalk'; import getType from 'jest-get-type'; @@ -26,7 +26,7 @@ const ERROR = `${BULLET}Reporter Validation Error`; */ export function createReporterError( reporterIndex: number, - reporterValue: Array | string, + reporterValue: Array | string, ): ValidationError { const errorMessage = ` Reporter at index ${reporterIndex} must be of type:\n` + @@ -38,7 +38,7 @@ export function createReporterError( } export function createArrayReporterError( - arrayReporter: ReporterConfig, + arrayReporter: Config.ReporterConfig, reporterIndex: number, valueIndex: number, value: string | Object, @@ -63,7 +63,7 @@ export function createArrayReporterError( } export function validateReporters( - reporterConfig: Array, + reporterConfig: Array, ): boolean { return reporterConfig.every((reporter, index) => { if (Array.isArray(reporter)) { @@ -77,7 +77,7 @@ export function validateReporters( } function validateArrayReporter( - arrayReporter: ReporterConfig, + arrayReporter: Config.ReporterConfig, reporterIndex: number, ) { const [path, options] = arrayReporter; diff --git a/packages/jest-config/src/ValidConfig.js b/packages/jest-config/src/ValidConfig.ts similarity index 93% rename from packages/jest-config/src/ValidConfig.js rename to packages/jest-config/src/ValidConfig.ts index 883ebb0b8d56..eb1019e57ec9 100644 --- a/packages/jest-config/src/ValidConfig.js +++ b/packages/jest-config/src/ValidConfig.ts @@ -3,21 +3,19 @@ * * 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 {InitialOptions} from 'types/Config'; - +import {Config} from '@jest/types'; import {replacePathSepForRegex} from 'jest-regex-util'; +// @ts-ignore: Not migrated to TS import {multipleValidOptions} from 'jest-validate'; import {NODE_MODULES} from './constants'; const NODE_MODULES_REGEXP = replacePathSepForRegex(NODE_MODULES); -export default ({ +const initialOptions: Config.InitialOptions = { automock: false, - bail: (multipleValidOptions(false, 0): any), + bail: multipleValidOptions(false, 0), browser: false, cache: true, cacheDirectory: '/tmp/user/jest', @@ -40,6 +38,7 @@ export default ({ statements: 100, }, }, + // @ts-ignore: Missing from initial options... https://github.com/facebook/jest/pull/7923 cwd: '/root', dependencyExtractor: '/dependencyExtractor.js', displayName: 'project-name', @@ -135,4 +134,6 @@ export default ({ ], ], watchman: true, -}: InitialOptions); +}; + +export default initialOptions; diff --git a/packages/jest-config/src/__tests__/Defaults.test.js b/packages/jest-config/src/__tests__/Defaults.test.ts similarity index 100% rename from packages/jest-config/src/__tests__/Defaults.test.js rename to packages/jest-config/src/__tests__/Defaults.test.ts diff --git a/packages/jest-config/src/__tests__/getMaxWorkers.test.js b/packages/jest-config/src/__tests__/getMaxWorkers.test.ts similarity index 91% rename from packages/jest-config/src/__tests__/getMaxWorkers.test.js rename to packages/jest-config/src/__tests__/getMaxWorkers.test.ts index 5ce2cf846dd1..aab3290c5357 100644 --- a/packages/jest-config/src/__tests__/getMaxWorkers.test.js +++ b/packages/jest-config/src/__tests__/getMaxWorkers.test.ts @@ -38,16 +38,19 @@ describe('getMaxWorkers', () => { describe('% based', () => { it('50% = 2 workers', () => { const argv = {maxWorkers: '50%'}; + // @ts-ignore: need to fix the typing expect(getMaxWorkers(argv)).toBe(2); }); it('< 0 workers should become 1', () => { const argv = {maxWorkers: '1%'}; + // @ts-ignore: need to fix the typing expect(getMaxWorkers(argv)).toBe(1); }); it("0% shouldn't break", () => { const argv = {maxWorkers: '0%'}; + // @ts-ignore: need to fix the typing expect(getMaxWorkers(argv)).toBe(1); }); }); diff --git a/packages/jest-config/src/__tests__/readConfig.test.js b/packages/jest-config/src/__tests__/readConfig.test.ts similarity index 95% rename from packages/jest-config/src/__tests__/readConfig.test.js rename to packages/jest-config/src/__tests__/readConfig.test.ts index f3914aa8b89c..58ff3551430b 100644 --- a/packages/jest-config/src/__tests__/readConfig.test.js +++ b/packages/jest-config/src/__tests__/readConfig.test.ts @@ -5,6 +5,7 @@ import {readConfig} from '../index'; test('readConfig() throws when an object is passed without a file path', () => { expect(() => { readConfig( + // @ts-ignore null /* argv */, {} /* packageRootOrConfig */, false /* skipArgvConfigOption */, diff --git a/packages/jest-config/src/__tests__/readConfigs.test.js b/packages/jest-config/src/__tests__/readConfigs.test.ts similarity index 94% rename from packages/jest-config/src/__tests__/readConfigs.test.js rename to packages/jest-config/src/__tests__/readConfigs.test.ts index 0e114e97b3c7..e08204487c07 100644 --- a/packages/jest-config/src/__tests__/readConfigs.test.js +++ b/packages/jest-config/src/__tests__/readConfigs.test.ts @@ -4,6 +4,7 @@ import {readConfigs} from '../index'; test('readConfigs() throws when called without project paths', () => { expect(() => { + // @ts-ignore readConfigs(null /* argv */, [] /* projectPaths */); }).toThrowError('jest: No configuration found for any project.'); }); diff --git a/packages/jest-config/src/__tests__/resolveConfigPath.test.js b/packages/jest-config/src/__tests__/resolveConfigPath.test.ts similarity index 99% rename from packages/jest-config/src/__tests__/resolveConfigPath.test.js rename to packages/jest-config/src/__tests__/resolveConfigPath.test.ts index 5000d5e58ee8..713c38def193 100644 --- a/packages/jest-config/src/__tests__/resolveConfigPath.test.js +++ b/packages/jest-config/src/__tests__/resolveConfigPath.test.ts @@ -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 */ import os from 'os'; diff --git a/packages/jest-config/src/__tests__/setFromArgv.test.js b/packages/jest-config/src/__tests__/setFromArgv.test.ts similarity index 82% rename from packages/jest-config/src/__tests__/setFromArgv.test.js rename to packages/jest-config/src/__tests__/setFromArgv.test.ts index 182ab5bb570a..8216b0e2c35c 100644 --- a/packages/jest-config/src/__tests__/setFromArgv.test.js +++ b/packages/jest-config/src/__tests__/setFromArgv.test.ts @@ -6,16 +6,17 @@ * */ +import {Config} from '@jest/types'; import setFromArgv from '../setFromArgv'; test('maps special values to valid options', () => { - const options = {}; + const options = {} as Config.InitialOptions; const argv = { coverage: true, env: 'node', json: true, watchAll: true, - }; + } as Config.Argv; expect(setFromArgv(options, argv)).toMatchObject({ collectCoverage: true, @@ -27,12 +28,12 @@ test('maps special values to valid options', () => { }); test('maps regular values to themselves', () => { - const options = {}; + const options = {} as Config.InitialOptions; const argv = { collectCoverageOnlyFrom: ['a', 'b'], coverageDirectory: 'covDir', watchman: true, - }; + } as Config.Argv; expect(setFromArgv(options, argv)).toMatchObject({ collectCoverageOnlyFrom: ['a', 'b'], @@ -42,11 +43,11 @@ test('maps regular values to themselves', () => { }); test('works with string objects', () => { - const options = {}; + const options = {} as Config.InitialOptions; const argv = { moduleNameMapper: '{"types/(.*)": "/src/types/$1"}', transform: '{"*.js": "/transformer"}', - }; + } as Config.Argv; expect(setFromArgv(options, argv)).toMatchObject({ moduleNameMapper: { 'types/(.*)': '/src/types/$1', @@ -58,10 +59,10 @@ test('works with string objects', () => { }); test('explicit flags override those from --config', () => { - const options = {}; + const options = {} as Config.InitialOptions; const argv = { config: '{"watch": false}', watch: true, - }; + } as Config.Argv; expect(setFromArgv(options, argv)).toMatchObject({watch: true}); }); diff --git a/packages/jest-config/src/__tests__/validatePattern.test.js b/packages/jest-config/src/__tests__/validatePattern.test.ts similarity index 100% rename from packages/jest-config/src/__tests__/validatePattern.test.js rename to packages/jest-config/src/__tests__/validatePattern.test.ts diff --git a/packages/jest-config/src/constants.js b/packages/jest-config/src/constants.ts similarity index 97% rename from packages/jest-config/src/constants.js rename to packages/jest-config/src/constants.ts index 3877e91b3177..90f055b87320 100644 --- a/packages/jest-config/src/constants.js +++ b/packages/jest-config/src/constants.ts @@ -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 */ import path from 'path'; diff --git a/packages/jest-config/src/getCacheDirectory.js b/packages/jest-config/src/getCacheDirectory.ts similarity index 91% rename from packages/jest-config/src/getCacheDirectory.js rename to packages/jest-config/src/getCacheDirectory.ts index 587d9c21fdb6..d9a4ca266460 100644 --- a/packages/jest-config/src/getCacheDirectory.js +++ b/packages/jest-config/src/getCacheDirectory.ts @@ -3,13 +3,10 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -const path = require('path'); -const os = require('os'); - +import path from 'path'; +import os from 'os'; import {sync as realpath} from 'realpath-native'; const getCacheDirectory = () => { diff --git a/packages/jest-config/src/getMaxWorkers.js b/packages/jest-config/src/getMaxWorkers.ts similarity index 60% rename from packages/jest-config/src/getMaxWorkers.js rename to packages/jest-config/src/getMaxWorkers.ts index b8419dfcbc7a..29e350b7a367 100644 --- a/packages/jest-config/src/getMaxWorkers.js +++ b/packages/jest-config/src/getMaxWorkers.ts @@ -3,23 +3,24 @@ * * 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 os from 'os'; +import {Config} from '@jest/types'; -export default function getMaxWorkers(argv: Argv): number { +export default function getMaxWorkers( + argv: Partial>, +): number { if (argv.runInBand) { return 1; } else if (argv.maxWorkers) { - const parsed = parseInt(argv.maxWorkers, 10); + // TODO: How to type this properly? Should probably use `coerce` from `yargs` + const maxWorkers = (argv.maxWorkers as unknown) as number | string; + const parsed = parseInt(maxWorkers as string, 10); if ( - typeof argv.maxWorkers === 'string' && - argv.maxWorkers.trim().endsWith('%') && + typeof maxWorkers === 'string' && + maxWorkers.trim().endsWith('%') && parsed > 0 && parsed <= 100 ) { diff --git a/packages/jest-config/src/index.js b/packages/jest-config/src/index.ts similarity index 92% rename from packages/jest-config/src/index.js rename to packages/jest-config/src/index.ts index 81d6970908c5..45bebcafcdac 100644 --- a/packages/jest-config/src/index.js +++ b/packages/jest-config/src/index.ts @@ -3,26 +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 type {Argv} from 'types/Argv'; -import type { - GlobalConfig, - InitialOptions, - Path, - ProjectConfig, -} from 'types/Config'; - -import chalk from 'chalk'; import fs from 'fs'; import path from 'path'; +import {Config} from '@jest/types'; +import chalk from 'chalk'; import {isJSONString, replaceRootDirInPath} from './utils'; import normalize from './normalize'; import resolveConfigPath from './resolveConfigPath'; import readConfigFileAndSetRootDir from './readConfigFileAndSetRootDir'; - export {getTestEnvironment, isJSONString} from './utils'; export {default as normalize} from './normalize'; export {default as deprecationEntries} from './Deprecated'; @@ -30,22 +20,24 @@ export {replaceRootDirInPath} from './utils'; export {default as defaults} from './Defaults'; export {default as descriptions} from './Descriptions'; +type ReadConfig = { + configPath: Config.Path | null | undefined; + globalConfig: Config.GlobalConfig; + hasDeprecationWarnings: boolean; + projectConfig: Config.ProjectConfig; +}; + export function readConfig( - argv: Argv, - packageRootOrConfig: Path | InitialOptions, + argv: Config.Argv, + packageRootOrConfig: Config.Path | Config.InitialOptions, // Whether it needs to look into `--config` arg passed to CLI. // It only used to read initial config. If the initial config contains // `project` property, we don't want to read `--config` value and rather // read individual configs for every project. skipArgvConfigOption?: boolean, - parentConfigPath: ?Path, - projectIndex?: number = Infinity, -): { - configPath: ?Path, - globalConfig: GlobalConfig, - hasDeprecationWarnings: boolean, - projectConfig: ProjectConfig, -} { + parentConfigPath?: Config.Path | null, + projectIndex: number = Infinity, +): ReadConfig { let rawOptions; let configPath = null; @@ -104,8 +96,11 @@ export function readConfig( } const groupOptions = ( - options: Object, -): {globalConfig: GlobalConfig, projectConfig: ProjectConfig} => ({ + options: Config.ProjectConfig & Config.GlobalConfig, +): { + globalConfig: Config.GlobalConfig; + projectConfig: Config.ProjectConfig; +} => ({ globalConfig: Object.freeze({ bail: options.bail, changedFilesWithAncestor: options.changedFilesWithAncestor, @@ -218,11 +213,15 @@ const groupOptions = ( }), }); -const ensureNoDuplicateConfigs = (parsedConfigs, projects) => { +const ensureNoDuplicateConfigs = ( + parsedConfigs: Array, + projects: Config.GlobalConfig['projects'], +) => { const configPathMap = new Map(); for (const config of parsedConfigs) { const {configPath} = config; + if (configPathMap.has(configPath)) { const message = `Whoops! Two projects resolved to the same config path: ${chalk.bold( String(configPath), @@ -256,18 +255,18 @@ This usually means that your ${chalk.bold( // If no projects are specified, process.cwd() will be used as the default // (and only) project. export function readConfigs( - argv: Argv, - projectPaths: Array, + argv: Config.Argv, + projectPaths: Array, ): { - globalConfig: GlobalConfig, - configs: Array, - hasDeprecationWarnings: boolean, + globalConfig: Config.GlobalConfig; + configs: Array; + hasDeprecationWarnings: boolean; } { let globalConfig; let hasDeprecationWarnings; - let configs: Array = []; + let configs: Array = []; let projects = projectPaths; - let configPath: ?Path; + let configPath: Config.Path | null | undefined; if (projectPaths.length === 1) { const parsedConfig = readConfig(argv, projects[0]); diff --git a/packages/jest-config/src/normalize.js b/packages/jest-config/src/normalize.ts similarity index 75% rename from packages/jest-config/src/normalize.js rename to packages/jest-config/src/normalize.ts index f938d520e03d..ed8940cc3d57 100644 --- a/packages/jest-config/src/normalize.js +++ b/packages/jest-config/src/normalize.ts @@ -3,32 +3,22 @@ * * 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 type { - InitialOptions, - DefaultOptions, - ReporterConfig, - GlobalConfig, - Path, - ProjectConfig, -} from 'types/Config'; - import crypto from 'crypto'; -import glob from 'glob'; import path from 'path'; +import glob from 'glob'; +import {Config} from '@jest/types'; +// @ts-ignore: Not migrated to TS import {ValidationError, validate} from 'jest-validate'; -import validatePattern from './validatePattern'; import {clearLine, replacePathSepForGlob} from 'jest-util'; import chalk from 'chalk'; -import getMaxWorkers from './getMaxWorkers'; import micromatch from 'micromatch'; import {sync as realpath} from 'realpath-native'; import Resolver from 'jest-resolve'; import {replacePathSepForRegex} from 'jest-regex-util'; +import validatePattern from './validatePattern'; +import getMaxWorkers from './getMaxWorkers'; import { BULLET, DOCUMENTATION_NOTE, @@ -46,18 +36,22 @@ import DEFAULT_CONFIG from './Defaults'; import DEPRECATED_CONFIG from './Deprecated'; import setFromArgv from './setFromArgv'; import VALID_CONFIG from './ValidConfig'; - const ERROR = `${BULLET}Validation Error`; const PRESET_EXTENSIONS = ['.json', '.js']; const PRESET_NAME = 'jest-preset'; -const createConfigError = message => +type AllOptions = Config.ProjectConfig & Config.GlobalConfig; + +const createConfigError = (message: string) => new ValidationError(ERROR, message, DOCUMENTATION_NOTE); const mergeOptionWithPreset = ( - options: InitialOptions, - preset: InitialOptions, - optionName: string, + options: Config.InitialOptions, + preset: Config.InitialOptions, + optionName: keyof Pick< + Config.InitialOptions, + 'moduleNameMapper' | 'transform' + >, ) => { if (options[optionName] && preset[optionName]) { options[optionName] = { @@ -69,10 +63,10 @@ const mergeOptionWithPreset = ( }; const setupPreset = ( - options: InitialOptions, + options: Config.InitialOptions, optionsPreset: string, -): InitialOptions => { - let preset; +): Config.InitialOptions => { + let preset: Config.InitialOptions; const presetPath = replaceRootDirInPath(options.rootDir, optionsPreset); const presetModule = Resolver.findNodeModule( presetPath.startsWith('.') @@ -92,8 +86,8 @@ const setupPreset = ( } } catch (e) {} - // $FlowFixMe - preset = (require(presetModule): InitialOptions); + // @ts-ignore: `presetModule` can be null? + preset = require(presetModule); } catch (error) { if (error instanceof SyntaxError || error instanceof TypeError) { throw createConfigError( @@ -132,7 +126,7 @@ const setupPreset = ( return {...preset, ...options}; }; -const setupBabelJest = (options: InitialOptions) => { +const setupBabelJest = (options: Config.InitialOptions) => { const transform = options.transform; let babelJest; if (transform) { @@ -175,12 +169,16 @@ const setupBabelJest = (options: InitialOptions) => { }; const normalizeCollectCoverageOnlyFrom = ( - options: InitialOptions, - key: string, + options: Config.InitialOptions & + Required>, + key: keyof Pick, ) => { - const collectCoverageOnlyFrom = Array.isArray(options[key]) - ? options[key] // passed from argv - : Object.keys(options[key]); // passed from options + const initialCollectCoverageFrom = options[key]; + const collectCoverageOnlyFrom: Config.Glob[] = Array.isArray( + initialCollectCoverageFrom, + ) + ? initialCollectCoverageFrom // passed from argv + : Object.keys(initialCollectCoverageFrom); // passed from options return collectCoverageOnlyFrom.reduce((map, filePath) => { filePath = path.resolve( options.rootDir, @@ -191,22 +189,27 @@ const normalizeCollectCoverageOnlyFrom = ( }, Object.create(null)); }; -const normalizeCollectCoverageFrom = (options: InitialOptions, key: string) => { - let value; - if (!options[key]) { +const normalizeCollectCoverageFrom = ( + options: Config.InitialOptions & + Required>, + key: keyof Pick, +) => { + const initialCollectCoverageFrom = options[key]; + let value: Config.Glob[] | undefined; + if (!initialCollectCoverageFrom) { value = []; } - if (!Array.isArray(options[key])) { + if (!Array.isArray(initialCollectCoverageFrom)) { try { - value = JSON.parse(options[key]); + value = JSON.parse(initialCollectCoverageFrom); } catch (e) {} if (options[key] && !Array.isArray(value)) { - value = [options[key]]; + value = [initialCollectCoverageFrom]; } } else { - value = options[key]; + value = initialCollectCoverageFrom; } if (value) { @@ -219,8 +222,16 @@ const normalizeCollectCoverageFrom = (options: InitialOptions, key: string) => { }; const normalizeUnmockedModulePathPatterns = ( - options: InitialOptions, - key: string, + options: Config.InitialOptions, + key: keyof Pick< + Config.InitialOptions, + | 'coveragePathIgnorePatterns' + | 'modulePathIgnorePatterns' + | 'testPathIgnorePatterns' + | 'transformIgnorePatterns' + | 'watchPathIgnorePatterns' + | 'unmockedModulePathPatterns' + >, ) => // _replaceRootDirTags is specifically well-suited for substituting // in paths (it deals with properly interpreting relative path @@ -228,11 +239,13 @@ const normalizeUnmockedModulePathPatterns = ( // // For patterns, direct global substitution is far more ideal, so we // special case substitutions for patterns here. - options[key].map(pattern => + options[key]!.map(pattern => replacePathSepForRegex(pattern.replace(//g, options.rootDir)), ); -const normalizePreprocessor = (options: InitialOptions): InitialOptions => { +const normalizePreprocessor = ( + options: Config.InitialOptions, +): Config.InitialOptions => { if (options.scriptPreprocessor && options.transform) { throw createConfigError( ` Options: ${chalk.bold('scriptPreprocessor')} and ${chalk.bold( @@ -269,10 +282,10 @@ const normalizePreprocessor = (options: InitialOptions): InitialOptions => { }; const normalizeMissingOptions = ( - options: InitialOptions, - configPath: ?Path, + options: Config.InitialOptions, + configPath: Config.Path | null | undefined, projectIndex: number, -): InitialOptions => { +): Config.InitialOptions => { if (!options.name) { options.name = crypto .createHash('md5') @@ -290,7 +303,9 @@ const normalizeMissingOptions = ( return options; }; -const normalizeRootDir = (options: InitialOptions): InitialOptions => { +const normalizeRootDir = ( + options: Config.InitialOptions, +): Config.InitialOptions => { // Assert that there *is* a rootDir if (!options.hasOwnProperty('rootDir')) { throw createConfigError( @@ -309,7 +324,7 @@ const normalizeRootDir = (options: InitialOptions): InitialOptions => { return options; }; -const normalizeReporters = (options: InitialOptions, basedir) => { +const normalizeReporters = (options: Config.InitialOptions) => { const reporters = options.reporters; if (!reporters || !Array.isArray(reporters)) { return options; @@ -317,7 +332,7 @@ const normalizeReporters = (options: InitialOptions, basedir) => { validateReporters(reporters); options.reporters = reporters.map(reporterConfig => { - const normalizedReporterConfig: ReporterConfig = + const normalizedReporterConfig: Config.ReporterConfig = typeof reporterConfig === 'string' ? // if reporter config is a string, we wrap it in an array // and pass an empty object for options argument, to normalize @@ -348,7 +363,7 @@ const normalizeReporters = (options: InitialOptions, basedir) => { return options; }; -const buildTestPathPattern = (argv: Argv): string => { +const buildTestPathPattern = (argv: Config.Argv): string => { const patterns = []; if (argv._) { @@ -386,11 +401,14 @@ const showTestPathPatternError = (testPathPattern: string) => { }; export default function normalize( - options: InitialOptions, - argv: Argv, - configPath: ?Path, - projectIndex?: number = Infinity, -) { + options: Config.InitialOptions, + argv: Config.Argv, + configPath?: Config.Path | null, + projectIndex: number = Infinity, +): { + hasDeprecationWarnings: boolean; + options: AllOptions; +} { const {hasDeprecationWarnings} = validate(options, { comment: DOCUMENTATION_NOTE, deprecatedConfig: DEPRECATED_CONFIG, @@ -429,14 +447,12 @@ export default function normalize( options.setupTestFrameworkScriptFile && options.setupFilesAfterEnv.length > 0 ) { - throw createConfigError( - ` Options: ${chalk.bold( - 'setupTestFrameworkScriptFile', - )} and ${chalk.bold('setupFilesAfterEnv')} cannot be used together. + throw createConfigError(` Options: ${chalk.bold( + 'setupTestFrameworkScriptFile', + )} and ${chalk.bold('setupFilesAfterEnv')} cannot be used together. Please change your configuration to only use ${chalk.bold( 'setupFilesAfterEnv', - )}.`, - ); + )}.`); } if (options.setupTestFrameworkScriptFile) { @@ -452,6 +468,7 @@ export default function normalize( options.roots = options.testPathDirs; delete options.testPathDirs; } + if (!options.roots) { options.roots = [options.rootDir]; } @@ -465,10 +482,10 @@ export default function normalize( } setupBabelJest(options); - - const newOptions: $Shape = { + // TODO: Type this properly + const newOptions = ({ ...DEFAULT_CONFIG, - }; + } as unknown) as AllOptions; try { // try to resolve windows short paths, ignoring errors (permission errors, mostly) @@ -485,51 +502,68 @@ export default function normalize( }); } - Object.keys(options).reduce((newOptions, key: $Keys) => { + const optionKeys = Object.keys(options) as Array; + + optionKeys.reduce((newOptions, key: keyof Config.InitialOptions) => { // The resolver has been resolved separately; skip it if (key === 'resolver') { return newOptions; } + + // This is cheating, because it claims that all keys of InitialOptions are Required. + // We only really know it's Required for oldOptions[key], not for oldOptions.someOtherKey, + // so oldOptions[key] is the only way it should be used. + const oldOptions = options as Config.InitialOptions & + Required>; let value; switch (key) { case 'collectCoverageOnlyFrom': - value = normalizeCollectCoverageOnlyFrom(options, key); + value = normalizeCollectCoverageOnlyFrom(oldOptions, key); break; case 'setupFiles': case 'setupFilesAfterEnv': case 'snapshotSerializers': - value = - options[key] && - options[key].map(filePath => - resolve(newOptions.resolver, { - filePath, - key, - rootDir: options.rootDir, - }), - ); + { + const option = oldOptions[key]; + value = + option && + option.map(filePath => + resolve(newOptions.resolver, { + filePath, + key, + rootDir: options.rootDir, + }), + ); + } break; case 'modulePaths': case 'roots': - value = - options[key] && - options[key].map(filePath => - path.resolve( - options.rootDir, - replaceRootDirInPath(options.rootDir, filePath), - ), - ); + { + const option = oldOptions[key]; + value = + option && + option.map(filePath => + path.resolve( + options.rootDir, + replaceRootDirInPath(options.rootDir, filePath), + ), + ); + } break; case 'collectCoverageFrom': - value = normalizeCollectCoverageFrom(options, key); + value = normalizeCollectCoverageFrom(oldOptions, key); break; case 'cacheDirectory': case 'coverageDirectory': - value = - options[key] && - path.resolve( - options.rootDir, - replaceRootDirInPath(options.rootDir, options[key]), - ); + { + const option = oldOptions[key]; + value = + option && + path.resolve( + options.rootDir, + replaceRootDirInPath(options.rootDir, option), + ); + } break; case 'dependencyExtractor': case 'globalSetup': @@ -539,37 +573,48 @@ export default function normalize( case 'testResultsProcessor': case 'testRunner': case 'filter': - value = - options[key] && - resolve(newOptions.resolver, { - filePath: options[key], - key, - rootDir: options.rootDir, - }); + { + const option = oldOptions[key]; + value = + option && + resolve(newOptions.resolver, { + filePath: option, + key, + rootDir: options.rootDir, + }); + } break; case 'runner': - value = - options[key] && - getRunner(newOptions.resolver, { - filePath: options[key], - rootDir: options.rootDir, - }); + { + const option = oldOptions[key]; + value = + option && + getRunner(newOptions.resolver, { + filePath: option, + rootDir: options.rootDir, + }); + } break; case 'prettierPath': - // We only want this to throw if "prettierPath" is explicitly passed - // from config or CLI, and the requested path isn't found. Otherwise we - // set it to null and throw an error lazily when it is used. - value = - options[key] && - resolve(newOptions.resolver, { - filePath: options[key], - key, - optional: options[key] === DEFAULT_CONFIG[key], - rootDir: options.rootDir, - }); + { + // We only want this to throw if "prettierPath" is explicitly passed + // from config or CLI, and the requested path isn't found. Otherwise we + // set it to null and throw an error lazily when it is used. + + const option = oldOptions[key]; + + value = + option && + resolve(newOptions.resolver, { + filePath: option, + key, + optional: option === DEFAULT_CONFIG[key], + rootDir: options.rootDir, + }); + } break; case 'moduleNameMapper': - const moduleNameMapper = options[key]; + const moduleNameMapper = oldOptions[key]; value = moduleNameMapper && Object.keys(moduleNameMapper).map(regex => { @@ -578,7 +623,7 @@ export default function normalize( }); break; case 'transform': - const transform = options[key]; + const transform = oldOptions[key]; value = transform && Object.keys(transform).map(regex => [ @@ -596,12 +641,12 @@ export default function normalize( case 'transformIgnorePatterns': case 'watchPathIgnorePatterns': case 'unmockedModulePathPatterns': - value = normalizeUnmockedModulePathPatterns(options, key); + value = normalizeUnmockedModulePathPatterns(oldOptions, key); break; case 'haste': - value = {...options[key]}; + value = {...oldOptions[key]}; if (value.hasteImplModulePath != null) { - value.hasteImplModulePath = resolve(newOptions.resolver, { + const resolvedHasteImpl = resolve(newOptions.resolver, { filePath: replaceRootDirInPath( options.rootDir, value.hasteImplModulePath, @@ -609,10 +654,12 @@ export default function normalize( key: 'haste.hasteImplModulePath', rootDir: options.rootDir, }); + + value.hasteImplModulePath = resolvedHasteImpl || undefined; } break; case 'projects': - value = (options[key] || []) + value = (oldOptions[key] || []) .map(project => typeof project === 'string' ? _replaceRootDirTags(options.rootDir, project) @@ -632,7 +679,7 @@ export default function normalize( { const replacedRootDirTags = _replaceRootDirTags( escapeGlobCharacters(options.rootDir), - options[key], + oldOptions[key], ); if (replacedRootDirTags) { @@ -645,12 +692,17 @@ export default function normalize( } break; case 'testRegex': - value = options[key] - ? [].concat(options[key]).map(replacePathSepForRegex) - : []; + { + const option = oldOptions[key]; + value = option + ? (Array.isArray(option) ? option : [option]).map( + replacePathSepForRegex, + ) + : []; + } break; case 'moduleFileExtensions': { - value = options[key]; + value = oldOptions[key]; if ( Array.isArray(value) && // If it's the wrong type, it can throw at a later time @@ -680,16 +732,17 @@ export default function normalize( break; } case 'bail': { - if (typeof options[key] === 'boolean') { - value = options[key] ? 1 : 0; - } else if (typeof options[key] === 'string') { + const bail = oldOptions[key]; + if (typeof bail === 'boolean') { + value = bail ? 1 : 0; + } else if (typeof bail === 'string') { value = 1; // If Jest is invoked as `jest --bail someTestPattern` then need to // move the pattern from the `bail` configuration and into `argv._` // to be processed as an extra parameter - argv._.push(options[key]); + argv._.push(bail); } else { - value = options[key]; + value = oldOptions[key]; } break; } @@ -746,10 +799,10 @@ export default function normalize( case 'watch': case 'watchAll': case 'watchman': - value = options[key]; + value = oldOptions[key]; break; case 'watchPlugins': - value = (options[key] || []).map(watchPlugin => { + value = (oldOptions[key] || []).map(watchPlugin => { if (typeof watchPlugin === 'string') { return { config: {}, @@ -770,7 +823,7 @@ export default function normalize( }); break; } - // $FlowFixMe - automock is missing in GlobalConfig, so what + // @ts-ignore: automock is missing in GlobalConfig, so what newOptions[key] = value; return newOptions; }, newOptions); @@ -779,16 +832,17 @@ export default function normalize( newOptions.testPathPattern = buildTestPathPattern(argv); newOptions.json = argv.json; - newOptions.testFailureExitCode = parseInt(newOptions.testFailureExitCode, 10); + newOptions.testFailureExitCode = parseInt( + (newOptions.testFailureExitCode as unknown) as string, + 10, + ); - for (const key of [ - 'lastCommit', - 'changedFilesWithAncestor', - 'changedSince', - ]) { - if (newOptions[key]) { - newOptions.onlyChanged = true; - } + if ( + newOptions.lastCommit || + newOptions.changedFilesWithAncestor || + newOptions.changedSince + ) { + newOptions.onlyChanged = true; } if (argv.all) { @@ -806,17 +860,20 @@ export default function normalize( ? 'all' : 'new'; - newOptions.maxConcurrency = parseInt(newOptions.maxConcurrency, 10); + newOptions.maxConcurrency = parseInt( + (newOptions.maxConcurrency as unknown) as string, + 10, + ); newOptions.maxWorkers = getMaxWorkers(argv); - if (newOptions.testRegex.length && options.testMatch) { + if (newOptions.testRegex!.length && options.testMatch) { throw createConfigError( ` Configuration options ${chalk.bold('testMatch')} and` + ` ${chalk.bold('testRegex')} cannot be used together.`, ); } - if (newOptions.testRegex.length && !options.testMatch) { + if (newOptions.testRegex!.length && !options.testMatch) { // Prevent the default testMatch conflicting with any explicitly // configured `testRegex` value newOptions.testMatch = []; @@ -849,7 +906,7 @@ export default function normalize( if ( !micromatch.some( replacePathSepForGlob(path.relative(options.rootDir, filename)), - newOptions.collectCoverageFrom, + newOptions.collectCoverageFrom!, ) ) { return patterns; diff --git a/packages/jest-config/src/readConfigFileAndSetRootDir.js b/packages/jest-config/src/readConfigFileAndSetRootDir.ts similarity index 90% rename from packages/jest-config/src/readConfigFileAndSetRootDir.js rename to packages/jest-config/src/readConfigFileAndSetRootDir.ts index a4a23f5eb02c..497a238a9281 100644 --- a/packages/jest-config/src/readConfigFileAndSetRootDir.js +++ b/packages/jest-config/src/readConfigFileAndSetRootDir.ts @@ -3,26 +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 */ -import type {InitialOptions, Path} from 'types/Config'; - import path from 'path'; import fs from 'fs'; +import {Config} from '@jest/types'; +// @ts-ignore: vendored import jsonlint from './vendor/jsonlint'; import {PACKAGE_JSON} from './constants'; // Read the configuration and set its `rootDir` // 1. If it's a `package.json` file, we look into its "jest" property // 2. For any other file, we just require it. -export default (configPath: Path): InitialOptions => { +export default (configPath: Config.Path): Config.InitialOptions => { const isJSON = configPath.endsWith('.json'); let configObject; try { - // $FlowFixMe dynamic require configObject = require(configPath); } catch (error) { if (isJSON) { diff --git a/packages/jest-config/src/resolveConfigPath.js b/packages/jest-config/src/resolveConfigPath.ts similarity index 87% rename from packages/jest-config/src/resolveConfigPath.js rename to packages/jest-config/src/resolveConfigPath.ts index a36dd758a807..d85cfb39e20c 100644 --- a/packages/jest-config/src/resolveConfigPath.js +++ b/packages/jest-config/src/resolveConfigPath.ts @@ -3,20 +3,17 @@ * * 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 {Path} from 'types/Config'; - import path from 'path'; import fs from 'fs'; +import {Config} from '@jest/types'; import {JEST_CONFIG, PACKAGE_JSON} from './constants'; -const isFile = filePath => +const isFile = (filePath: Config.Path) => fs.existsSync(filePath) && !fs.lstatSync(filePath).isDirectory(); -export default (pathToResolve: Path, cwd: Path): Path => { +export default (pathToResolve: Config.Path, cwd: Config.Path): Config.Path => { if (!path.isAbsolute(cwd)) { throw new Error(`"cwd" must be an absolute path. cwd: ${cwd}`); } @@ -50,10 +47,10 @@ export default (pathToResolve: Path, cwd: Path): Path => { }; const resolveConfigPathByTraversing = ( - pathToResolve: Path, - initialPath: Path, - cwd: Path, -) => { + pathToResolve: Config.Path, + initialPath: Config.Path, + cwd: Config.Path, +): Config.Path => { const jestConfig = path.resolve(pathToResolve, JEST_CONFIG); if (isFile(jestConfig)) { return jestConfig; @@ -78,7 +75,10 @@ const resolveConfigPathByTraversing = ( ); }; -const makeResolutionErrorMessage = (initialPath: Path, cwd: Path) => +const makeResolutionErrorMessage = ( + initialPath: Config.Path, + cwd: Config.Path, +) => 'Could not find a config file based on provided values:\n' + `path: "${initialPath}"\n` + `cwd: "${cwd}"\n` + diff --git a/packages/jest-config/src/setFromArgv.js b/packages/jest-config/src/setFromArgv.ts similarity index 86% rename from packages/jest-config/src/setFromArgv.js rename to packages/jest-config/src/setFromArgv.ts index 2cde2ee37d33..f1ca92ed20be 100644 --- a/packages/jest-config/src/setFromArgv.js +++ b/packages/jest-config/src/setFromArgv.ts @@ -3,24 +3,21 @@ * * 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 {InitialOptions} from 'types/Config'; -import type {Argv} from 'types/Argv'; +import {Config} from '@jest/types'; const specialArgs = ['_', '$0', 'h', 'help', 'config']; import {isJSONString} from './utils'; export default function setFromArgv( - options: InitialOptions, - argv: Argv, -): InitialOptions { + options: Config.InitialOptions, + argv: Config.Argv, +): Config.InitialOptions { // $FlowFixMe: Seems like flow doesn't approve of string values const argvToOptions = Object.keys(argv) .filter(key => argv[key] !== undefined && specialArgs.indexOf(key) === -1) - .reduce((options: {[key: string]: mixed}, key) => { + .reduce((options: {[key: string]: unknown}, key) => { switch (key) { case 'coverage': options.collectCoverage = argv[key]; diff --git a/packages/jest-config/src/utils.js b/packages/jest-config/src/utils.ts similarity index 77% rename from packages/jest-config/src/utils.js rename to packages/jest-config/src/utils.ts index 168c07657cb3..5a93c832969d 100644 --- a/packages/jest-config/src/utils.js +++ b/packages/jest-config/src/utils.ts @@ -3,23 +3,21 @@ * * 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 {Path, Glob} from 'types/Config'; - import path from 'path'; +import {Config} from '@jest/types'; +// @ts-ignore: Not migrated to TS import {ValidationError} from 'jest-validate'; import Resolver from 'jest-resolve'; import chalk from 'chalk'; -type ResolveOptions = {| - rootDir: string, - key: string, - filePath: Path, - optional?: boolean, -|}; +type ResolveOptions = { + rootDir: Config.Path; + key: string; + filePath: Config.Path; + optional?: boolean; +}; export const BULLET: string = chalk.bold('\u25cf '); export const DOCUMENTATION_NOTE = ` ${chalk.bold( @@ -32,14 +30,14 @@ const createValidationError = (message: string) => new ValidationError(`${BULLET}Validation Error`, message, DOCUMENTATION_NOTE); export const resolve = ( - resolver: ?string, + resolver: string | null | undefined, {key, filePath, rootDir, optional}: ResolveOptions, ) => { const module = Resolver.findNodeModule( replaceRootDirInPath(rootDir, filePath), { basedir: rootDir, - resolver, + resolver: resolver || undefined, }, ); @@ -55,12 +53,12 @@ export const resolve = ( return module; }; -export const escapeGlobCharacters = (path: Path): Glob => +export const escapeGlobCharacters = (path: Config.Path): Config.Glob => path.replace(/([()*{}\[\]!?\\])/g, '\\$1'); export const replaceRootDirInPath = ( - rootDir: string, - filePath: Path, + rootDir: Config.Path, + filePath: Config.Path, ): string => { if (!/^/.test(filePath)) { return filePath; @@ -72,9 +70,13 @@ export const replaceRootDirInPath = ( ); }; -const _replaceRootDirInObject = (rootDir: string, config: any): Object => { +// TODO: Type as returning same type as input +const _replaceRootDirInObject = ( + rootDir: Config.Path, + config: any, +): {[key: string]: unknown} => { if (config !== null) { - const newConfig = {}; + const newConfig: {[key: string]: unknown} = {}; for (const configKey in config) { newConfig[configKey] = configKey === 'rootDir' @@ -86,7 +88,8 @@ const _replaceRootDirInObject = (rootDir: string, config: any): Object => { return config; }; -export const _replaceRootDirTags = (rootDir: string, config: any) => { +// TODO: Type as returning same type as input +export const _replaceRootDirTags = (rootDir: Config.Path, config: any): any => { switch (typeof config) { case 'object': if (Array.isArray(config)) { @@ -103,7 +106,7 @@ export const _replaceRootDirTags = (rootDir: string, config: any) => { }; export const resolveWithPrefix = ( - resolver: ?string, + resolver: string | undefined | null, { filePath, humanOptionName, @@ -111,17 +114,17 @@ export const resolveWithPrefix = ( prefix, rootDir, }: { - filePath: string, - humanOptionName: string, - optionName: string, - prefix: string, - rootDir: string, + filePath: string; + humanOptionName: string; + optionName: string; + prefix: string; + rootDir: Config.Path; }, ) => { const fileName = replaceRootDirInPath(rootDir, filePath); let module = Resolver.findNodeModule(`${prefix}${fileName}`, { basedir: rootDir, - resolver, + resolver: resolver || undefined, }); if (module) { return module; @@ -133,7 +136,7 @@ export const resolveWithPrefix = ( module = Resolver.findNodeModule(fileName, { basedir: rootDir, - resolver, + resolver: resolver || undefined, }); if (module) { return module; @@ -164,10 +167,10 @@ export const getTestEnvironment = ({ rootDir, testEnvironment: filePath, }: { - rootDir: string, - testEnvironment: string, + rootDir: Config.Path; + testEnvironment: string; }) => - resolveWithPrefix(null, { + resolveWithPrefix(undefined, { filePath, humanOptionName: 'Test environment', optionName: 'testEnvironment', @@ -184,8 +187,8 @@ export const getTestEnvironment = ({ * 1. looks for relative to Jest. */ export const getWatchPlugin = ( - resolver: ?string, - {filePath, rootDir}: {filePath: string, rootDir: string}, + resolver: string | undefined | null, + {filePath, rootDir}: {filePath: string; rootDir: Config.Path}, ) => resolveWithPrefix(resolver, { filePath, @@ -204,8 +207,8 @@ export const getWatchPlugin = ( * 1. looks for relative to Jest. */ export const getRunner = ( - resolver: ?string, - {filePath, rootDir}: {filePath: string, rootDir: string}, + resolver: string | undefined | null, + {filePath, rootDir}: {filePath: string; rootDir: Config.Path}, ) => resolveWithPrefix(resolver, { filePath, @@ -215,7 +218,7 @@ export const getRunner = ( rootDir, }); -export const isJSONString = (text: ?string) => +export const isJSONString = (text?: string) => text && typeof text === 'string' && text.startsWith('{') && diff --git a/packages/jest-config/src/validatePattern.js b/packages/jest-config/src/validatePattern.ts similarity index 84% rename from packages/jest-config/src/validatePattern.js rename to packages/jest-config/src/validatePattern.ts index 483277ad9d6a..476a0b271f46 100644 --- a/packages/jest-config/src/validatePattern.js +++ b/packages/jest-config/src/validatePattern.ts @@ -3,11 +3,9 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -export default function validatePattern(pattern: string) { +export default function validatePattern(pattern?: string) { if (pattern) { try { // eslint-disable-next-line no-new diff --git a/packages/jest-config/tsconfig.json b/packages/jest-config/tsconfig.json new file mode 100644 index 000000000000..50965f82bc07 --- /dev/null +++ b/packages/jest-config/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + }, + // TODO: This is missing `jest-validate`, in addition to `jest-environment-jsdom`, `jest-environment-node` and + // `jest-jasmine2`, but those are just `require.resolve`d, so no real use for their types + "references": [ + {"path": "../jest-get-type"}, + {"path": "../jest-regex-util"}, + {"path": "../jest-resolve"}, + {"path": "../jest-types"}, + {"path": "../jest-util"}, + {"path": "../pretty-format"} + ] +} diff --git a/packages/jest-runner/src/runTest.ts b/packages/jest-runner/src/runTest.ts index 22e7cd57465b..2bda5aa71b3c 100644 --- a/packages/jest-runner/src/runTest.ts +++ b/packages/jest-runner/src/runTest.ts @@ -20,7 +20,6 @@ import { } from 'jest-util'; import LeakDetector from 'jest-leak-detector'; import Resolver from 'jest-resolve'; -// @ts-ignore: not migrated to TS import {getTestEnvironment} from 'jest-config'; import * as docblock from 'jest-docblock'; import {formatExecError} from 'jest-message-util'; @@ -90,6 +89,13 @@ async function runTestInternal( let testEnvironment = config.testEnvironment; if (customEnvironment) { + if (Array.isArray(customEnvironment)) { + throw new Error( + `You can only define a single test environment through docblocks, got "${customEnvironment.join( + ', ', + )}"`, + ); + } testEnvironment = getTestEnvironment({ ...config, testEnvironment: customEnvironment, diff --git a/packages/jest-runner/tsconfig.json b/packages/jest-runner/tsconfig.json index 72bf161339bc..2b89d723edb6 100644 --- a/packages/jest-runner/tsconfig.json +++ b/packages/jest-runner/tsconfig.json @@ -5,6 +5,7 @@ "outDir": "build" }, "references": [ + {"path": "../jest-config"}, {"path": "../jest-docblock"}, {"path": "../jest-environment"}, {"path": "../jest-haste-map"}, diff --git a/packages/jest-runtime/src/cli/index.ts b/packages/jest-runtime/src/cli/index.ts index 5a6284388da5..70a125076fb4 100644 --- a/packages/jest-runtime/src/cli/index.ts +++ b/packages/jest-runtime/src/cli/index.ts @@ -15,7 +15,6 @@ import {JestEnvironment} from '@jest/environment'; import {Console, setGlobal} from 'jest-util'; // @ts-ignore: Not migrated to TS import {validateCLIOptions} from 'jest-validate'; -// @ts-ignore: Not migrated to TS import {readConfig, deprecationEntries} from 'jest-config'; import {VERSION} from '../version'; import {Context} from '../types'; @@ -63,6 +62,8 @@ export function run(cliArgv?: Config.Argv, cliInfo?: Array) { const info = cliInfo ? ', ' + cliInfo.join(', ') : ''; console.log(`Using Jest Runtime v${VERSION}${info}`); } + // TODO: Figure this out + // @ts-ignore: this might not have the correct arguments const options = readConfig(argv, root); const globalConfig = options.globalConfig; // Always disable automocking in scripts. diff --git a/packages/jest-runtime/tsconfig.json b/packages/jest-runtime/tsconfig.json index b73127d8e1fd..1b373a713a15 100644 --- a/packages/jest-runtime/tsconfig.json +++ b/packages/jest-runtime/tsconfig.json @@ -4,8 +4,9 @@ "rootDir": "src", "outDir": "build" }, - // TODO: Missing `jest-config`, `jest-validate` and `jest-environment-node` + // TODO: Missing `jest-validate` and `jest-environment-node` "references": [ + {"path": "../jest-config"}, {"path": "../jest-environment"}, {"path": "../jest-haste-map"}, {"path": "../jest-message-util"},