diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 1b660ebb7de4..f2a301a360e9 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -54,6 +54,8 @@ jobs: run: yarn typecheck:examples - name: typecheck tests run: yarn typecheck:tests + - name: run ESLint with type info + run: yarn lint-ts-files lint: name: Lint diff --git a/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap b/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap index c7eb02d6cfe1..20aad34dd9ef 100644 --- a/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap +++ b/e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap @@ -41,7 +41,7 @@ exports[`moduleNameMapper wrong array configuration 1`] = ` 12 | module.exports = () => 'test'; 13 | - at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:896:17) + at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:891:17) at Object.require (index.js:10:1) at Object.require (__tests__/index.js:10:20)" `; @@ -71,7 +71,7 @@ exports[`moduleNameMapper wrong configuration 1`] = ` 12 | module.exports = () => 'test'; 13 | - at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:896:17) + at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:891:17) at Object.require (index.js:10:1) at Object.require (__tests__/index.js:10:20)" `; diff --git a/package.json b/package.json index 425b52667541..16a62e0bcac4 100644 --- a/package.json +++ b/package.json @@ -103,6 +103,7 @@ "lint:prettier-script": "prettier . \"!**/*.{js,jsx,cjs,mjs,ts,tsx}\" --cache", "lint:prettier": "yarn lint:prettier-script --write", "lint:prettier:ci": "yarn lint:prettier-script --check", + "lint-ts-files": "node scripts/lintTs.mjs", "remove-examples": "node ./scripts/remove-examples.mjs", "test-ci-partial": "yarn test-ci-partial:parallel -i", "test-ci-partial:parallel": "yarn jest --color --config jest.config.ci.mjs", diff --git a/packages/babel-jest/src/__tests__/getCacheKey.test.ts b/packages/babel-jest/src/__tests__/getCacheKey.test.ts index 12b99b59bc65..0c5933625d2c 100644 --- a/packages/babel-jest/src/__tests__/getCacheKey.test.ts +++ b/packages/babel-jest/src/__tests__/getCacheKey.test.ts @@ -52,7 +52,8 @@ describe('getCacheKey', () => { readFileSync: () => 'new this file', })); - const {createTransformer}: typeof import('../index') = require('../index'); + const {createTransformer} = + require('../index') as typeof import('../index'); const newCacheKey = createTransformer().getCacheKey!( sourceText, @@ -65,7 +66,7 @@ describe('getCacheKey', () => { test('if `babelOptions.options` value is changing', () => { jest.doMock('../loadBabelConfig', () => { - const babel: typeof import('@babel/core') = require('@babel/core'); + const babel = require('@babel/core') as typeof import('@babel/core'); return { loadPartialConfig: (options: BabelTransformOptions) => ({ @@ -75,7 +76,8 @@ describe('getCacheKey', () => { }; }); - const {createTransformer}: typeof import('../index') = require('../index'); + const {createTransformer} = + require('../index') as typeof import('../index'); const newCacheKey = createTransformer().getCacheKey!( sourceText, @@ -117,7 +119,7 @@ describe('getCacheKey', () => { test('if `babelOptions.config` value is changing', () => { jest.doMock('../loadBabelConfig', () => { - const babel: typeof import('@babel/core') = require('@babel/core'); + const babel = require('@babel/core') as typeof import('@babel/core'); return { loadPartialConfig: (options: BabelTransformOptions) => ({ @@ -127,7 +129,8 @@ describe('getCacheKey', () => { }; }); - const {createTransformer}: typeof import('../index') = require('../index'); + const {createTransformer} = + require('../index') as typeof import('../index'); const newCacheKey = createTransformer().getCacheKey!( sourceText, @@ -140,7 +143,7 @@ describe('getCacheKey', () => { test('if `babelOptions.babelrc` value is changing', () => { jest.doMock('../loadBabelConfig', () => { - const babel: typeof import('@babel/core') = require('@babel/core'); + const babel = require('@babel/core') as typeof import('@babel/core'); return { loadPartialConfig: (options: BabelTransformOptions) => ({ @@ -150,7 +153,8 @@ describe('getCacheKey', () => { }; }); - const {createTransformer}: typeof import('../index') = require('../index'); + const {createTransformer} = + require('../index') as typeof import('../index'); const newCacheKey = createTransformer().getCacheKey!( sourceText, diff --git a/packages/babel-jest/src/__tests__/index.ts b/packages/babel-jest/src/__tests__/index.ts index 64ccfcad5bdd..9903c2b773b0 100644 --- a/packages/babel-jest/src/__tests__/index.ts +++ b/packages/babel-jest/src/__tests__/index.ts @@ -13,14 +13,17 @@ import {makeProjectConfig} from '@jest/test-utils'; import type {TransformOptions} from '@jest/transform'; import babelJest, {createTransformer} from '../index'; import {loadPartialConfig} from '../loadBabelConfig'; + jest.mock('../loadBabelConfig', () => { const actual = jest.requireActual('@babel/core'); return { - loadPartialConfig: jest.fn((...args) => actual.loadPartialConfig(...args)), - loadPartialConfigAsync: jest.fn((...args) => - actual.loadPartialConfigAsync(...args), + loadPartialConfig: jest.fn((...args) => + actual.loadPartialConfig(...args), + ), + loadPartialConfigAsync: jest.fn( + (...args) => actual.loadPartialConfigAsync(...args), ), }; }); @@ -69,7 +72,7 @@ test('Returns source string with inline maps when no transformOptions is passed' }); test('Returns source string with inline maps when no transformOptions is passed async', async () => { - const result: any = await defaultBabelJestTransformer.processAsync!( + const result = await defaultBabelJestTransformer.processAsync!( sourceString, 'dummy_path.js', { @@ -86,8 +89,18 @@ test('Returns source string with inline maps when no transformOptions is passed expect(result.map).toBeDefined(); expect(result.code).toMatch('//# sourceMappingURL'); expect(result.code).toMatch('customMultiply'); - expect(result.map!.sources).toEqual(['dummy_path.js']); - expect(JSON.stringify(result.map!.sourcesContent)).toMatch('customMultiply'); + + const {map} = result; + + expect(map).toBeTruthy(); + expect(typeof map).not.toBe('string'); + + if (map == null || typeof map === 'string') { + throw new Error('dead code'); + } + + expect(map.sources).toEqual(['dummy_path.js']); + expect(JSON.stringify(map.sourcesContent)).toMatch('customMultiply'); }); describe('caller option correctly merges from defaults and options', () => { diff --git a/packages/babel-jest/src/index.ts b/packages/babel-jest/src/index.ts index da93a3c4cecf..cc51dbc4ea0d 100644 --- a/packages/babel-jest/src/index.ts +++ b/packages/babel-jest/src/index.ts @@ -51,7 +51,7 @@ function addIstanbulInstrumentation( const copiedBabelOptions: TransformOptions = {...babelOptions}; copiedBabelOptions.auxiliaryCommentBefore = ' istanbul ignore next '; // Copied from jest-runtime transform.js - copiedBabelOptions.plugins = (copiedBabelOptions.plugins || []).concat([ + copiedBabelOptions.plugins = (copiedBabelOptions.plugins ?? []).concat([ [ babelIstanbulPlugin, { @@ -76,7 +76,7 @@ function getCacheKeyFromConfig( ): string { const {config, configString, instrument} = transformOptions; - const configPath = [babelOptions.config || '', babelOptions.babelrc || '']; + const configPath = [babelOptions.config ?? '', babelOptions.babelrc ?? '']; return createHash('sha256') .update(THIS_FILE) @@ -93,9 +93,9 @@ function getCacheKeyFromConfig( .update('\0', 'utf8') .update(instrument ? 'instrument' : '') .update('\0', 'utf8') - .update(process.env.NODE_ENV || '') + .update(process.env.NODE_ENV ?? '') .update('\0', 'utf8') - .update(process.env.BABEL_ENV || '') + .update(process.env.BABEL_ENV ?? '') .update('\0', 'utf8') .update(process.version) .digest('hex') diff --git a/packages/babel-plugin-jest-hoist/src/index.ts b/packages/babel-plugin-jest-hoist/src/index.ts index 2841036ede50..99eb02c3d081 100644 --- a/packages/babel-plugin-jest-hoist/src/index.ts +++ b/packages/babel-plugin-jest-hoist/src/index.ts @@ -114,10 +114,10 @@ const IDVisitor = { ], }; -const FUNCTIONS: Record< +const FUNCTIONS = Object.create(null) as Record< string, (args: Array>) => boolean -> = Object.create(null); +>; FUNCTIONS.mock = args => { if (args.length === 1) { @@ -142,7 +142,7 @@ FUNCTIONS.mock = args => { let scope = id.scope; while (scope !== parentScope) { - if (scope.bindings[name]) { + if (scope.bindings[name] != null) { found = true; break; } diff --git a/packages/diff-sequences/src/__tests__/index.property.test.ts b/packages/diff-sequences/src/__tests__/index.property.test.ts index 63773d62749e..17573e5df7c6 100644 --- a/packages/diff-sequences/src/__tests__/index.property.test.ts +++ b/packages/diff-sequences/src/__tests__/index.property.test.ts @@ -27,7 +27,7 @@ const findCommonItems = (a: Array, b: Array): Array => { const extractCount = (data: Array): Map => { const countPerChar = new Map(); for (const item of data) { - const currentCount = countPerChar.get(item) || 0; + const currentCount = countPerChar.get(item) ?? 0; countPerChar.set(item, currentCount + 1); } return countPerChar; @@ -93,7 +93,7 @@ it('should have at most the same number of each character as its inputs', () => const commonCount = extractCount(commonItems); const aCount = extractCount(a); for (const [item, count] of commonCount) { - const countOfItemInA = aCount.get(item) || 0; + const countOfItemInA = aCount.get(item) ?? 0; expect(countOfItemInA).toBeGreaterThanOrEqual(count); } }), diff --git a/packages/diff-sequences/src/__tests__/index.test.ts b/packages/diff-sequences/src/__tests__/index.test.ts index 7120126beef0..8f8f56c6cf49 100644 --- a/packages/diff-sequences/src/__tests__/index.test.ts +++ b/packages/diff-sequences/src/__tests__/index.test.ts @@ -15,7 +15,8 @@ describe('invalid arg', () => { describe('length', () => { test('is not a number', () => { expect(() => { - diff('0' as any, 0, isCommon, foundSubsequence); + // @ts-expect-error: Testing runtime errors here + diff('0', 0, isCommon, foundSubsequence); }).toThrow(/aLength/); }); test('Infinity is not a safe integer', () => { @@ -49,12 +50,14 @@ describe('invalid arg', () => { describe('callback', () => { test('null is not a function', () => { expect(() => { - diff(0, 0, null as any, foundSubsequence); + // @ts-expect-error: Testing runtime errors here + diff(0, 0, null, foundSubsequence); }).toThrow(/isCommon/); }); test('undefined is not a function', () => { expect(() => { - diff(0, 0, isCommon, undefined as any); + // @ts-expect-error: Testing runtime errors here + diff(0, 0, isCommon, undefined); }).toThrow(/foundSubsequence/); }); }); diff --git a/packages/expect-utils/src/jasmineUtils.ts b/packages/expect-utils/src/jasmineUtils.ts index 37671b4876e2..6aec53aa1127 100644 --- a/packages/expect-utils/src/jasmineUtils.ts +++ b/packages/expect-utils/src/jasmineUtils.ts @@ -211,9 +211,7 @@ function keys(obj: object, hasKey: (obj: object, key: string) => boolean) { } return keys.concat( (Object.getOwnPropertySymbols(obj) as Array).filter( - symbol => - (Object.getOwnPropertyDescriptor(obj, symbol) as PropertyDescriptor) - .enumerable, + symbol => Object.getOwnPropertyDescriptor(obj, symbol)!.enumerable, ), ); } diff --git a/packages/expect/src/__tests__/toThrowMatchers.test.ts b/packages/expect/src/__tests__/toThrowMatchers.test.ts index d1b0abcf47bb..ae4df6b638b4 100644 --- a/packages/expect/src/__tests__/toThrowMatchers.test.ts +++ b/packages/expect/src/__tests__/toThrowMatchers.test.ts @@ -458,9 +458,9 @@ matchers.forEach(toThrow => { err = new Err('async apple'); } if (resolve) { - return await Promise.resolve(err || 'apple'); + return Promise.resolve(err || 'apple'); } else { - return await Promise.reject(err || 'apple'); + return Promise.reject(err || 'apple'); } }; diff --git a/packages/expect/src/asymmetricMatchers.ts b/packages/expect/src/asymmetricMatchers.ts index dd62850eea2f..5c51bd921d8b 100644 --- a/packages/expect/src/asymmetricMatchers.ts +++ b/packages/expect/src/asymmetricMatchers.ts @@ -296,7 +296,7 @@ class StringMatching extends AsymmetricMatcher { } class CloseTo extends AsymmetricMatcher { - private precision: number; + private readonly precision: number; constructor(sample: number, precision = 2, inverse = false) { if (!isA('Number', sample)) { diff --git a/packages/expect/src/index.ts b/packages/expect/src/index.ts index e6420bd138c9..8ed6390738fb 100644 --- a/packages/expect/src/index.ts +++ b/packages/expect/src/index.ts @@ -39,7 +39,6 @@ import toThrowMatchers, { createMatcher as createThrowMatcher, } from './toThrowMatchers'; import type { - AsyncExpectationResult, Expect, ExpectationResult, MatcherContext, @@ -363,19 +362,16 @@ const makeThrowingMatcher = ( })(); if (isPromise(potentialResult)) { - const asyncResult = potentialResult as AsyncExpectationResult; const asyncError = new JestAssertionError(); if (Error.captureStackTrace) { Error.captureStackTrace(asyncError, throwingMatcher); } - return asyncResult + return potentialResult .then(aResult => processResult(aResult, asyncError)) .catch(handleError); } else { - const syncResult = potentialResult as SyncExpectationResult; - - return processResult(syncResult); + return processResult(potentialResult); } } catch (error: any) { return handleError(error); diff --git a/packages/jest-config/src/normalize.ts b/packages/jest-config/src/normalize.ts index 1a3adee53052..3d13af7e2bb7 100644 --- a/packages/jest-config/src/normalize.ts +++ b/packages/jest-config/src/normalize.ts @@ -108,8 +108,8 @@ const mergeGlobalsWithPreset = ( options: Config.InitialOptions, preset: Config.InitialOptions, ) => { - if (options['globals'] && preset['globals']) { - options['globals'] = merge(preset['globals'], options['globals']); + if (options.globals && preset.globals) { + options.globals = merge(preset.globals, options.globals); } }; @@ -1061,14 +1061,14 @@ export default async function normalize( ); newOptions.maxWorkers = getMaxWorkers(argv, options); - if (newOptions.testRegex!.length && options.testMatch) { + if (newOptions.testRegex.length > 0 && 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 > 0 && !options.testMatch) { // Prevent the default testMatch conflicting with any explicitly // configured `testRegex` value newOptions.testMatch = []; @@ -1101,7 +1101,7 @@ export default async function normalize( if ( micromatch( [replacePathSepForGlob(path.relative(options.rootDir, filename))], - newOptions.collectCoverageFrom!, + newOptions.collectCoverageFrom, ).length === 0 ) { return patterns; diff --git a/packages/jest-config/src/utils.ts b/packages/jest-config/src/utils.ts index 9d60f8fc9573..c547e2de477e 100644 --- a/packages/jest-config/src/utils.ts +++ b/packages/jest-config/src/utils.ts @@ -48,7 +48,7 @@ export const resolve = ( ); } /// can cast as string since nulls will be thrown - return module as string; + return module!; }; export const escapeGlobCharacters = (path: string): string => @@ -106,10 +106,7 @@ export const _replaceRootDirTags = ( return config; } - return _replaceRootDirInObject( - rootDir, - config as ReplaceRootDirConfigObj, - ) as T; + return _replaceRootDirInObject(rootDir, config) as T; case 'string': return replaceRootDirInPath(rootDir, config) as T; } diff --git a/packages/jest-console/src/BufferedConsole.ts b/packages/jest-console/src/BufferedConsole.ts index 07e7b4613e41..5fcac15adaf7 100644 --- a/packages/jest-console/src/BufferedConsole.ts +++ b/packages/jest-console/src/BufferedConsole.ts @@ -19,7 +19,7 @@ import type { } from './types'; export default class BufferedConsole extends Console { - private _buffer: ConsoleBuffer = []; + private readonly _buffer: ConsoleBuffer = []; private _counters: LogCounters = {}; private _timers: LogTimers = {}; private _groupDepth = 0; diff --git a/packages/jest-console/src/CustomConsole.ts b/packages/jest-console/src/CustomConsole.ts index 5688fb4af047..4ffe045b6f9e 100644 --- a/packages/jest-console/src/CustomConsole.ts +++ b/packages/jest-console/src/CustomConsole.ts @@ -15,9 +15,9 @@ import type {LogCounters, LogMessage, LogTimers, LogType} from './types'; type Formatter = (type: LogType, message: LogMessage) => string; export default class CustomConsole extends Console { - private _stdout: NodeJS.WriteStream; - private _stderr: NodeJS.WriteStream; - private _formatBuffer: Formatter; + private readonly _stdout: NodeJS.WriteStream; + private readonly _stderr: NodeJS.WriteStream; + private readonly _formatBuffer: Formatter; private _counters: LogCounters = {}; private _timers: LogTimers = {}; private _groupDepth = 0; diff --git a/packages/jest-core/src/FailedTestsInteractiveMode.ts b/packages/jest-core/src/FailedTestsInteractiveMode.ts index 8006603602a0..892fdb36df19 100644 --- a/packages/jest-core/src/FailedTestsInteractiveMode.ts +++ b/packages/jest-core/src/FailedTestsInteractiveMode.ts @@ -28,7 +28,7 @@ export default class FailedTestsInteractiveMode { private _testAssertions: Array = []; private _updateTestRunnerConfig?: RunnerUpdateFunction; - constructor(private _pipe: NodeJS.WritableStream) {} + constructor(private readonly _pipe: NodeJS.WritableStream) {} isActive(): boolean { return this._isActive; diff --git a/packages/jest-core/src/SearchSource.ts b/packages/jest-core/src/SearchSource.ts index b1c8d31125fd..0f9ab2712ffb 100644 --- a/packages/jest-core/src/SearchSource.ts +++ b/packages/jest-core/src/SearchSource.ts @@ -55,9 +55,9 @@ const hasSCM = (changedFilesInfo: ChangedFiles) => { }; export default class SearchSource { - private _context: TestContext; + private readonly _context: TestContext; private _dependencyResolver: DependencyResolver | null; - private _testPathCases: TestPathCases = []; + private readonly _testPathCases: TestPathCases = []; constructor(context: TestContext) { const {config} = context; diff --git a/packages/jest-core/src/SnapshotInteractiveMode.ts b/packages/jest-core/src/SnapshotInteractiveMode.ts index 111a90426868..8239f9d38eba 100644 --- a/packages/jest-core/src/SnapshotInteractiveMode.ts +++ b/packages/jest-core/src/SnapshotInteractiveMode.ts @@ -14,7 +14,7 @@ import {KEYS} from 'jest-watcher'; const {ARROW, CLEAR} = specialChars; export default class SnapshotInteractiveMode { - private _pipe: NodeJS.WritableStream; + private readonly _pipe: NodeJS.WritableStream; private _isActive: boolean; private _updateTestRunnerConfig!: ( assertion: AssertionLocation | null, diff --git a/packages/jest-core/src/__tests__/watchFileChanges.test.ts b/packages/jest-core/src/__tests__/watchFileChanges.test.ts index ed5a63821d72..4ed78ec942ec 100644 --- a/packages/jest-core/src/__tests__/watchFileChanges.test.ts +++ b/packages/jest-core/src/__tests__/watchFileChanges.test.ts @@ -167,7 +167,7 @@ describe('Watch mode flows with changed files', () => { }); class MockStdin { - private _callbacks: Array; + private readonly _callbacks: Array; constructor() { this._callbacks = []; diff --git a/packages/jest-core/src/cli/index.ts b/packages/jest-core/src/cli/index.ts index 95598c8b4e30..b3e7df8c5e12 100644 --- a/packages/jest-core/src/cli/index.ts +++ b/packages/jest-core/src/cli/index.ts @@ -242,7 +242,7 @@ const runWatch = async ( if (hasDeprecationWarnings) { try { await handleDeprecationWarnings(outputStream, process.stdin); - return watch( + return await watch( globalConfig, contexts, outputStream, diff --git a/packages/jest-core/src/plugins/TestPathPattern.ts b/packages/jest-core/src/plugins/TestPathPattern.ts index d0866db9b27f..d8c2137c0267 100644 --- a/packages/jest-core/src/plugins/TestPathPattern.ts +++ b/packages/jest-core/src/plugins/TestPathPattern.ts @@ -16,7 +16,7 @@ import TestPathPatternPrompt from '../TestPathPatternPrompt'; import activeFilters from '../lib/activeFiltersMessage'; class TestPathPatternPlugin extends BaseWatchPlugin { - private _prompt: Prompt; + private readonly _prompt: Prompt; isInternal: true; constructor(options: {stdin: NodeJS.ReadStream; stdout: NodeJS.WriteStream}) { diff --git a/packages/jest-core/src/plugins/UpdateSnapshotsInteractive.ts b/packages/jest-core/src/plugins/UpdateSnapshotsInteractive.ts index 84842abf8c90..58a3272292fd 100644 --- a/packages/jest-core/src/plugins/UpdateSnapshotsInteractive.ts +++ b/packages/jest-core/src/plugins/UpdateSnapshotsInteractive.ts @@ -13,7 +13,7 @@ import {BaseWatchPlugin, JestHookSubscriber, UsageData} from 'jest-watcher'; import SnapshotInteractiveMode from '../SnapshotInteractiveMode'; class UpdateSnapshotInteractivePlugin extends BaseWatchPlugin { - private _snapshotInteractiveMode: SnapshotInteractiveMode = + private readonly _snapshotInteractiveMode: SnapshotInteractiveMode = new SnapshotInteractiveMode(this._stdout); private _failedSnapshotTestAssertions: Array = []; isInternal = true as const; diff --git a/packages/jest-diff/src/getAlignedDiffs.ts b/packages/jest-diff/src/getAlignedDiffs.ts index e4831f509409..0dc010b38e1b 100644 --- a/packages/jest-diff/src/getAlignedDiffs.ts +++ b/packages/jest-diff/src/getAlignedDiffs.ts @@ -30,10 +30,10 @@ const concatenateRelevantDiffs = ( // Encapsulate change lines until either a common newline or the end. class ChangeBuffer { - private op: number; + private readonly op: number; private line: Array; // incomplete line private lines: Array; // complete lines - private changeColor: DiffOptionsColor; + private readonly changeColor: DiffOptionsColor; constructor(op: number, changeColor: DiffOptionsColor) { this.op = op; @@ -114,9 +114,9 @@ class ChangeBuffer { // Encapsulate common and change lines. class CommonBuffer { - private deleteBuffer: ChangeBuffer; - private insertBuffer: ChangeBuffer; - private lines: Array; + private readonly deleteBuffer: ChangeBuffer; + private readonly insertBuffer: ChangeBuffer; + private readonly lines: Array; constructor(deleteBuffer: ChangeBuffer, insertBuffer: ChangeBuffer) { this.deleteBuffer = deleteBuffer; diff --git a/packages/jest-fake-timers/src/legacyFakeTimers.ts b/packages/jest-fake-timers/src/legacyFakeTimers.ts index b7e614001528..b25653b44c50 100644 --- a/packages/jest-fake-timers/src/legacyFakeTimers.ts +++ b/packages/jest-fake-timers/src/legacyFakeTimers.ts @@ -66,20 +66,20 @@ const MS_IN_A_YEAR = 31536000000; export default class FakeTimers { private _cancelledTicks!: Record; - private _config: StackTraceConfig; - private _disposed?: boolean; + private readonly _config: StackTraceConfig; + private _disposed: boolean; private _fakeTimerAPIs!: FakeTimerAPI; private _fakingTime = false; private _global: typeof globalThis; private _immediates!: Array; - private _maxLoops: number; - private _moduleMocker: ModuleMocker; + private readonly _maxLoops: number; + private readonly _moduleMocker: ModuleMocker; private _now!: number; private _ticks!: Array; - private _timerAPIs: TimerAPI; + private readonly _timerAPIs: TimerAPI; private _timers!: Map; private _uuidCounter: number; - private _timerConfig: TimerConfig; + private readonly _timerConfig: TimerConfig; constructor({ global, @@ -114,6 +114,8 @@ export default class FakeTimers { setTimeout: global.setTimeout, }; + this._disposed = false; + this.reset(); } @@ -510,11 +512,13 @@ export default class FakeTimers { }); this._timerAPIs.setImmediate(() => { - if (this._immediates.find(x => x.uuid === uuid)) { - try { - callback.apply(null, args); - } finally { - this._fakeClearImmediate(uuid); + if (!this._disposed) { + if (this._immediates.find(x => x.uuid === uuid)) { + try { + callback.apply(null, args); + } finally { + this._fakeClearImmediate(uuid); + } } } }); diff --git a/packages/jest-fake-timers/src/modernFakeTimers.ts b/packages/jest-fake-timers/src/modernFakeTimers.ts index 1a325ee662e3..42d495b688fd 100644 --- a/packages/jest-fake-timers/src/modernFakeTimers.ts +++ b/packages/jest-fake-timers/src/modernFakeTimers.ts @@ -17,10 +17,10 @@ import {formatStackTrace} from 'jest-message-util'; export default class FakeTimers { private _clock!: InstalledClock; - private _config: Config.ProjectConfig; + private readonly _config: Config.ProjectConfig; private _fakingTime: boolean; - private _global: typeof globalThis; - private _fakeTimers: FakeTimerWithContext; + private readonly _global: typeof globalThis; + private readonly _fakeTimers: FakeTimerWithContext; constructor({ global, diff --git a/packages/jest-haste-map/src/index.ts b/packages/jest-haste-map/src/index.ts index 812a4cbdb700..1ab221bbe82a 100644 --- a/packages/jest-haste-map/src/index.ts +++ b/packages/jest-haste-map/src/index.ts @@ -217,7 +217,7 @@ class HasteMap extends EventEmitter implements IHasteMap { private _buildPromise: Promise | null = null; private _cachePath = ''; private _changeInterval?: ReturnType; - private _console: Console; + private readonly _console: Console; private _isWatchmanInstalledPromise: Promise | null = null; private _options: InternalOptions; private _watchers: Array = []; @@ -797,7 +797,7 @@ class HasteMap extends EventEmitter implements IHasteMap { }; try { - return crawl(crawlerOptions).catch(retry); + return await crawl(crawlerOptions); } catch (error: any) { return retry(error); } diff --git a/packages/jest-haste-map/src/watchers/FSEventsWatcher.ts b/packages/jest-haste-map/src/watchers/FSEventsWatcher.ts index da9530af2520..67d10685b172 100644 --- a/packages/jest-haste-map/src/watchers/FSEventsWatcher.ts +++ b/packages/jest-haste-map/src/watchers/FSEventsWatcher.ts @@ -46,7 +46,7 @@ export class FSEventsWatcher extends EventEmitter { readonly hasIgnore: boolean; readonly doIgnore: (path: string) => boolean; readonly fsEventsWatchStopper: () => Promise; - private _tracked: Set; + private readonly _tracked: Set; static isSupported(): boolean { return fsevents !== null; diff --git a/packages/jest-jasmine2/src/PCancelable.ts b/packages/jest-jasmine2/src/PCancelable.ts index 4bc49293803d..2605f6a08c88 100644 --- a/packages/jest-jasmine2/src/PCancelable.ts +++ b/packages/jest-jasmine2/src/PCancelable.ts @@ -15,7 +15,7 @@ class CancelError extends Error { export default class PCancelable implements PromiseLike { private _pending = true; private _canceled = false; - private _promise: Promise; + private readonly _promise: Promise; private _cancel?: () => void; // eslint-disable-next-line @typescript-eslint/no-empty-function private _reject: (reason?: unknown) => void = () => {}; diff --git a/packages/jest-jasmine2/src/jasmine/Suite.ts b/packages/jest-jasmine2/src/jasmine/Suite.ts index 7b1a3ba03d17..c6d89affb72c 100644 --- a/packages/jest-jasmine2/src/jasmine/Suite.ts +++ b/packages/jest-jasmine2/src/jasmine/Suite.ts @@ -159,7 +159,7 @@ export default class Suite { } getResult() { - this.result.status! = this.status(); + this.result.status = this.status(); return this.result; } diff --git a/packages/jest-jasmine2/src/jasmine/spyRegistry.ts b/packages/jest-jasmine2/src/jasmine/spyRegistry.ts index ac3e98428179..cd4a039479a1 100644 --- a/packages/jest-jasmine2/src/jasmine/spyRegistry.ts +++ b/packages/jest-jasmine2/src/jasmine/spyRegistry.ts @@ -64,7 +64,7 @@ export default class SpyRegistry { clearSpies: () => void; respy: unknown; - private _spyOnProperty: ( + private readonly _spyOnProperty: ( obj: Record, propertyName: string, accessType: keyof PropertyDescriptor, diff --git a/packages/jest-jasmine2/src/reporter.ts b/packages/jest-jasmine2/src/reporter.ts index a82caa84100c..53163d20f374 100644 --- a/packages/jest-jasmine2/src/reporter.ts +++ b/packages/jest-jasmine2/src/reporter.ts @@ -19,14 +19,14 @@ import type {Reporter, RunDetails} from './types'; type Microseconds = number; export default class Jasmine2Reporter implements Reporter { - private _testResults: Array; - private _globalConfig: Config.GlobalConfig; - private _config: Config.ProjectConfig; - private _currentSuites: Array; + private readonly _testResults: Array; + private readonly _globalConfig: Config.GlobalConfig; + private readonly _config: Config.ProjectConfig; + private readonly _currentSuites: Array; private _resolve: any; - private _resultsPromise: Promise; - private _startTimes: Map; - private _testPath: string; + private readonly _resultsPromise: Promise; + private readonly _startTimes: Map; + private readonly _testPath: string; constructor( globalConfig: Config.GlobalConfig, diff --git a/packages/jest-leak-detector/src/index.ts b/packages/jest-leak-detector/src/index.ts index ff61ae78a246..f7f6f583198c 100644 --- a/packages/jest-leak-detector/src/index.ts +++ b/packages/jest-leak-detector/src/index.ts @@ -16,7 +16,7 @@ const tick = promisify(setImmediate); export default class LeakDetector { private _isReferenceBeingHeld: boolean; - private _finalizationRegistry?: FinalizationRegistry; + private readonly _finalizationRegistry?: FinalizationRegistry; constructor(value: unknown) { if (isPrimitive(value)) { diff --git a/packages/jest-mock/src/index.ts b/packages/jest-mock/src/index.ts index b727d18e749f..157b47c76c67 100644 --- a/packages/jest-mock/src/index.ts +++ b/packages/jest-mock/src/index.ts @@ -463,7 +463,7 @@ function isReadonlyProp(object: unknown, prop: string): boolean { } export class ModuleMocker { - private _environmentGlobal: typeof globalThis; + private readonly _environmentGlobal: typeof globalThis; private _mockState: WeakMap; private _mockConfigRegistry: WeakMap; private _spyState: Set<() => void>; diff --git a/packages/jest-reporters/src/CoverageReporter.ts b/packages/jest-reporters/src/CoverageReporter.ts index a09e0f9b3f1b..33de15715741 100644 --- a/packages/jest-reporters/src/CoverageReporter.ts +++ b/packages/jest-reporters/src/CoverageReporter.ts @@ -37,11 +37,11 @@ const FAIL_COLOR = chalk.bold.red; const RUNNING_TEST_COLOR = chalk.bold.dim; export default class CoverageReporter extends BaseReporter { - private _context: ReporterContext; - private _coverageMap: istanbulCoverage.CoverageMap; - private _globalConfig: Config.GlobalConfig; - private _sourceMapStore: libSourceMaps.MapStore; - private _v8CoverageResults: Array; + private readonly _context: ReporterContext; + private readonly _coverageMap: istanbulCoverage.CoverageMap; + private readonly _globalConfig: Config.GlobalConfig; + private readonly _sourceMapStore: libSourceMaps.MapStore; + private readonly _v8CoverageResults: Array; static readonly filename = __filename; diff --git a/packages/jest-reporters/src/DefaultReporter.ts b/packages/jest-reporters/src/DefaultReporter.ts index 432073401f3d..c8451b3a9468 100644 --- a/packages/jest-reporters/src/DefaultReporter.ts +++ b/packages/jest-reporters/src/DefaultReporter.ts @@ -33,11 +33,11 @@ const TITLE_BULLET = chalk.bold('\u25cf '); export default class DefaultReporter extends BaseReporter { private _clear: string; // ANSI clear sequence for the last printed status - private _err: write; + private readonly _err: write; protected _globalConfig: Config.GlobalConfig; - private _out: write; - private _status: Status; - private _bufferedOutput: Set; + private readonly _out: write; + private readonly _status: Status; + private readonly _bufferedOutput: Set; static readonly filename = __filename; diff --git a/packages/jest-reporters/src/NotifyReporter.ts b/packages/jest-reporters/src/NotifyReporter.ts index 337022af8916..a8d4f8eccb7b 100644 --- a/packages/jest-reporters/src/NotifyReporter.ts +++ b/packages/jest-reporters/src/NotifyReporter.ts @@ -19,8 +19,8 @@ const isDarwin = process.platform === 'darwin'; const icon = path.resolve(__dirname, '../assets/jest_logo.png'); export default class NotifyReporter extends BaseReporter { - private _notifier = loadNotifier(); - private _globalConfig: Config.GlobalConfig; + private readonly _notifier = loadNotifier(); + private readonly _globalConfig: Config.GlobalConfig; private _context: ReporterContext; static readonly filename = __filename; diff --git a/packages/jest-reporters/src/Status.ts b/packages/jest-reporters/src/Status.ts index 5236e14373f5..f0a993f2eb96 100644 --- a/packages/jest-reporters/src/Status.ts +++ b/packages/jest-reporters/src/Status.ts @@ -73,7 +73,7 @@ type Cache = { export default class Status { private _cache: Cache | null; private _callback?: () => void; - private _currentTests: CurrentTestList; + private readonly _currentTests: CurrentTestList; private _currentTestCases: Array<{ test: Test; testCaseResult: TestCaseResult; @@ -160,7 +160,7 @@ export default class Status { return {clear: '', content: ''}; } - const width: number = process.stdout.columns!; + const width = process.stdout.columns; let content = '\n'; this._currentTests.get().forEach(record => { if (record) { diff --git a/packages/jest-reporters/src/SummaryReporter.ts b/packages/jest-reporters/src/SummaryReporter.ts index 5103d3f52fcc..ea7b8a0b037a 100644 --- a/packages/jest-reporters/src/SummaryReporter.ts +++ b/packages/jest-reporters/src/SummaryReporter.ts @@ -53,7 +53,7 @@ const {npm_config_user_agent, npm_lifecycle_event, npm_lifecycle_script} = export default class SummaryReporter extends BaseReporter { private _estimatedTime: number; - private _globalConfig: Config.GlobalConfig; + private readonly _globalConfig: Config.GlobalConfig; static readonly filename = __filename; diff --git a/packages/jest-reporters/src/__tests__/NotifyReporter.test.ts b/packages/jest-reporters/src/__tests__/NotifyReporter.test.ts index bc59000d41f8..a3e6a65a6ae1 100644 --- a/packages/jest-reporters/src/__tests__/NotifyReporter.test.ts +++ b/packages/jest-reporters/src/__tests__/NotifyReporter.test.ts @@ -248,7 +248,7 @@ describe('node-notifier is an optional dependency', () => { const mockNodeNotifier = {notify: jest.fn()}; jest.doMock('node-notifier', () => mockNodeNotifier); const result = ctor(); - expect(result['_notifier']).toBe(mockNodeNotifier); + expect(result._notifier).toBe(mockNodeNotifier); }); }); diff --git a/packages/jest-reporters/src/wrapAnsiString.ts b/packages/jest-reporters/src/wrapAnsiString.ts index 6a6db4d3da55..0dca82ef8b71 100644 --- a/packages/jest-reporters/src/wrapAnsiString.ts +++ b/packages/jest-reporters/src/wrapAnsiString.ts @@ -23,7 +23,7 @@ export default function wrapAnsiString( while ((match = ANSI_REGEXP.exec(string))) { const ansi = match[0]; - const index = match['index']; + const index = match.index; if (index != lastIndex) { tokens.push(['string', string.slice(lastIndex, index)]); } diff --git a/packages/jest-resolve-dependencies/src/index.ts b/packages/jest-resolve-dependencies/src/index.ts index 58ea8b26481b..6e6c6c004cfc 100644 --- a/packages/jest-resolve-dependencies/src/index.ts +++ b/packages/jest-resolve-dependencies/src/index.ts @@ -20,9 +20,9 @@ export type ResolvedModule = { * to retrieve a list of all transitive inverse dependencies. */ export class DependencyResolver { - private _hasteFS: IHasteFS; - private _resolver: Resolver; - private _snapshotResolver: SnapshotResolver; + private readonly _hasteFS: IHasteFS; + private readonly _resolver: Resolver; + private readonly _snapshotResolver: SnapshotResolver; constructor( resolver: Resolver, diff --git a/packages/jest-resolve/src/resolver.ts b/packages/jest-resolve/src/resolver.ts index 02b7c16634b7..98dbfc916ad7 100644 --- a/packages/jest-resolve/src/resolver.ts +++ b/packages/jest-resolve/src/resolver.ts @@ -89,7 +89,7 @@ export default class Resolver { error: unknown, ): ModuleNotFoundError | null { if (error instanceof ModuleNotFoundError) { - return error as ModuleNotFoundError; + return error; } const casted = error as ModuleNotFoundError; @@ -295,7 +295,7 @@ export default class Resolver { return name; } - return await Resolver.findNodeModuleAsync(name, { + return Resolver.findNodeModuleAsync(name, { basedir: dirname, conditions: options?.conditions, extensions, @@ -636,12 +636,7 @@ export default class Resolver { ); return isModuleResolved ? this.getModule(moduleName) - : await this._getVirtualMockPathAsync( - virtualMocks, - from, - moduleName, - options, - ); + : this._getVirtualMockPathAsync(virtualMocks, from, moduleName, options); } private _getMockPath(from: string, moduleName: string): string | null { @@ -655,7 +650,7 @@ export default class Resolver { moduleName: string, ): Promise { return !this.isCoreModule(moduleName) - ? await this.getMockModuleAsync(from, moduleName) + ? this.getMockModuleAsync(from, moduleName) : null; } @@ -683,7 +678,7 @@ export default class Resolver { return virtualMocks.get(virtualMockPath) ? virtualMockPath : moduleName - ? await this.resolveModuleAsync(from, moduleName, options) + ? this.resolveModuleAsync(from, moduleName, options) : from; } diff --git a/packages/jest-runner/src/index.ts b/packages/jest-runner/src/index.ts index c646c432232c..759a91529b1c 100644 --- a/packages/jest-runner/src/index.ts +++ b/packages/jest-runner/src/index.ts @@ -47,9 +47,9 @@ export default class TestRunner extends EmittingTestRunner { watcher: TestWatcher, options: TestRunnerOptions, ): Promise { - return await (options.serial + return options.serial ? this.#createInBandTestRun(tests, watcher) - : this.#createParallelTestRun(tests, watcher)); + : this.#createParallelTestRun(tests, watcher); } async #createInBandTestRun(tests: Array, watcher: TestWatcher) { diff --git a/packages/jest-runner/src/runTest.ts b/packages/jest-runner/src/runTest.ts index 40b69f771207..8a1d045f27e2 100644 --- a/packages/jest-runner/src/runTest.ts +++ b/packages/jest-runner/src/runTest.ts @@ -198,6 +198,16 @@ async function runTestInternal( path, ); + let isTornDown = false; + + const tearDownEnv = async () => { + if (!isTornDown) { + runtime.teardown(); + await environment.teardown(); + isTornDown = true; + } + }; + const start = Date.now(); for (const path of projectConfig.setupFiles) { @@ -347,13 +357,14 @@ async function runTestInternal( result.memoryUsage = process.memoryUsage().heapUsed; } + await tearDownEnv(); + // Delay the resolution to allow log messages to be output. - return new Promise(resolve => { + return await new Promise(resolve => { setImmediate(() => resolve({leakDetector, result})); }); } finally { - runtime.teardown(); - await environment.teardown(); + await tearDownEnv(); sourcemapSupport.resetRetrieveHandlers(); } diff --git a/packages/jest-runtime/src/__tests__/runtime_require_resolve.test.ts b/packages/jest-runtime/src/__tests__/runtime_require_resolve.test.ts index 35976982beff..afa8e888a633 100644 --- a/packages/jest-runtime/src/__tests__/runtime_require_resolve.test.ts +++ b/packages/jest-runtime/src/__tests__/runtime_require_resolve.test.ts @@ -19,7 +19,7 @@ let createRuntime: ( ) => Promise; const getTmpDir = async () => - await fs.mkdtemp(path.join(os.tmpdir(), 'jest-resolve-test-')); + fs.mkdtemp(path.join(os.tmpdir(), 'jest-resolve-test-')); describe('Runtime require.resolve', () => { beforeEach(() => { diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index d3b6d59fefd3..5b64adeb2ea2 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -171,7 +171,7 @@ export default class Runtime { private readonly _mockMetaDataCache: Map>; private _mockRegistry: Map; private _isolatedMockRegistry: Map | null; - private _moduleMockRegistry: Map; + private readonly _moduleMockRegistry: Map; private readonly _moduleMockFactories: Map unknown>; private readonly _moduleMocker: ModuleMocker; private _isolatedModuleRegistry: ModuleRegistry | null; diff --git a/packages/jest-snapshot/src/SnapshotResolver.ts b/packages/jest-snapshot/src/SnapshotResolver.ts index 76e879ed8c4d..e0fd4c37cb42 100644 --- a/packages/jest-snapshot/src/SnapshotResolver.ts +++ b/packages/jest-snapshot/src/SnapshotResolver.ts @@ -52,7 +52,7 @@ async function createSnapshotResolver( snapshotResolverPath?: string | null, ): Promise { return typeof snapshotResolverPath === 'string' - ? await createCustomSnapshotResolver(snapshotResolverPath, localRequire) + ? createCustomSnapshotResolver(snapshotResolverPath, localRequire) : createDefaultSnapshotResolver(); } diff --git a/packages/jest-source-map/src/getCallsite.ts b/packages/jest-source-map/src/getCallsite.ts index 930e5c1eb5bb..19134bdcba4c 100644 --- a/packages/jest-source-map/src/getCallsite.ts +++ b/packages/jest-source-map/src/getCallsite.ts @@ -15,15 +15,15 @@ const addSourceMapConsumer = ( callsite: callsites.CallSite, tracer: TraceMap, ) => { - const getLineNumber = callsite.getLineNumber; - const getColumnNumber = callsite.getColumnNumber; + const getLineNumber = callsite.getLineNumber.bind(callsite); + const getColumnNumber = callsite.getColumnNumber.bind(callsite); let position: ReturnType | null = null; function getPosition() { if (!position) { position = originalPositionFor(tracer, { - column: getColumnNumber.call(callsite) || -1, - line: getLineNumber.call(callsite) || -1, + column: getColumnNumber() ?? -1, + line: getLineNumber() ?? -1, }); } @@ -33,13 +33,16 @@ const addSourceMapConsumer = ( Object.defineProperties(callsite, { getColumnNumber: { value() { - return getPosition().column || getColumnNumber.call(callsite); + const value = getPosition().column; + return value == null || value === 0 ? getColumnNumber() : value; }, writable: false, }, getLineNumber: { value() { - return getPosition().line || getLineNumber.call(callsite); + const value = getPosition().line; + + return value == null || value === 0 ? getLineNumber() : value; }, writable: false, }, @@ -52,9 +55,9 @@ export default function getCallsite( ): callsites.CallSite { const levelAfterThisCall = level + 1; const stack = callsites()[levelAfterThisCall]; - const sourceMapFileName = sourceMaps?.get(stack.getFileName() || ''); + const sourceMapFileName = sourceMaps?.get(stack.getFileName() ?? ''); - if (sourceMapFileName) { + if (sourceMapFileName != null && sourceMapFileName !== '') { try { const sourceMap = readFileSync(sourceMapFileName, 'utf8'); addSourceMapConsumer(stack, new TraceMap(sourceMap)); diff --git a/packages/jest-test-sequencer/src/index.ts b/packages/jest-test-sequencer/src/index.ts index 2a643c4f777d..e21dc2028ef1 100644 --- a/packages/jest-test-sequencer/src/index.ts +++ b/packages/jest-test-sequencer/src/index.ts @@ -38,7 +38,7 @@ export type ShardOptions = { * is called to store/update this information on the cache map. */ export default class TestSequencer { - private _cache: Map = new Map(); + private readonly _cache: Map = new Map(); _getCachePath(testContext: TestContext): string { const {config} = testContext; diff --git a/packages/jest-transform/src/ScriptTransformer.ts b/packages/jest-transform/src/ScriptTransformer.ts index 1cfaaca71ec6..53e83bbce750 100644 --- a/packages/jest-transform/src/ScriptTransformer.ts +++ b/packages/jest-transform/src/ScriptTransformer.ts @@ -797,7 +797,7 @@ class ScriptTransformer { const cbResult = callback(module); if (isPromise(cbResult)) { - return waitForPromiseWithCleanup(cbResult, revertHook).then( + return await waitForPromiseWithCleanup(cbResult, revertHook).then( () => module, ); } diff --git a/packages/jest-watcher/src/JestHooks.ts b/packages/jest-watcher/src/JestHooks.ts index 0e49c7681ca7..d65b1bdbdf4b 100644 --- a/packages/jest-watcher/src/JestHooks.ts +++ b/packages/jest-watcher/src/JestHooks.ts @@ -19,14 +19,14 @@ type AvailableHooks = | 'shouldRunTestSuite'; class JestHooks { - private _listeners: { + private readonly _listeners: { onFileChange: Array; onTestRunComplete: Array; shouldRunTestSuite: Array; }; - private _subscriber: JestHookSubscriber; - private _emitter: JestHookEmitter; + private readonly _subscriber: JestHookSubscriber; + private readonly _emitter: JestHookEmitter; constructor() { this._listeners = { diff --git a/packages/jest-watcher/src/TestWatcher.ts b/packages/jest-watcher/src/TestWatcher.ts index af91597dddc1..a21a11fd63b2 100644 --- a/packages/jest-watcher/src/TestWatcher.ts +++ b/packages/jest-watcher/src/TestWatcher.ts @@ -13,7 +13,7 @@ type State = { export default class TestWatcher extends Emittery<{change: State}> { state: State; - private _isWatchMode: boolean; + private readonly _isWatchMode: boolean; constructor({isWatchMode}: {isWatchMode: boolean}) { super(); diff --git a/packages/jest-watcher/src/lib/Prompt.ts b/packages/jest-watcher/src/lib/Prompt.ts index af857770910f..ad23881af6ad 100644 --- a/packages/jest-watcher/src/lib/Prompt.ts +++ b/packages/jest-watcher/src/lib/Prompt.ts @@ -33,7 +33,7 @@ export default class Prompt { /* eslint-enable */ } - private _onResize = (): void => { + private readonly _onResize = (): void => { this._onChange(); }; diff --git a/packages/jest-worker/src/Farm.ts b/packages/jest-worker/src/Farm.ts index a490443e6035..1029bad4b66a 100644 --- a/packages/jest-worker/src/Farm.ts +++ b/packages/jest-worker/src/Farm.ts @@ -31,8 +31,8 @@ export default class Farm { private readonly _taskQueue: TaskQueue; constructor( - private _numOfWorkers: number, - private _callback: WorkerCallback, + private readonly _numOfWorkers: number, + private readonly _callback: WorkerCallback, options: WorkerFarmOptions = {}, ) { this._computeWorkerKey = options.computeWorkerKey; diff --git a/packages/jest-worker/src/FifoQueue.ts b/packages/jest-worker/src/FifoQueue.ts index 61eed878e8a2..3eb4314d216b 100644 --- a/packages/jest-worker/src/FifoQueue.ts +++ b/packages/jest-worker/src/FifoQueue.ts @@ -27,7 +27,7 @@ type WorkerQueueValue = { export default class FifoQueue implements TaskQueue { private _workerQueues: Array | undefined> = []; - private _sharedQueue = new InternalQueue(); + private readonly _sharedQueue = new InternalQueue(); enqueue(task: QueueChildMessage, workerId?: number): void { if (workerId == null) { diff --git a/packages/jest-worker/src/PriorityQueue.ts b/packages/jest-worker/src/PriorityQueue.ts index 9a5c2cf95385..5dea1b5618c1 100644 --- a/packages/jest-worker/src/PriorityQueue.ts +++ b/packages/jest-worker/src/PriorityQueue.ts @@ -28,9 +28,9 @@ type QueueItem = { */ export default class PriorityQueue implements TaskQueue { private _queue: Array> = []; - private _sharedQueue = new MinHeap(); + private readonly _sharedQueue = new MinHeap(); - constructor(private _computePriority: ComputeTaskPriorityCallback) {} + constructor(private readonly _computePriority: ComputeTaskPriorityCallback) {} enqueue(task: QueueChildMessage, workerId?: number): void { if (workerId == null) { @@ -86,7 +86,7 @@ type HeapItem = { }; class MinHeap { - private _heap: Array = []; + private readonly _heap: Array = []; peek(): TItem | null { return this._heap[0] ?? null; diff --git a/packages/jest-worker/src/index.ts b/packages/jest-worker/src/index.ts index e8a531f6458b..25fb79e55ca2 100644 --- a/packages/jest-worker/src/index.ts +++ b/packages/jest-worker/src/index.ts @@ -81,9 +81,9 @@ function getExposedMethods( */ export class Worker { private _ending: boolean; - private _farm: Farm; - private _options: WorkerFarmOptions; - private _workerPool: WorkerPoolInterface; + private readonly _farm: Farm; + private readonly _options: WorkerFarmOptions; + private readonly _workerPool: WorkerPoolInterface; constructor(workerPath: string, options?: WorkerFarmOptions) { this._options = {...options}; diff --git a/packages/jest-worker/src/workers/ChildProcessWorker.ts b/packages/jest-worker/src/workers/ChildProcessWorker.ts index 9706a0235f56..e3999fa49e1a 100644 --- a/packages/jest-worker/src/workers/ChildProcessWorker.ts +++ b/packages/jest-worker/src/workers/ChildProcessWorker.ts @@ -58,7 +58,7 @@ export default class ChildProcessWorker implements WorkerInterface { private _child!: ChildProcess; - private _options: WorkerOptions; + private readonly _options: WorkerOptions; private _request: ChildMessage | null; private _retries!: number; @@ -74,10 +74,10 @@ export default class ChildProcessWorker private _resolveMemoryUsage: ((arg0: number) => void) | undefined; private _childIdleMemoryUsage: number | null; - private _childIdleMemoryUsageLimit: number | null; + private readonly _childIdleMemoryUsageLimit: number | null; private _memoryUsageCheck = false; - private _childWorkerPath: string; + private readonly _childWorkerPath: string; constructor(options: WorkerOptions) { super(options); diff --git a/packages/jest-worker/src/workers/NodeThreadsWorker.ts b/packages/jest-worker/src/workers/NodeThreadsWorker.ts index 8735404ed243..c6c0584c6ec2 100644 --- a/packages/jest-worker/src/workers/NodeThreadsWorker.ts +++ b/packages/jest-worker/src/workers/NodeThreadsWorker.ts @@ -32,7 +32,7 @@ export default class ExperimentalWorker implements WorkerInterface { private _worker!: Worker; - private _options: WorkerOptions; + private readonly _options: WorkerOptions; private _request: ChildMessage | null; private _retries!: number; @@ -45,10 +45,10 @@ export default class ExperimentalWorker private _memoryUsagePromise: Promise | undefined; private _resolveMemoryUsage: ((arg0: number) => void) | undefined; - private _childWorkerPath: string; + private readonly _childWorkerPath: string; private _childIdleMemoryUsage: number | null; - private _childIdleMemoryUsageLimit: number | null; + private readonly _childIdleMemoryUsageLimit: number | null; private _memoryUsageCheck = false; constructor(options: WorkerOptions) { diff --git a/packages/jest-worker/src/workers/processChild.ts b/packages/jest-worker/src/workers/processChild.ts index e1ed99cd7e94..9ce8952a0e9d 100644 --- a/packages/jest-worker/src/workers/processChild.ts +++ b/packages/jest-worker/src/workers/processChild.ts @@ -139,7 +139,7 @@ function execMethod(method: string, args: Array): void { let fn: UnknownFunction; if (method === 'default') { - fn = main.__esModule ? main['default'] : main; + fn = main.__esModule ? main.default : main; } else { fn = main[method]; } diff --git a/packages/jest-worker/src/workers/threadChild.ts b/packages/jest-worker/src/workers/threadChild.ts index c948c7441778..a57d8205cb3b 100644 --- a/packages/jest-worker/src/workers/threadChild.ts +++ b/packages/jest-worker/src/workers/threadChild.ts @@ -141,7 +141,7 @@ function execMethod(method: string, args: Array): void { let fn: UnknownFunction; if (method === 'default') { - fn = main.__esModule ? main['default'] : main; + fn = main.__esModule ? main.default : main; } else { fn = main[method]; } diff --git a/packages/pretty-format/src/__tests__/AsymmetricMatcher.test.ts b/packages/pretty-format/src/__tests__/AsymmetricMatcher.test.ts index 95354dbeeadd..e7c228dd42f9 100644 --- a/packages/pretty-format/src/__tests__/AsymmetricMatcher.test.ts +++ b/packages/pretty-format/src/__tests__/AsymmetricMatcher.test.ts @@ -351,7 +351,7 @@ test('min option', () => { class DummyMatcher { $$typeof = Symbol.for('jest.asymmetricMatcher'); - constructor(private sample: number) {} + constructor(private readonly sample: number) {} asymmetricMatch(other: number) { return this.sample === other; diff --git a/scripts/buildTs.mjs b/scripts/buildTs.mjs index 903ec3f9a601..d6513277bda1 100644 --- a/scripts/buildTs.mjs +++ b/scripts/buildTs.mjs @@ -14,13 +14,9 @@ import globby from 'globby'; import fs from 'graceful-fs'; import pLimit from 'p-limit'; import stripJsonComments from 'strip-json-comments'; -import {getPackages} from './buildUtils.mjs'; +import {getPackagesWithTsConfig} from './buildUtils.mjs'; -const packages = getPackages(); - -const packagesWithTs = packages.filter(p => - fs.existsSync(path.resolve(p.packageDir, 'tsconfig.json')), -); +const packagesWithTs = getPackagesWithTsConfig(); const {stdout: allWorkspacesString} = await execa('yarn', [ 'workspaces', diff --git a/scripts/buildUtils.mjs b/scripts/buildUtils.mjs index f03b9c42ac15..04fea52eeea5 100644 --- a/scripts/buildUtils.mjs +++ b/scripts/buildUtils.mjs @@ -121,3 +121,9 @@ export function adjustToTerminalWidth(str) { } return strs.slice(0, -1).concat(lastString).join('\n'); } + +export function getPackagesWithTsConfig() { + return getPackages().filter(p => + fs.existsSync(path.resolve(p.packageDir, 'tsconfig.json')), + ); +} diff --git a/scripts/lintTs.mjs b/scripts/lintTs.mjs new file mode 100644 index 000000000000..149ba0c53e5f --- /dev/null +++ b/scripts/lintTs.mjs @@ -0,0 +1,120 @@ +/** + * 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 * as os from 'os'; +import * as path from 'path'; +import * as url from 'url'; +import chalk from 'chalk'; +import {ESLint} from 'eslint'; +import pLimit from 'p-limit'; +import {getPackagesWithTsConfig} from './buildUtils.mjs'; + +// we want to limit the number of processes we spawn +const cpus = Math.max(1, os.cpus().length - 1); +const mutex = pLimit(cpus); + +const fix = process.argv.slice(2).some(arg => arg === '--fix'); + +const monorepoRoot = path.resolve(url.fileURLToPath(import.meta.url), '../..'); + +// TODO: remove this list at some point and run against all packages +const packagesToTest = [ + 'babel-jest', + 'babel-plugin-jest-hoist', + 'diff-sequences', + 'jest-source-map', +]; + +const packagesWithTs = getPackagesWithTsConfig() + .map(({packageDir}) => packageDir) + .concat(path.resolve(monorepoRoot, 'e2e')) + .filter(packageDir => packagesToTest.some(pkg => packageDir.endsWith(pkg))); + +const allLintResults = []; + +try { + await Promise.all( + packagesWithTs.map(packageDir => + mutex(async () => { + const eslint = new ESLint({ + cache: true, + cacheLocation: path.resolve(packageDir, '.eslintcache'), + cwd: monorepoRoot, + extensions: ['.ts'], + fix, + fixTypes: ['problem', 'suggestion', 'layout'], + overrideConfig: { + extends: [ + 'plugin:@typescript-eslint/recommended-requiring-type-checking', + ], + parser: '@typescript-eslint/parser', + parserOptions: { + project: ['./tsconfig.json', `${packageDir}/tsconfig.json`], + tsconfigRootDir: monorepoRoot, + }, + plugins: ['@typescript-eslint'], + root: true, + rules: { + '@typescript-eslint/consistent-type-exports': 'error', + '@typescript-eslint/dot-notation': 'error', + '@typescript-eslint/non-nullable-type-assertion-style': 'error', + '@typescript-eslint/prefer-nullish-coalescing': 'error', + '@typescript-eslint/prefer-readonly': 'error', + // TODO: activate this at some point + // '@typescript-eslint/prefer-readonly-parameter-types': 'error', + '@typescript-eslint/prefer-reduce-type-parameter': 'error', + '@typescript-eslint/return-await': 'error', + '@typescript-eslint/strict-boolean-expressions': 'error', + '@typescript-eslint/switch-exhaustiveness-check': 'error', + }, + }, + }); + + const filesToLint = packageDir.endsWith('e2e') + ? `${packageDir}/__tests__/` + : `${packageDir}/src/`; + + let results = await eslint.lintFiles(filesToLint); + + if (fix) { + await ESLint.outputFixes(results); + + // re-run after outputting fixes + results = await eslint.lintFiles(filesToLint); + } + + const filteredResults = ESLint.getErrorResults(results); + + allLintResults.push(...filteredResults); + }), + ), + ); +} catch (e) { + console.error( + chalk.inverse.red(' Unable to lint using TypeScript info files '), + ); + + throw e; +} + +if (allLintResults.length > 0) { + const eslint = new ESLint({cwd: monorepoRoot}); + const formatter = await eslint.loadFormatter('stylish'); + const resultText = formatter.format(allLintResults); + + console.error(resultText); + + console.error( + chalk.inverse.red(' Unable to lint using TypeScript info files '), + ); + + process.exitCode = 1; +} else { + console.log( + chalk.inverse.green(' Successfully linted using TypeScript info files '), + ); +}