diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 91e621ed7a41..ef35b0800df5 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -35,13 +35,19 @@ module.exports = { 'plugin:eslint-comments/recommended', 'plugin:prettier/recommended', 'plugin:unicorn/recommended', + 'plugin:promise/recommended', ], globals: { console: 'readonly', }, overrides: [ { - extends: ['plugin:@typescript-eslint/strict', 'plugin:import/typescript'], + extends: [ + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:@typescript-eslint/strict', + 'plugin:@typescript-eslint/stylistic', + 'plugin:import/typescript', + ], files: ['*.ts', '*.tsx'], plugins: ['@typescript-eslint/eslint-plugin', 'local'], rules: { @@ -59,6 +65,7 @@ module.exports = { ], '@typescript-eslint/prefer-ts-expect-error': 'error', '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/consistent-indexed-object-style': 'off', // TS verifies these 'consistent-return': 'off', 'no-dupe-class-members': 'off', @@ -68,10 +75,7 @@ module.exports = { '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-non-null-assertion': 'off', '@typescript-eslint/no-invalid-void-type': 'off', - - // TODO: part of "stylistic" rules, remove explicit activation when that lands - '@typescript-eslint/no-empty-function': 'error', - '@typescript-eslint/no-empty-interface': 'error', + '@typescript-eslint/consistent-type-definitions': 'off', // not needed to be enforced for TS 'import/namespace': 'off', @@ -326,6 +330,7 @@ module.exports = { rules: { '@typescript-eslint/ban-ts-comment': 'off', '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/class-literal-property-style': 'off', }, }, { @@ -427,6 +432,12 @@ module.exports = { 'unicorn/no-static-only-class': 'off', }, }, + { + files: '**/*.mjs', + rules: { + 'unicorn/prefer-top-level-await': 'error', + }, + }, ], parser: '@typescript-eslint/parser', parserOptions: { @@ -617,6 +628,11 @@ module.exports = { 'prefer-arrow-callback': ['error', {allowNamedFunctions: true}], 'prefer-const': 'error', 'prefer-template': 'error', + + 'promise/always-return': 'off', + 'promise/catch-or-return': 'off', + 'promise/no-callback-in-promise': 'off', + quotes: [ 'error', 'single', @@ -655,10 +671,8 @@ module.exports = { 'unicorn/prefer-event-target': 'off', 'unicorn/prefer-switch': 'off', 'unicorn/prefer-ternary': 'off', - 'unicorn/switch-case-braces': 'off', - - // TODO: enable for `.mjs` files 'unicorn/prefer-top-level-await': 'off', + 'unicorn/switch-case-braces': 'off', // TODO: decide whether or not we want these 'unicorn/filename-case': 'off', diff --git a/e2e/jasmine-async/__tests__/concurrent-parallel-failure.test.js b/e2e/jasmine-async/__tests__/concurrent-parallel-failure.test.js index c2bf6975132f..42b953b2a21d 100644 --- a/e2e/jasmine-async/__tests__/concurrent-parallel-failure.test.js +++ b/e2e/jasmine-async/__tests__/concurrent-parallel-failure.test.js @@ -8,7 +8,7 @@ 'use strict'; it.concurrent('Good Test', async () => { - await new Promise(r => setTimeout(r, 100)); + await new Promise(resolve => setTimeout(resolve, 100)); }); it.concurrent('Bad Test', async () => { diff --git a/e2e/snapshot-concurrent/__tests__/works.test.js b/e2e/snapshot-concurrent/__tests__/works.test.js index dd59e404cf88..1aaf331249a1 100644 --- a/e2e/snapshot-concurrent/__tests__/works.test.js +++ b/e2e/snapshot-concurrent/__tests__/works.test.js @@ -7,7 +7,7 @@ */ 'use strict'; -const sleep = ms => new Promise(r => setTimeout(r, ms)); +const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); describe('A', () => { it.concurrent('a', async () => { diff --git a/package.json b/package.json index 60c19cc57b3f..788a59614eec 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "eslint-plugin-local": "link:./.eslintplugin", "eslint-plugin-markdown": "^3.0.0", "eslint-plugin-prettier": "^5.0.0", + "eslint-plugin-promise": "^6.1.1", "eslint-plugin-unicorn": "^50.0.0", "execa": "^5.0.0", "find-process": "^1.4.1", diff --git a/packages/babel-plugin-jest-hoist/src/index.ts b/packages/babel-plugin-jest-hoist/src/index.ts index 43a34b120ee1..7f8e5ffaf97b 100644 --- a/packages/babel-plugin-jest-hoist/src/index.ts +++ b/packages/babel-plugin-jest-hoist/src/index.ts @@ -133,7 +133,7 @@ FUNCTIONS.mock = args => { ); } - const ids: Set> = new Set(); + const ids = new Set>(); const parentScope = moduleFactory.parentPath.scope; // @ts-expect-error: ReferencedIdentifier and denylist are not known on visitors moduleFactory.traverse(IDVisitor, {ids}); diff --git a/packages/expect-utils/src/jasmineUtils.ts b/packages/expect-utils/src/jasmineUtils.ts index 3f70f308054d..e5f32eed4dd9 100644 --- a/packages/expect-utils/src/jasmineUtils.ts +++ b/packages/expect-utils/src/jasmineUtils.ts @@ -76,13 +76,8 @@ function eq( } const testerContext: TesterContext = {equals}; - for (let i = 0; i < customTesters.length; i++) { - const customTesterResult = customTesters[i].call( - testerContext, - a, - b, - customTesters, - ); + for (const item of customTesters) { + const customTesterResult = item.call(testerContext, a, b, customTesters); if (customTesterResult !== undefined) { return customTesterResult; } diff --git a/packages/expect/src/index.ts b/packages/expect/src/index.ts index 8fdd5d71adee..ab752b73570c 100644 --- a/packages/expect/src/index.ts +++ b/packages/expect/src/index.ts @@ -203,7 +203,7 @@ const makeResolveMatcher = )}\n\n` + 'Received promise rejected instead of resolved\n' + `Rejected to value: ${matcherUtils.printReceived(reason)}`; - return Promise.reject(outerErr); + throw outerErr; }, ); }; @@ -254,7 +254,7 @@ const makeRejectMatcher = )}\n\n` + 'Received promise resolved instead of rejected\n' + `Resolved to value: ${matcherUtils.printReceived(result)}`; - return Promise.reject(outerErr); + throw outerErr; }, reason => makeThrowingMatcher(matcher, isNot, 'rejects', reason, innerErr).apply( diff --git a/packages/expect/src/types.ts b/packages/expect/src/types.ts index c40f64acd593..d29bc6b4a92c 100644 --- a/packages/expect/src/types.ts +++ b/packages/expect/src/types.ts @@ -98,11 +98,10 @@ export interface BaseExpect { setState(state: Partial): void; } -export type Expect = { - ( - actual: T, - ): Matchers & Inverse> & PromiseMatchers; -} & BaseExpect & +export type Expect = (( + actual: T, +) => Matchers & Inverse> & PromiseMatchers) & + BaseExpect & AsymmetricMatchers & Inverse>; diff --git a/packages/jest-core/src/__tests__/collectHandles.test.js b/packages/jest-core/src/__tests__/collectHandles.test.js index 0c77e7783e11..905c5b83e097 100644 --- a/packages/jest-core/src/__tests__/collectHandles.test.js +++ b/packages/jest-core/src/__tests__/collectHandles.test.js @@ -113,8 +113,8 @@ describe('collectHandles', () => { const server = http.createServer((_, response) => response.end('ok')); // Start and stop server. - await new Promise(r => server.listen(0, r)); - await new Promise(r => server.close(r)); + await new Promise(resolve => server.listen(0, resolve)); + await new Promise(resolve => server.close(resolve)); const openHandles = await handleCollector(); expect(openHandles).toHaveLength(0); @@ -130,11 +130,13 @@ describe('collectHandles', () => { // creates a long-lived `TCPSERVERWRAP` resource. We want to make sure we // capture that long-lived resource. const server = new http.Server(); - await new Promise(r => server.listen({host: 'localhost', port: 0}, r)); + await new Promise(resolve => + server.listen({host: 'localhost', port: 0}, resolve), + ); const openHandles = await handleCollector(); - await new Promise(r => server.close(r)); + await new Promise(resolve => server.close(resolve)); expect(openHandles).toContainEqual( expect.objectContaining({message: 'TCPSERVERWRAP'}), diff --git a/packages/jest-core/src/__tests__/watch.test.js b/packages/jest-core/src/__tests__/watch.test.js index 4425b8285800..41e565d5f6df 100644 --- a/packages/jest-core/src/__tests__/watch.test.js +++ b/packages/jest-core/src/__tests__/watch.test.js @@ -88,7 +88,7 @@ const regularUpdateGlobalConfig = require('../lib/updateGlobalConfig').default; const updateGlobalConfig = jest.fn(regularUpdateGlobalConfig); jest.doMock('../lib/updateGlobalConfig', () => updateGlobalConfig); -const nextTick = () => new Promise(res => process.nextTick(res)); +const nextTick = () => new Promise(resolve => process.nextTick(resolve)); beforeAll(() => { jest.spyOn(process, 'on').mockImplementation(() => {}); @@ -771,7 +771,9 @@ describe('Watch mode flows', () => { it('prevents Jest from handling keys when active and returns control when end is called', async () => { let resolveShowPrompt; - const run = jest.fn(() => new Promise(res => (resolveShowPrompt = res))); + const run = jest.fn( + () => new Promise(resolve => (resolveShowPrompt = resolve)), + ); const pluginPath = `${__dirname}/__fixtures__/plugin_path_1`; jest.doMock( pluginPath, diff --git a/packages/jest-core/src/__tests__/watchFilenamePatternMode.test.js b/packages/jest-core/src/__tests__/watchFilenamePatternMode.test.js index 2900fe6d02e8..89e70617d494 100644 --- a/packages/jest-core/src/__tests__/watchFilenamePatternMode.test.js +++ b/packages/jest-core/src/__tests__/watchFilenamePatternMode.test.js @@ -68,7 +68,7 @@ jest.doMock( const watch = require('../watch').default; -const nextTick = () => new Promise(res => process.nextTick(res)); +const nextTick = () => new Promise(resolve => process.nextTick(resolve)); const globalConfig = { rootDir: '', diff --git a/packages/jest-core/src/plugins/TestNamePattern.ts b/packages/jest-core/src/plugins/TestNamePattern.ts index 64d31a786026..d74762edc1fc 100644 --- a/packages/jest-core/src/plugins/TestNamePattern.ts +++ b/packages/jest-core/src/plugins/TestNamePattern.ts @@ -41,7 +41,7 @@ class TestNamePatternPlugin extends BaseWatchPlugin { globalConfig: Config.GlobalConfig, updateConfigAndRun: UpdateConfigCallback, ): Promise { - return new Promise((res, rej) => { + return new Promise((resolve, reject) => { const testNamePatternPrompt = new TestNamePatternPrompt( this._stdout, this._prompt, @@ -50,9 +50,9 @@ class TestNamePatternPlugin extends BaseWatchPlugin { testNamePatternPrompt.run( (value: string) => { updateConfigAndRun({mode: 'watch', testNamePattern: value}); - res(); + resolve(); }, - rej, + reject, { header: activeFilters(globalConfig), }, diff --git a/packages/jest-core/src/plugins/TestPathPattern.ts b/packages/jest-core/src/plugins/TestPathPattern.ts index 78c88fcb54c4..9163e20d29b9 100644 --- a/packages/jest-core/src/plugins/TestPathPattern.ts +++ b/packages/jest-core/src/plugins/TestPathPattern.ts @@ -41,7 +41,7 @@ class TestPathPatternPlugin extends BaseWatchPlugin { globalConfig: Config.GlobalConfig, updateConfigAndRun: UpdateConfigCallback, ): Promise { - return new Promise((res, rej) => { + return new Promise((resolve, reject) => { const testPathPatternPrompt = new TestPathPatternPrompt( this._stdout, this._prompt, @@ -50,9 +50,9 @@ class TestPathPatternPlugin extends BaseWatchPlugin { testPathPatternPrompt.run( (value: string) => { updateConfigAndRun({mode: 'watch', testPathPatterns: [value]}); - res(); + resolve(); }, - rej, + reject, { header: activeFilters(globalConfig), }, diff --git a/packages/jest-core/src/plugins/UpdateSnapshotsInteractive.ts b/packages/jest-core/src/plugins/UpdateSnapshotsInteractive.ts index da1c7dc56280..5e975371ef88 100644 --- a/packages/jest-core/src/plugins/UpdateSnapshotsInteractive.ts +++ b/packages/jest-core/src/plugins/UpdateSnapshotsInteractive.ts @@ -67,7 +67,7 @@ class UpdateSnapshotInteractivePlugin extends BaseWatchPlugin { updateConfigAndRun: Function, ): Promise { if (this._failedSnapshotTestAssertions.length > 0) { - return new Promise(res => { + return new Promise(resolve => { this._snapshotInteractiveMode.run( this._failedSnapshotTestAssertions, (assertion, shouldUpdateSnapshot) => { @@ -79,7 +79,7 @@ class UpdateSnapshotInteractivePlugin extends BaseWatchPlugin { updateSnapshot: shouldUpdateSnapshot ? 'all' : 'none', }); if (!this._snapshotInteractiveMode.isActive()) { - res(); + resolve(); } }, ); diff --git a/packages/jest-docblock/src/index.ts b/packages/jest-docblock/src/index.ts index 39839778aa41..ae4525f4824b 100644 --- a/packages/jest-docblock/src/index.ts +++ b/packages/jest-docblock/src/index.ts @@ -27,8 +27,9 @@ export function extract(contents: string): string { } export function strip(contents: string): string { - const match = contents.match(docblockRe); - return match && match[0] ? contents.slice(match[0].length) : contents; + const matchResult = contents.match(docblockRe); + const match = matchResult?.[0]; + return match == null ? contents : contents.slice(match.length); } export function parse(docblock: string): Pragmas { diff --git a/packages/jest-haste-map/src/blacklist.ts b/packages/jest-haste-map/src/blacklist.ts index c272c995814f..19a17f916f11 100644 --- a/packages/jest-haste-map/src/blacklist.ts +++ b/packages/jest-haste-map/src/blacklist.ts @@ -15,7 +15,7 @@ // // Feel free to add any extensions that cannot be a Haste module. -const extensions: Set = new Set([ +const extensions = new Set([ // JSONs are never haste modules, except for "package.json", which is handled. '.json', diff --git a/packages/jest-jasmine2/src/jasmine/CallTracker.ts b/packages/jest-jasmine2/src/jasmine/CallTracker.ts index 030d95fb2da3..0409d71d2344 100644 --- a/packages/jest-jasmine2/src/jasmine/CallTracker.ts +++ b/packages/jest-jasmine2/src/jasmine/CallTracker.ts @@ -71,9 +71,9 @@ class CallTracker { }; this.allArgs = function () { - const callArgs = []; - for (let i = 0; i < calls.length; i++) { - callArgs.push(calls[i].args); + const callArgs: Array = []; + for (const call of calls) { + callArgs.push(call.args); } return callArgs; diff --git a/packages/jest-jasmine2/src/jasmine/ReportDispatcher.ts b/packages/jest-jasmine2/src/jasmine/ReportDispatcher.ts index 9b2323f50ce2..b0bacf02e166 100644 --- a/packages/jest-jasmine2/src/jasmine/ReportDispatcher.ts +++ b/packages/jest-jasmine2/src/jasmine/ReportDispatcher.ts @@ -56,8 +56,7 @@ export default class ReportDispatcher implements Reporter { constructor(methods: Array) { const dispatchedMethods = methods || []; - for (let i = 0; i < dispatchedMethods.length; i++) { - const method = dispatchedMethods[i]; + for (const method of dispatchedMethods) { this[method] = (function (m) { return function () { dispatch(m, arguments); @@ -86,8 +85,7 @@ export default class ReportDispatcher implements Reporter { if (reporters.length === 0 && fallbackReporter !== null) { reporters.push(fallbackReporter); } - for (let i = 0; i < reporters.length; i++) { - const reporter = reporters[i]; + for (const reporter of reporters) { if (reporter[method]) { // @ts-expect-error: wrong context reporter[method].apply(reporter, args); diff --git a/packages/jest-jasmine2/src/jasmine/Suite.ts b/packages/jest-jasmine2/src/jasmine/Suite.ts index 533174215415..3a4037cc278a 100644 --- a/packages/jest-jasmine2/src/jasmine/Suite.ts +++ b/packages/jest-jasmine2/src/jasmine/Suite.ts @@ -190,8 +190,7 @@ export default class Suite { }; this.result.failedExpectations.push(expectationResultFactory(data)); } else { - for (let i = 0; i < this.children.length; i++) { - const child = this.children[i]; + for (const child of this.children) { child.onException.apply(child, args); } } @@ -205,8 +204,7 @@ export default class Suite { throw new ExpectationFailed(); } } else { - for (let i = 0; i < this.children.length; i++) { - const child = this.children[i]; + for (const child of this.children) { try { child.addExpectationResult.apply(child, args); } catch { diff --git a/packages/jest-matcher-utils/src/deepCyclicCopyReplaceable.ts b/packages/jest-matcher-utils/src/deepCyclicCopyReplaceable.ts index 64cca1335eda..73b9ce0bd4f8 100644 --- a/packages/jest-matcher-utils/src/deepCyclicCopyReplaceable.ts +++ b/packages/jest-matcher-utils/src/deepCyclicCopyReplaceable.ts @@ -36,7 +36,7 @@ const isMap = (value: any): value is Map => export default function deepCyclicCopyReplaceable( value: T, - cycles: WeakMap = new WeakMap(), + cycles = new WeakMap(), ): T { if (typeof value !== 'object' || value === null) { return value; diff --git a/packages/jest-mock/__typetests__/Mocked.test.ts b/packages/jest-mock/__typetests__/Mocked.test.ts index 8bf5e65b6d45..7f67cf8774c4 100644 --- a/packages/jest-mock/__typetests__/Mocked.test.ts +++ b/packages/jest-mock/__typetests__/Mocked.test.ts @@ -101,9 +101,7 @@ interface SomeFunctionObject { one: { (oneA: number, oneB?: boolean): boolean; more: { - time: { - (time: number): void; - }; + time: (time: number) => void; }; }; } diff --git a/packages/jest-mock/src/index.ts b/packages/jest-mock/src/index.ts index 04221b409659..1ab36498fb19 100644 --- a/packages/jest-mock/src/index.ts +++ b/packages/jest-mock/src/index.ts @@ -30,7 +30,7 @@ export type MockMetadata = { length?: number; }; -export type ClassLike = {new (...args: any): any}; +export type ClassLike = new (...args: any) => any; export type FunctionLike = (...args: any) => any; export type ConstructorLikeKeys = keyof { @@ -91,7 +91,7 @@ export type MockedShallow = T extends ClassLike : T; export type UnknownFunction = (...args: Array) => unknown; -export type UnknownClass = {new (...args: Array): unknown}; +export type UnknownClass = new (...args: Array) => unknown; export type SpiedClass = MockInstance< (...args: ConstructorParameters) => InstanceType @@ -533,9 +533,7 @@ export class ModuleMocker { ) { const ownNames = Object.getOwnPropertyNames(object); - for (let i = 0; i < ownNames.length; i++) { - const prop = ownNames[i]; - + for (const prop of ownNames) { if (!isReadonlyProp(object, prop)) { const propDesc = Object.getOwnPropertyDescriptor(object, prop); if ((propDesc !== undefined && !propDesc.get) || object.__esModule) { diff --git a/packages/jest-repl/src/cli/repl.ts b/packages/jest-repl/src/cli/repl.ts index 475f73487a4d..0039fa56a7c3 100644 --- a/packages/jest-repl/src/cli/repl.ts +++ b/packages/jest-repl/src/cli/repl.ts @@ -75,10 +75,10 @@ const isRecoverableError = (error: unknown) => { if (jestProjectConfig.transform) { let transformerPath = null; - for (let i = 0; i < jestProjectConfig.transform.length; i++) { - if (new RegExp(jestProjectConfig.transform[i][0]).test('foobar.js')) { - transformerPath = jestProjectConfig.transform[i][1]; - transformerConfig = jestProjectConfig.transform[i][2]; + for (const transform of jestProjectConfig.transform) { + if (new RegExp(transform[0]).test('foobar.js')) { + transformerPath = transform[1]; + transformerConfig = transform[2]; break; } } diff --git a/packages/jest-repl/src/cli/runtime-cli.ts b/packages/jest-repl/src/cli/runtime-cli.ts index 7a35879cfe6b..a8cdaf11606d 100644 --- a/packages/jest-repl/src/cli/runtime-cli.ts +++ b/packages/jest-repl/src/cli/runtime-cli.ts @@ -33,10 +33,11 @@ export async function run( if (cliArgv) { argv = cliArgv; } else { - argv = ( - yargs.usage(args.usage).help(false).version(false).options(args.options) - .argv - ); + argv = yargs + .usage(args.usage) + .help(false) + .version(false) + .options(args.options).argv as Config.Argv; validateCLIOptions(argv, {...args.options, deprecationEntries}); } diff --git a/packages/jest-reporters/src/GitHubActionsReporter.ts b/packages/jest-reporters/src/GitHubActionsReporter.ts index 72595756df01..be3990ae0752 100644 --- a/packages/jest-reporters/src/GitHubActionsReporter.ts +++ b/packages/jest-reporters/src/GitHubActionsReporter.ts @@ -231,10 +231,8 @@ export default class GitHubActionsReporter extends BaseReporter { }); } else { let alreadyInserted = false; - for (let index = 0; index < branches.length; index++) { - if ( - this.arrayEqual(branches[index], element.ancestorTitles.slice(0, 1)) - ) { + for (const branch of branches) { + if (this.arrayEqual(branch, element.ancestorTitles.slice(0, 1))) { alreadyInserted = true; break; } @@ -286,10 +284,10 @@ export default class GitHubActionsReporter extends BaseReporter { ) ) { let alreadyInserted = false; - for (let index = 0; index < branches.length; index++) { + for (const branch of branches) { if ( this.arrayEqual( - branches[index], + branch, element.ancestorTitles.slice(0, ancestors.length + 1), ) ) { diff --git a/packages/jest-reporters/src/Status.ts b/packages/jest-reporters/src/Status.ts index 2a53af30331f..4be94db624ed 100644 --- a/packages/jest-reporters/src/Status.ts +++ b/packages/jest-reporters/src/Status.ts @@ -192,8 +192,8 @@ export default class Status { let height = 0; - for (let i = 0; i < content.length; i++) { - if (content[i] === '\n') { + for (const char of content) { + if (char === '\n') { height++; } } diff --git a/packages/jest-resolve-dependencies/src/index.ts b/packages/jest-resolve-dependencies/src/index.ts index c4a0ef365b6b..f842c6e1e243 100644 --- a/packages/jest-resolve-dependencies/src/index.ts +++ b/packages/jest-resolve-dependencies/src/index.ts @@ -139,7 +139,7 @@ export class DependencyResolver { }; const relatedPaths = new Set(); - const changed: Set = new Set(); + const changed = new Set(); for (const path of paths) { if (this._hasteFS.exists(path)) { const modulePath = isSnapshotPath(path) diff --git a/packages/jest-runner/src/index.ts b/packages/jest-runner/src/index.ts index a43c2fa85323..c023917797d1 100644 --- a/packages/jest-runner/src/index.ts +++ b/packages/jest-runner/src/index.ts @@ -102,7 +102,7 @@ export default class TestRunner extends EmittingTestRunner { } async #createParallelTestRun(tests: Array, watcher: TestWatcher) { - const resolvers: Map = new Map(); + const resolvers = new Map(); for (const test of tests) { if (!resolvers.has(test.context.config.id)) { resolvers.set(test.context.config.id, { @@ -167,7 +167,7 @@ export default class TestRunner extends EmittingTestRunner { return promise; }); - const onInterrupt = new Promise((_, reject) => { + const onInterrupt = new Promise((_resolve, reject) => { watcher.on('change', state => { if (state.interrupted) { reject(new CancelRun()); diff --git a/packages/jest-transform/src/ScriptTransformer.ts b/packages/jest-transform/src/ScriptTransformer.ts index 0163670a9acd..f8afda252310 100644 --- a/packages/jest-transform/src/ScriptTransformer.ts +++ b/packages/jest-transform/src/ScriptTransformer.ts @@ -254,8 +254,8 @@ class ScriptTransformer { return undefined; } - for (let i = 0; i < transformEntry.length; i++) { - const [transformRegExp, transformPath] = transformEntry[i]; + for (const item of transformEntry) { + const [transformRegExp, transformPath] = item; if (transformRegExp.test(filename)) { return [transformRegExp.source, transformPath]; } @@ -1017,12 +1017,8 @@ const calcTransformRegExp = (config: Config.ProjectConfig) => { } const transformRegexp: Array<[RegExp, string, Record]> = []; - for (let i = 0; i < config.transform.length; i++) { - transformRegexp.push([ - new RegExp(config.transform[i][0]), - config.transform[i][1], - config.transform[i][2], - ]); + for (const item of config.transform) { + transformRegexp.push([new RegExp(item[0]), item[1], item[2]]); } return transformRegexp; diff --git a/packages/jest-types/src/Global.ts b/packages/jest-types/src/Global.ts index f8ec8b573b61..b851af094368 100644 --- a/packages/jest-types/src/Global.ts +++ b/packages/jest-types/src/Global.ts @@ -113,9 +113,7 @@ interface Each { ) => void; } -export interface HookBase { - (fn: HookFn, timeout?: number): void; -} +export type HookBase = (fn: HookFn, timeout?: number) => void; export interface Failing { (testName: TestNameLike, fn: T, timeout?: number): void; diff --git a/packages/jest-util/src/deepCyclicCopy.ts b/packages/jest-util/src/deepCyclicCopy.ts index 10d123858b04..7f759baba9b4 100644 --- a/packages/jest-util/src/deepCyclicCopy.ts +++ b/packages/jest-util/src/deepCyclicCopy.ts @@ -15,7 +15,7 @@ export type DeepCyclicCopyOptions = { export default function deepCyclicCopy( value: T, options: DeepCyclicCopyOptions = {blacklist: EMPTY, keepPrototype: false}, - cycles: WeakMap = new WeakMap(), + cycles = new WeakMap(), ): T { if (typeof value !== 'object' || value === null || Buffer.isBuffer(value)) { return value; diff --git a/packages/jest-util/src/globsToMatcher.ts b/packages/jest-util/src/globsToMatcher.ts index 527c770deb72..c66626939efb 100644 --- a/packages/jest-util/src/globsToMatcher.ts +++ b/packages/jest-util/src/globsToMatcher.ts @@ -63,8 +63,8 @@ export default function globsToMatcher(globs: Array): Matcher { let kept = undefined; let negatives = 0; - for (let i = 0; i < matchers.length; i++) { - const {isMatch, negated} = matchers[i]; + for (const matcher of matchers) { + const {isMatch, negated} = matcher; if (negated) { negatives++; diff --git a/packages/jest-validate/src/condition.ts b/packages/jest-validate/src/condition.ts index b79abc0a91b0..b61fb0357e42 100644 --- a/packages/jest-validate/src/condition.ts +++ b/packages/jest-validate/src/condition.ts @@ -42,7 +42,7 @@ export function validationCondition( export function multipleValidOptions>( ...args: T ): T[number] { - const options = [...args]; + const options = [...args] as T; // @ts-expect-error: no index signature options[MULTIPLE_VALID_OPTIONS_SYMBOL] = true; diff --git a/packages/jest-watcher/src/PatternPrompt.ts b/packages/jest-watcher/src/PatternPrompt.ts index 59bc87176623..c05cd7141486 100644 --- a/packages/jest-watcher/src/PatternPrompt.ts +++ b/packages/jest-watcher/src/PatternPrompt.ts @@ -41,7 +41,7 @@ export default abstract class PatternPrompt { this._pipe.write(ansiEscapes.cursorHide); this._pipe.write(CLEAR); - if (options && options.header) { + if (typeof options?.header === 'string' && options.header) { this._pipe.write(`${options.header}\n`); this._currentUsageRows = usageRows + options.header.split('\n').length; } else { diff --git a/packages/jest-watcher/src/types.ts b/packages/jest-watcher/src/types.ts index 77d16515e557..18c5ba64e41a 100644 --- a/packages/jest-watcher/src/types.ts +++ b/packages/jest-watcher/src/types.ts @@ -81,13 +81,11 @@ export interface WatchPlugin { updateConfigAndRun: UpdateConfigCallback, ) => Promise; } -export interface WatchPluginClass { - new (options: { - config: Record; - stdin: ReadStream; - stdout: WriteStream; - }): WatchPlugin; -} +export type WatchPluginClass = new (options: { + config: Record; + stdin: ReadStream; + stdout: WriteStream; +}) => WatchPlugin; export type ScrollOptions = { offset: number; diff --git a/packages/pretty-format/src/index.ts b/packages/pretty-format/src/index.ts index b0ba1cd2597c..d730c36818d1 100644 --- a/packages/pretty-format/src/index.ts +++ b/packages/pretty-format/src/index.ts @@ -342,10 +342,10 @@ function printPlugin( } function findPlugin(plugins: Plugins, val: unknown) { - for (let p = 0; p < plugins.length; p++) { + for (const plugin of plugins) { try { - if (plugins[p].test(val)) { - return plugins[p]; + if (plugin.test(val)) { + return plugin; } } catch (error: any) { throw new PrettyFormatPluginError(error.message, error.stack); diff --git a/scripts/build.mjs b/scripts/build.mjs index ca9202c9d407..0f919d1a869c 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -93,7 +93,10 @@ async function buildNodePackages() { process.stdout.write(`${OK}\n`); } -buildNodePackages().catch(error => { +try { + await buildNodePackages(); +} catch (error) { + process.stderr.write(`${ERROR}\n`); console.error(error); process.exitCode = 1; -}); +} diff --git a/scripts/lintTs.mjs b/scripts/lintTs.mjs index b79ef9514628..f6a0a090a42e 100644 --- a/scripts/lintTs.mjs +++ b/scripts/lintTs.mjs @@ -85,7 +85,10 @@ try { fix, fixTypes: ['problem', 'suggestion', 'layout'], overrideConfig: { - extends: ['plugin:@typescript-eslint/recommended-type-checked'], + extends: [ + 'plugin:@typescript-eslint/recommended-type-checked', + 'plugin:@typescript-eslint/stylistic-type-checked', + ], overrides: [ { files: ['**/__tests__/**'], @@ -97,6 +100,15 @@ try { 'jest/unbound-method': 'error', }, }, + { + files: 'packages/jest-types/src/Circus.ts', + rules: { + // We're faking nominal types + '@typescript-eslint/no-duplicate-type-constituents': 'off', + // this file has `Exception`, which is `unknown` + '@typescript-eslint/no-redundant-type-constituents': 'off', + }, + }, ], parser: '@typescript-eslint/parser', parserOptions: { @@ -109,6 +121,14 @@ try { rules: { '@typescript-eslint/consistent-type-exports': 'error', '@typescript-eslint/dot-notation': 'error', + '@typescript-eslint/no-base-to-string': [ + 'error', + // https://github.com/typescript-eslint/typescript-eslint/issues/1655#issuecomment-593639305 + {ignoredTypeNames: ['AssertionError', 'Error']}, + ], + '@typescript-eslint/no-duplicate-type-constituents': 'error', + '@typescript-eslint/no-redundant-type-constituents': 'error', + '@typescript-eslint/no-useless-template-literals': 'error', '@typescript-eslint/non-nullable-type-assertion-style': 'error', '@typescript-eslint/prefer-nullish-coalescing': 'error', '@typescript-eslint/prefer-readonly': 'error', @@ -119,18 +139,17 @@ try { '@typescript-eslint/strict-boolean-expressions': 'error', '@typescript-eslint/switch-exhaustiveness-check': 'error', - // TODO: enable these + // TODO: enable this '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-redundant-type-constituents': 'off', - '@typescript-eslint/no-duplicate-type-constituents': 'off', - '@typescript-eslint/no-base-to-string': 'off', // disable the ones we disable in main config '@typescript-eslint/no-invalid-void-type': 'off', '@typescript-eslint/no-dynamic-delete': 'off', '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/consistent-type-definitions': 'off', // nah + '@typescript-eslint/consistent-indexed-object-style': 'off', '@typescript-eslint/require-await': 'off', }, }, diff --git a/yarn.lock b/yarn.lock index 78ac27335f79..7a734bf74830 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3040,6 +3040,7 @@ __metadata: eslint-plugin-local: "link:./.eslintplugin" eslint-plugin-markdown: ^3.0.0 eslint-plugin-prettier: ^5.0.0 + eslint-plugin-promise: ^6.1.1 eslint-plugin-unicorn: ^50.0.0 execa: ^5.0.0 find-process: ^1.4.1 @@ -9647,6 +9648,15 @@ __metadata: languageName: node linkType: hard +"eslint-plugin-promise@npm:^6.1.1": + version: 6.1.1 + resolution: "eslint-plugin-promise@npm:6.1.1" + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + checksum: 46b9a4f79dae5539987922afc27cc17cbccdecf4f0ba19c0ccbf911b0e31853e9f39d9959eefb9637461b52772afa1a482f1f87ff16c1ba38bdb6fcf21897e9a + languageName: node + linkType: hard + "eslint-plugin-unicorn@npm:^50.0.0": version: 50.0.1 resolution: "eslint-plugin-unicorn@npm:50.0.1"