From 1bb8c2b2ccfc84c9748ee39e2e7d511535b47800 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Wed, 24 Jul 2024 23:08:23 -0500 Subject: [PATCH 01/40] enable rule --- eslint.config.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/eslint.config.mjs b/eslint.config.mjs index 0bb1576063d9..b6a5b0e86e7d 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -318,6 +318,7 @@ export default tseslint.config( // 'jsdoc/informative-docs': 'error', + 'unicorn/no-array-reduce': 'error', 'unicorn/no-typeof-undefined': 'error', }, }, From 739a61de5b27518722c0e2659a880398cf88c8f2 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Wed, 24 Jul 2024 23:09:58 -0500 Subject: [PATCH 02/40] member-ordering --- .../src/rules/member-ordering.ts | 132 +++++++++--------- 1 file changed, 65 insertions(+), 67 deletions(-) diff --git a/packages/eslint-plugin/src/rules/member-ordering.ts b/packages/eslint-plugin/src/rules/member-ordering.ts index 4713192770a7..cc6744452e9f 100644 --- a/packages/eslint-plugin/src/rules/member-ordering.ts +++ b/packages/eslint-plugin/src/rules/member-ordering.ts @@ -299,75 +299,73 @@ export const defaultOrder: MemberType[] = [ 'method', ]; -const allMemberTypes = Array.from( - ( - [ - 'readonly-signature', - 'signature', - 'readonly-field', - 'field', - 'method', - 'call-signature', - 'constructor', - 'accessor', - 'get', - 'set', - 'static-initialization', - ] as const - ).reduce>((all, type) => { - all.add(type); - - (['public', 'protected', 'private', '#private'] as const).forEach( - accessibility => { - if ( - type !== 'readonly-signature' && - type !== 'signature' && - type !== 'static-initialization' && - type !== 'call-signature' && - !(type === 'constructor' && accessibility === '#private') - ) { - all.add(`${accessibility}-${type}`); // e.g. `public-field` - } - - // Only class instance fields, methods, accessors, get and set can have decorators attached to them - if ( - accessibility !== '#private' && - (type === 'readonly-field' || - type === 'field' || - type === 'method' || - type === 'accessor' || - type === 'get' || - type === 'set') - ) { - all.add(`${accessibility}-decorated-${type}`); - all.add(`decorated-${type}`); - } +const all = new Set(); +for (const type of [ + 'readonly-signature', + 'signature', + 'readonly-field', + 'field', + 'method', + 'call-signature', + 'constructor', + 'accessor', + 'get', + 'set', + 'static-initialization', +] as const) { + all.add(type); + + for (const accessibility of [ + 'public', + 'protected', + 'private', + '#private', + ] as const) { + if ( + type !== 'readonly-signature' && + type !== 'signature' && + type !== 'static-initialization' && + type !== 'call-signature' && + !(type === 'constructor' && accessibility === '#private') + ) { + all.add(`${accessibility}-${type}`); // e.g. `public-field` + } - if ( - type !== 'constructor' && - type !== 'readonly-signature' && - type !== 'signature' && - type !== 'call-signature' - ) { - // There is no `static-constructor` or `instance-constructor` or `abstract-constructor` - if (accessibility === '#private' || accessibility === 'private') { - (['static', 'instance'] as const).forEach(scope => { - all.add(`${scope}-${type}`); - all.add(`${accessibility}-${scope}-${type}`); - }); - } else { - (['static', 'instance', 'abstract'] as const).forEach(scope => { - all.add(`${scope}-${type}`); - all.add(`${accessibility}-${scope}-${type}`); - }); - } - } - }, - ); + // Only class instance fields, methods, accessors, get and set can have decorators attached to them + if ( + accessibility !== '#private' && + (type === 'readonly-field' || + type === 'field' || + type === 'method' || + type === 'accessor' || + type === 'get' || + type === 'set') + ) { + all.add(`${accessibility}-decorated-${type}`); + all.add(`decorated-${type}`); + } - return all; - }, new Set()), -); + if ( + type !== 'constructor' && + type !== 'readonly-signature' && + type !== 'signature' && + type !== 'call-signature' + ) { + // There is no `static-constructor` or `instance-constructor` or `abstract-constructor` + for (const scope of [ + 'static', + 'instance', + ...(accessibility === '#private' || accessibility === 'private' + ? [] + : (['abstract'] as const)), + ] as const) { + all.add(`${scope}-${type}`); + all.add(`${accessibility}-${scope}-${type}`); + } + } + } +} +const allMemberTypes = [...all]; const functionExpressions = [ AST_NODE_TYPES.FunctionExpression, From 5c75d886c65a0d7a71854a8a013763ca461cda6f Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Wed, 24 Jul 2024 23:12:03 -0500 Subject: [PATCH 03/40] parse-options --- .../rules/naming-convention-utils/parse-options.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/eslint-plugin/src/rules/naming-convention-utils/parse-options.ts b/packages/eslint-plugin/src/rules/naming-convention-utils/parse-options.ts index 08ac8d41c8b0..e66cdaaebda7 100644 --- a/packages/eslint-plugin/src/rules/naming-convention-utils/parse-options.ts +++ b/packages/eslint-plugin/src/rules/naming-convention-utils/parse-options.ts @@ -82,13 +82,12 @@ function normalizeOption(option: Selector): NormalizedSelector[] { function parseOptions(context: Context): ParsedOptions { const normalizedOptions = context.options.flatMap(normalizeOption); - const result = getEnumNames(Selectors).reduce((acc, k) => { - acc[k] = createValidator(k, context, normalizedOptions); - return acc; - // eslint-disable-next-line @typescript-eslint/prefer-reduce-type-parameter - }, {} as ParsedOptions); - - return result; + return Object.fromEntries( + getEnumNames(Selectors).map(k => [ + k, + createValidator(k, context, normalizedOptions), + ]), + ) as ParsedOptions; } export { parseOptions }; From 77f83b0525c391c646262749b1753294553567c2 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Wed, 24 Jul 2024 23:13:34 -0500 Subject: [PATCH 04/40] no-type-alias --- packages/eslint-plugin/src/rules/no-type-alias.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-type-alias.ts b/packages/eslint-plugin/src/rules/no-type-alias.ts index 53e64a3b5c69..79279d99ee19 100644 --- a/packages/eslint-plugin/src/rules/no-type-alias.ts +++ b/packages/eslint-plugin/src/rules/no-type-alias.ts @@ -325,10 +325,7 @@ export default createRule({ node.type === AST_NODE_TYPES.TSUnionType || node.type === AST_NODE_TYPES.TSIntersectionType ) { - return node.types.reduce((acc, type) => { - acc.push(...getTypes(type, node.type)); - return acc; - }, []); + return node.types.flatMap(type => getTypes(type, node.type)); } return [{ node, compositionType }]; } From cac19fc554eab11ff9a5ff6cb6e0f25e98f7b42f Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Wed, 24 Jul 2024 23:14:39 -0500 Subject: [PATCH 05/40] no-base-to-string --- .../eslint-plugin/tests/rules/no-base-to-string.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts b/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts index a16b08056bdb..e4e25e7bd8e7 100644 --- a/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts +++ b/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts @@ -44,9 +44,9 @@ ruleTester.run('no-base-to-string', rule, { ...literalList.map(i => `\`\${${i}}\`;`), // operator + += - ...literalListWrapped - .map(l => literalListWrapped.map(r => `${l} + ${r};`)) - .reduce((pre, cur) => [...pre, ...cur]), + ...literalListWrapped.flatMap(l => + literalListWrapped.map(r => `${l} + ${r};`), + ), // toString() ...literalListWrapped.map(i => `${i === '1' ? `(${i})` : i}.toString();`), From 1aaf5f353831d5847155777c5846b728027b0408 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 25 Jul 2024 06:41:19 -0500 Subject: [PATCH 06/40] semi --- packages/eslint-plugin/src/rules/semi.ts | 25 +++++------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/packages/eslint-plugin/src/rules/semi.ts b/packages/eslint-plugin/src/rules/semi.ts index 674ea99e3a96..fc1e565e78c6 100644 --- a/packages/eslint-plugin/src/rules/semi.ts +++ b/packages/eslint-plugin/src/rules/semi.ts @@ -38,10 +38,9 @@ export default createRule({ ], create(context) { const rules = baseRule.create(context); - const checkForSemicolon = - rules.ExpressionStatement as TSESLint.RuleFunction; - - /* + return { + ...rules, + /* The following nodes are handled by the member-delimiter-style rule AST_NODE_TYPES.TSCallSignatureDeclaration, AST_NODE_TYPES.TSConstructSignatureDeclaration, @@ -49,22 +48,8 @@ export default createRule({ AST_NODE_TYPES.TSMethodSignature, AST_NODE_TYPES.TSPropertySignature, */ - const nodesToCheck = [ - AST_NODE_TYPES.PropertyDefinition, - AST_NODE_TYPES.TSAbstractPropertyDefinition, - AST_NODE_TYPES.TSDeclareFunction, - AST_NODE_TYPES.TSExportAssignment, - AST_NODE_TYPES.TSImportEqualsDeclaration, - AST_NODE_TYPES.TSTypeAliasDeclaration, - AST_NODE_TYPES.TSEmptyBodyFunctionExpression, - ].reduce((acc, node) => { - acc[node as string] = checkForSemicolon; - return acc; - }, {}); - - return { - ...rules, - ...nodesToCheck, + 'PropertyDefinition, TSAbstractPropertyDefinition, TSDeclareFunction, TSExportAssignment, TSImportEqualsDeclaration, TSTypeAliasDeclaration, TSEmptyBodyFunctionExpression': + rules.ExpressionStatement as TSESLint.RuleFunction, ExportDefaultDeclaration(node): void { if (node.declaration.type !== AST_NODE_TYPES.TSInterfaceDeclaration) { rules.ExportDefaultDeclaration(node); From 7ce8860a6901bde12d9ea2faee21e02d1595d27d Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 25 Jul 2024 06:42:40 -0500 Subject: [PATCH 07/40] no-inferrable-types --- .../tests/rules/no-inferrable-types.test.ts | 37 ++++++++----------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/no-inferrable-types.test.ts b/packages/eslint-plugin/tests/rules/no-inferrable-types.test.ts index fe91490df3e7..5a4d9f70a561 100644 --- a/packages/eslint-plugin/tests/rules/no-inferrable-types.test.ts +++ b/packages/eslint-plugin/tests/rules/no-inferrable-types.test.ts @@ -10,9 +10,6 @@ import type { type MessageIds = InferMessageIdsTypeFromRule; type Options = InferOptionsTypeFromRule; -function flatten(arr: T[][]): T[] { - return arr.reduce((acc, a) => acc.concat(a), []); -} const testCases = [ { type: 'bigint', @@ -70,27 +67,25 @@ const testCases = [ code: ['undefined', 'void someValue'], }, ]; -const validTestCases = flatten( - testCases.map(c => c.code.map(code => `const a = ${code}`)), +const validTestCases = testCases.flatMap(c => + c.code.map(code => `const a = ${code}`), ); const invalidTestCases: TSESLint.InvalidTestCase[] = - flatten( - testCases.map(cas => - cas.code.map(code => ({ - code: `const a: ${cas.type} = ${code}`, - output: `const a = ${code}`, - errors: [ - { - messageId: 'noInferrableType', - data: { - type: cas.type, - }, - line: 1, - column: 7, + testCases.flatMap(cas => + cas.code.map(code => ({ + code: `const a: ${cas.type} = ${code}`, + output: `const a = ${code}`, + errors: [ + { + messageId: 'noInferrableType', + data: { + type: cas.type, }, - ], - })), - ), + line: 1, + column: 7, + }, + ], + })), ); const ruleTester = new RuleTester({ From 5159c6240197141db0c934db9f39b3a7bec09d66 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 25 Jul 2024 06:45:20 -0500 Subject: [PATCH 08/40] no-duplicate-type-constituents --- .../rules/no-duplicate-type-constituents.ts | 64 +++++++++---------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-duplicate-type-constituents.ts b/packages/eslint-plugin/src/rules/no-duplicate-type-constituents.ts index 58bd5ad6db24..48c8e784fce1 100644 --- a/packages/eslint-plugin/src/rules/no-duplicate-type-constituents.ts +++ b/packages/eslint-plugin/src/rules/no-duplicate-type-constituents.ts @@ -109,41 +109,39 @@ export default createRule({ node: TSESTree.TSIntersectionType | TSESTree.TSUnionType, ): void { const cachedTypeMap = new Map(); - node.types.reduce( - (uniqueConstituents, constituentNode) => { - const duplicatedPreviousConstituentInAst = uniqueConstituents.find( - ele => isSameAstNode(ele, constituentNode), + const uniqueConstituents: TSESTree.TypeNode[] = []; + for (const constituentNode of node.types) { + const duplicatedPreviousConstituentInAst = uniqueConstituents.find( + ele => isSameAstNode(ele, constituentNode), + ); + if (duplicatedPreviousConstituentInAst) { + reportDuplicate( + { + duplicated: constituentNode, + duplicatePrevious: duplicatedPreviousConstituentInAst, + }, + node, ); - if (duplicatedPreviousConstituentInAst) { - reportDuplicate( - { - duplicated: constituentNode, - duplicatePrevious: duplicatedPreviousConstituentInAst, - }, - node, - ); - return uniqueConstituents; - } - const constituentNodeType = checker.getTypeAtLocation( - parserServices.esTreeNodeToTSNodeMap.get(constituentNode), + continue; + } + const constituentNodeType = checker.getTypeAtLocation( + parserServices.esTreeNodeToTSNodeMap.get(constituentNode), + ); + const duplicatedPreviousConstituentInType = + cachedTypeMap.get(constituentNodeType); + if (duplicatedPreviousConstituentInType) { + reportDuplicate( + { + duplicated: constituentNode, + duplicatePrevious: duplicatedPreviousConstituentInType, + }, + node, ); - const duplicatedPreviousConstituentInType = - cachedTypeMap.get(constituentNodeType); - if (duplicatedPreviousConstituentInType) { - reportDuplicate( - { - duplicated: constituentNode, - duplicatePrevious: duplicatedPreviousConstituentInType, - }, - node, - ); - return uniqueConstituents; - } - cachedTypeMap.set(constituentNodeType, constituentNode); - return [...uniqueConstituents, constituentNode]; - }, - [], - ); + continue; + } + cachedTypeMap.set(constituentNodeType, constituentNode); + uniqueConstituents.push(constituentNode); + } } function reportDuplicate( duplicateConstituent: { From 49ddaa0a64f418efbc0fe6779bbda954af0f6d39 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 25 Jul 2024 06:46:41 -0500 Subject: [PATCH 09/40] configs.test --- packages/eslint-plugin/tests/configs.test.ts | 51 +++++++++++++------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/packages/eslint-plugin/tests/configs.test.ts b/packages/eslint-plugin/tests/configs.test.ts index db9130e18011..f6f287568e14 100644 --- a/packages/eslint-plugin/tests/configs.test.ts +++ b/packages/eslint-plugin/tests/configs.test.ts @@ -16,13 +16,6 @@ const EXTENSION_RULES = Object.entries(rules) ] as const, ); -function entriesToObject(value: [string, T][]): Record { - return value.reduce>((accum, [k, v]) => { - accum[k] = v; - return accum; - }, {}); -} - function filterRules( values: Record, ): [string, string | unknown[]][] { @@ -118,7 +111,9 @@ describe('all.ts', () => { excludeDeprecated: true, }); - expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + expect(Object.fromEntries(ruleConfigs)).toEqual( + Object.fromEntries(configRules), + ); }); itHasBaseRulesOverriden(unfilteredConfigRules); @@ -135,7 +130,9 @@ describe('disable-type-checked.ts', () => { .filter(([, rule]) => rule.meta.docs?.requiresTypeChecking) .map(([name]) => [`${RULE_NAME_PREFIX}${name}`, 'off']); - expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + expect(Object.fromEntries(ruleConfigs)).toEqual( + Object.fromEntries(configRules), + ); }); }); @@ -151,7 +148,9 @@ describe('recommended.ts', () => { recommendations: ['recommended'], }); - expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + expect(Object.fromEntries(ruleConfigs)).toEqual( + Object.fromEntries(configRules), + ); }); itHasBaseRulesOverriden(unfilteredConfigRules); @@ -168,7 +167,9 @@ describe('recommended-type-checked.ts', () => { recommendations: ['recommended'], }); - expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + expect(Object.fromEntries(ruleConfigs)).toEqual( + Object.fromEntries(configRules), + ); }); itHasBaseRulesOverriden(unfilteredConfigRules); @@ -186,7 +187,9 @@ describe('recommended-type-checked-only.ts', () => { recommendations: ['recommended'], }).filter(([ruleName]) => ruleName); - expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + expect(Object.fromEntries(ruleConfigs)).toEqual( + Object.fromEntries(configRules), + ); }); itHasBaseRulesOverriden(unfilteredConfigRules); @@ -205,7 +208,9 @@ describe('strict.ts', () => { recommendations: ['recommended', 'strict'], }); - expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + expect(Object.fromEntries(ruleConfigs)).toEqual( + Object.fromEntries(configRules), + ); }); itHasBaseRulesOverriden(unfilteredConfigRules); @@ -222,7 +227,9 @@ describe('strict-type-checked.ts', () => { excludeDeprecated: true, recommendations: ['recommended', 'strict'], }); - expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + expect(Object.fromEntries(ruleConfigs)).toEqual( + Object.fromEntries(configRules), + ); }); itHasBaseRulesOverriden(unfilteredConfigRules); @@ -241,7 +248,9 @@ describe('strict-type-checked-only.ts', () => { recommendations: ['recommended', 'strict'], }).filter(([ruleName]) => ruleName); - expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + expect(Object.fromEntries(ruleConfigs)).toEqual( + Object.fromEntries(configRules), + ); }); itHasBaseRulesOverriden(unfilteredConfigRules); @@ -259,7 +268,9 @@ describe('stylistic.ts', () => { recommendations: ['stylistic'], }); - expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + expect(Object.fromEntries(ruleConfigs)).toEqual( + Object.fromEntries(configRules), + ); }); itHasBaseRulesOverriden(unfilteredConfigRules); @@ -275,7 +286,9 @@ describe('stylistic-type-checked.ts', () => { }); it('contains all stylistic rules, excluding deprecated ones', () => { - expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + expect(Object.fromEntries(ruleConfigs)).toEqual( + Object.fromEntries(configRules), + ); }); itHasBaseRulesOverriden(unfilteredConfigRules); @@ -293,7 +306,9 @@ describe('stylistic-type-checked-only.ts', () => { recommendations: ['stylistic'], }).filter(([ruleName]) => ruleName); - expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + expect(Object.fromEntries(ruleConfigs)).toEqual( + Object.fromEntries(configRules), + ); }); itHasBaseRulesOverriden(unfilteredConfigRules); From 97c54c8bf65db8ac6b814f070940be2c60814645 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 25 Jul 2024 06:47:59 -0500 Subject: [PATCH 10/40] func-call-spacing.test --- .../tests/rules/func-call-spacing.test.ts | 50 +++++++++---------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/func-call-spacing.test.ts b/packages/eslint-plugin/tests/rules/func-call-spacing.test.ts index e67567ada705..8614a0da9e48 100644 --- a/packages/eslint-plugin/tests/rules/func-call-spacing.test.ts +++ b/packages/eslint-plugin/tests/rules/func-call-spacing.test.ts @@ -327,32 +327,28 @@ var a = foo 'f\n?.();', 'f?.\n();', 'f\n?.\n();', - ].reduce[]>((acc, code) => { - acc.push( - { - options: ['always', { allowNewlines: true }], - errors: [{ messageId: 'unexpectedWhitespace' }], - code, - // apply no fixers to it - output: null, - }, - { - options: ['always'], - errors: [{ messageId: 'unexpectedWhitespace' }], - code, - // apply no fixers to it - output: null, - }, - { - options: ['never'], - errors: [{ messageId: 'unexpectedWhitespace' }], - code, - // apply no fixers to it - output: null, - }, - ); - - return acc; - }, []), + ].flatMap>(code => [ + { + options: ['always', { allowNewlines: true }], + errors: [{ messageId: 'unexpectedWhitespace' }], + code, + // apply no fixers to it + output: null, + }, + { + options: ['always'], + errors: [{ messageId: 'unexpectedWhitespace' }], + code, + // apply no fixers to it + output: null, + }, + { + options: ['never'], + errors: [{ messageId: 'unexpectedWhitespace' }], + code, + // apply no fixers to it + output: null, + }, + ]), ], }); From 13fb7d8d38977890e8a0eaa4ad63df4000eba366 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 25 Jul 2024 06:50:16 -0500 Subject: [PATCH 11/40] prefer-nullish-coalescing.test --- .../tests/rules/prefer-nullish-coalescing.test.ts | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/prefer-nullish-coalescing.test.ts b/packages/eslint-plugin/tests/rules/prefer-nullish-coalescing.test.ts index bd0cd13c4b5a..ffa7ec84cf8a 100644 --- a/packages/eslint-plugin/tests/rules/prefer-nullish-coalescing.test.ts +++ b/packages/eslint-plugin/tests/rules/prefer-nullish-coalescing.test.ts @@ -28,22 +28,13 @@ function typeValidTest( ): (TSESLint.ValidTestCase | string)[] { return types.map(type => cb(type)); } -function nullishTypeValidTest( +const nullishTypeValidTest = ( cb: ( nullish: string, type: string, ) => TSESLint.ValidTestCase | string, -): (TSESLint.ValidTestCase | string)[] { - return nullishTypes.reduce<(TSESLint.ValidTestCase | string)[]>( - (acc, nullish) => { - types.forEach(type => { - acc.push(cb(nullish, type)); - }); - return acc; - }, - [], - ); -} +): (TSESLint.ValidTestCase | string)[] => + nullishTypes.flatMap(nullish => types.map(type => cb(nullish, type))); function nullishTypeInvalidTest( cb: ( nullish: string, From 2c61ea44b1a70b839f24652ae93cb4c32f1059f0 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 25 Jul 2024 06:51:58 -0500 Subject: [PATCH 12/40] no-explicit-any.test --- .../tests/rules/no-explicit-any.test.ts | 65 +++++++++---------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/no-explicit-any.test.ts b/packages/eslint-plugin/tests/rules/no-explicit-any.test.ts index 2dc995cb66f0..41c5e7d2bfc1 100644 --- a/packages/eslint-plugin/tests/rules/no-explicit-any.test.ts +++ b/packages/eslint-plugin/tests/rules/no-explicit-any.test.ts @@ -1197,7 +1197,7 @@ const test = >() => {}; ], }, ] as InvalidTestCase[] - ).reduce((acc, testCase) => { + ).flatMap(testCase => { const suggestions = (code: string): SuggestionOutput[] => [ { messageId: 'suggestUnknown', @@ -1208,38 +1208,37 @@ const test = >() => {}; output: code.replace(/any/, 'never'), }, ]; - acc.push({ - ...testCase, - errors: testCase.errors.map(e => ({ - ...e, - suggestions: e.suggestions ?? suggestions(testCase.code), - })), - }); - const options = testCase.options ?? []; const code = `// fixToUnknown: true\n${testCase.code}`; - acc.push({ - code, - output: code.replace(/any/g, 'unknown'), - options: [{ ...options[0], fixToUnknown: true }], - errors: testCase.errors.map(err => { - if (err.line === undefined) { - return err; - } - - return { - ...err, - line: err.line + 1, - suggestions: - err.suggestions?.map( - (s): SuggestionOutput => ({ - ...s, - output: `// fixToUnknown: true\n${s.output}`, - }), - ) ?? suggestions(code), - }; - }), - }); + return [ + { + ...testCase, + errors: testCase.errors.map(e => ({ + ...e, + suggestions: e.suggestions ?? suggestions(testCase.code), + })), + }, + { + code, + output: code.replace(/any/g, 'unknown'), + options: [{ ...testCase.options?.[0], fixToUnknown: true }], + errors: testCase.errors.map(err => { + if (err.line === undefined) { + return err; + } - return acc; - }, []), + return { + ...err, + line: err.line + 1, + suggestions: + err.suggestions?.map( + (s): SuggestionOutput => ({ + ...s, + output: `// fixToUnknown: true\n${s.output}`, + }), + ) ?? suggestions(code), + }; + }), + }, + ]; + }), }); From 1236c79680797879419bfa2c6d0b868a4902b6b8 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 25 Jul 2024 06:55:03 -0500 Subject: [PATCH 13/40] no-unsafe-assignment --- .../tests/rules/no-unsafe-assignment.test.ts | 88 +++++++++---------- 1 file changed, 42 insertions(+), 46 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/no-unsafe-assignment.test.ts b/packages/eslint-plugin/tests/rules/no-unsafe-assignment.test.ts index a65923ab6778..497ec4e14d89 100644 --- a/packages/eslint-plugin/tests/rules/no-unsafe-assignment.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unsafe-assignment.test.ts @@ -12,55 +12,51 @@ type Options = InferOptionsTypeFromRule; type MessageIds = InferMessageIdsTypeFromRule; type InvalidTest = TSESLint.InvalidTestCase; -function assignmentTest( +const assignmentTest = ( tests: [string, number, number, boolean?][], -): InvalidTest[] { - return tests.reduce( - (acc, [assignment, column, endColumn, skipAssignmentExpression]) => { - // VariableDeclaration - acc.push({ - code: `const ${assignment}`, - errors: [ - { - messageId: 'unsafeArrayPatternFromTuple', - line: 1, - column: column + 6, - endColumn: endColumn + 6, - }, - ], - }); - // AssignmentPattern - acc.push({ - code: `function foo(${assignment}) {}`, - errors: [ +): InvalidTest[] => + tests.flatMap(([assignment, column, endColumn, skipAssignmentExpression]) => [ + // VariableDeclaration + { + code: `const ${assignment}`, + errors: [ + { + messageId: 'unsafeArrayPatternFromTuple', + line: 1, + column: column + 6, + endColumn: endColumn + 6, + }, + ], + }, + // AssignmentPattern + { + code: `function foo(${assignment}) {}`, + errors: [ + { + messageId: 'unsafeArrayPatternFromTuple', + line: 1, + column: column + 13, + endColumn: endColumn + 13, + }, + ], + }, + // AssignmentExpression + ...(skipAssignmentExpression + ? [] + : [ { - messageId: 'unsafeArrayPatternFromTuple', - line: 1, - column: column + 13, - endColumn: endColumn + 13, + code: `(${assignment})`, + errors: [ + { + messageId: 'unsafeArrayPatternFromTuple' as const, + line: 1, + column: column + 1, + endColumn: endColumn + 1, + }, + ], }, - ], - }); - // AssignmentExpression - if (skipAssignmentExpression !== true) { - acc.push({ - code: `(${assignment})`, - errors: [ - { - messageId: 'unsafeArrayPatternFromTuple', - line: 1, - column: column + 1, - endColumn: endColumn + 1, - }, - ], - }); - } - - return acc; - }, - [], - ); -} + ]), + ]); const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', From 052b1eb271a910d47e9b25d0bd251cbc13723ccb Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 25 Jul 2024 06:57:11 -0500 Subject: [PATCH 14/40] semi.test --- .../eslint-plugin/tests/rules/semi.test.ts | 40 ++++++------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/semi.test.ts b/packages/eslint-plugin/tests/rules/semi.test.ts index e1f53043a9ce..854e1b8878ce 100644 --- a/packages/eslint-plugin/tests/rules/semi.test.ts +++ b/packages/eslint-plugin/tests/rules/semi.test.ts @@ -266,18 +266,10 @@ class PanCamera extends FreeCamera { 'export default (foo) => foo.bar();', 'export default foo = 42;', 'export default foo += 42;', - ].reduce[]>((acc, code) => { - acc.push({ - code, - options: ['always'], - }); - acc.push({ - code: code.replace(/;/g, ''), - options: ['never'], - }); - - return acc; - }, []), + ].flatMap>(code => [ + { code, options: ['always'] }, + { code: code.replace(/;/g, ''), options: ['never'] }, + ]), ], invalid: [ { @@ -1079,27 +1071,19 @@ class PanCamera extends FreeCamera { }, ], }, - ].reduce[]>((acc, test) => { - acc.push({ + ].flatMap>(test => [ + { code: test.code.replace(/;/g, ''), output: test.code, options: ['always'], - errors: test.errors.map(e => ({ - ...e, - ...missingSemicolon, - })), - }); - acc.push({ + errors: test.errors.map(e => ({ ...e, ...missingSemicolon })), + }, + { code: test.code, output: test.output ?? test.code.replace(/;/g, ''), options: ['never'], - errors: test.errors.map(e => ({ - ...e, - ...extraSemicolon, - })), - }); - - return acc; - }, []), + errors: test.errors.map(e => ({ ...e, ...extraSemicolon })), + }, + ]), ], }); From 9ca9249d2ef266eafdec8aa1101b868333e84666 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 25 Jul 2024 07:00:56 -0500 Subject: [PATCH 15/40] prefer-nullish-coalescing.test --- .../rules/prefer-nullish-coalescing.test.ts | 73 ++++++++----------- 1 file changed, 29 insertions(+), 44 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/prefer-nullish-coalescing.test.ts b/packages/eslint-plugin/tests/rules/prefer-nullish-coalescing.test.ts index ffa7ec84cf8a..65395eb4b17a 100644 --- a/packages/eslint-plugin/tests/rules/prefer-nullish-coalescing.test.ts +++ b/packages/eslint-plugin/tests/rules/prefer-nullish-coalescing.test.ts @@ -28,29 +28,14 @@ function typeValidTest( ): (TSESLint.ValidTestCase | string)[] { return types.map(type => cb(type)); } -const nullishTypeValidTest = ( - cb: ( - nullish: string, - type: string, - ) => TSESLint.ValidTestCase | string, -): (TSESLint.ValidTestCase | string)[] => - nullishTypes.flatMap(nullish => types.map(type => cb(nullish, type))); -function nullishTypeInvalidTest( - cb: ( - nullish: string, - type: string, - ) => TSESLint.InvalidTestCase, -): TSESLint.InvalidTestCase[] { - return nullishTypes.reduce[]>( - (acc, nullish) => { - types.forEach(type => { - acc.push(cb(nullish, type)); - }); - return acc; - }, - [], - ); -} +const nullishTypeTest = < + T extends + | TSESLint.ValidTestCase + | TSESLint.InvalidTestCase + | string, +>( + cb: (nullish: string, type: string) => T, +): T[] => nullishTypes.flatMap(nullish => types.map(type => cb(nullish, type))); ruleTester.run('prefer-nullish-coalescing', rule, { valid: [ @@ -60,7 +45,7 @@ declare const x: ${type}; x || 'foo'; `, ), - ...nullishTypeValidTest( + ...nullishTypeTest( (nullish, type) => ` declare const x: ${type} | ${nullish}; x ?? 'foo'; @@ -129,35 +114,35 @@ x === null ? x : y; })), // ignoreConditionalTests - ...nullishTypeValidTest((nullish, type) => ({ + ...nullishTypeTest((nullish, type) => ({ code: ` declare const x: ${type} | ${nullish}; x || 'foo' ? null : null; `, options: [{ ignoreConditionalTests: true }], })), - ...nullishTypeValidTest((nullish, type) => ({ + ...nullishTypeTest((nullish, type) => ({ code: ` declare const x: ${type} | ${nullish}; if (x || 'foo') {} `, options: [{ ignoreConditionalTests: true }], })), - ...nullishTypeValidTest((nullish, type) => ({ + ...nullishTypeTest((nullish, type) => ({ code: ` declare const x: ${type} | ${nullish}; do {} while (x || 'foo') `, options: [{ ignoreConditionalTests: true }], })), - ...nullishTypeValidTest((nullish, type) => ({ + ...nullishTypeTest((nullish, type) => ({ code: ` declare const x: ${type} | ${nullish}; for (;x || 'foo';) {} `, options: [{ ignoreConditionalTests: true }], })), - ...nullishTypeValidTest((nullish, type) => ({ + ...nullishTypeTest((nullish, type) => ({ code: ` declare const x: ${type} | ${nullish}; while (x || 'foo') {} @@ -166,7 +151,7 @@ while (x || 'foo') {} })), // ignoreMixedLogicalExpressions - ...nullishTypeValidTest((nullish, type) => ({ + ...nullishTypeTest((nullish, type) => ({ code: ` declare const a: ${type} | ${nullish}; declare const b: ${type} | ${nullish}; @@ -175,7 +160,7 @@ a || b && c; `, options: [{ ignoreMixedLogicalExpressions: true }], })), - ...nullishTypeValidTest((nullish, type) => ({ + ...nullishTypeTest((nullish, type) => ({ code: ` declare const a: ${type} | ${nullish}; declare const b: ${type} | ${nullish}; @@ -185,7 +170,7 @@ a || b || c && d; `, options: [{ ignoreMixedLogicalExpressions: true }], })), - ...nullishTypeValidTest((nullish, type) => ({ + ...nullishTypeTest((nullish, type) => ({ code: ` declare const a: ${type} | ${nullish}; declare const b: ${type} | ${nullish}; @@ -372,7 +357,7 @@ x || y; }, ], invalid: [ - ...nullishTypeInvalidTest((nullish, type) => ({ + ...nullishTypeTest((nullish, type) => ({ code: ` declare const x: ${type} | ${nullish}; x || 'foo'; @@ -591,7 +576,7 @@ if (x) { }, // ignoreConditionalTests - ...nullishTypeInvalidTest((nullish, type) => ({ + ...nullishTypeTest((nullish, type) => ({ code: ` declare const x: ${type} | ${nullish}; x || 'foo' ? null : null; @@ -617,7 +602,7 @@ x ?? 'foo' ? null : null; }, ], })), - ...nullishTypeInvalidTest((nullish, type) => ({ + ...nullishTypeTest((nullish, type) => ({ code: ` declare const x: ${type} | ${nullish}; if (x || 'foo') {} @@ -643,7 +628,7 @@ if (x ?? 'foo') {} }, ], })), - ...nullishTypeInvalidTest((nullish, type) => ({ + ...nullishTypeTest((nullish, type) => ({ code: ` declare const x: ${type} | ${nullish}; do {} while (x || 'foo') @@ -669,7 +654,7 @@ do {} while (x ?? 'foo') }, ], })), - ...nullishTypeInvalidTest((nullish, type) => ({ + ...nullishTypeTest((nullish, type) => ({ code: ` declare const x: ${type} | ${nullish}; for (;x || 'foo';) {} @@ -695,7 +680,7 @@ for (;x ?? 'foo';) {} }, ], })), - ...nullishTypeInvalidTest((nullish, type) => ({ + ...nullishTypeTest((nullish, type) => ({ code: ` declare const x: ${type} | ${nullish}; while (x || 'foo') {} @@ -723,7 +708,7 @@ while (x ?? 'foo') {} })), // ignoreMixedLogicalExpressions - ...nullishTypeInvalidTest((nullish, type) => ({ + ...nullishTypeTest((nullish, type) => ({ code: ` declare const a: ${type} | ${nullish}; declare const b: ${type} | ${nullish}; @@ -752,7 +737,7 @@ a ?? b && c; }, ], })), - ...nullishTypeInvalidTest((nullish, type) => ({ + ...nullishTypeTest((nullish, type) => ({ code: ` declare const a: ${type} | ${nullish}; declare const b: ${type} | ${nullish}; @@ -802,7 +787,7 @@ a || b ?? c && d; }, ], })), - ...nullishTypeInvalidTest((nullish, type) => ({ + ...nullishTypeTest((nullish, type) => ({ code: ` declare const a: ${type} | ${nullish}; declare const b: ${type} | ${nullish}; @@ -854,7 +839,7 @@ a && b || c ?? d; })), // should not false positive for functions inside conditional tests - ...nullishTypeInvalidTest((nullish, type) => ({ + ...nullishTypeTest((nullish, type) => ({ code: ` declare const x: ${type} | ${nullish}; if (() => x || 'foo') {} @@ -880,7 +865,7 @@ if (() => x ?? 'foo') {} }, ], })), - ...nullishTypeInvalidTest((nullish, type) => ({ + ...nullishTypeTest((nullish, type) => ({ code: ` declare const x: ${type} | ${nullish}; if (function werid() { return x || 'foo' }) {} @@ -907,7 +892,7 @@ if (function werid() { return x ?? 'foo' }) {} ], })), // https://github.com/typescript-eslint/typescript-eslint/issues/1290 - ...nullishTypeInvalidTest((nullish, type) => ({ + ...nullishTypeTest((nullish, type) => ({ code: ` declare const a: ${type} | ${nullish}; declare const b: ${type}; From b8e3944604669c0e0aa5d650ac166527b0cb57bb Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 25 Jul 2024 07:04:22 -0500 Subject: [PATCH 16/40] type-annotation-spacing.test --- .../rules/type-annotation-spacing.test.ts | 793 +++++++++--------- 1 file changed, 392 insertions(+), 401 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/type-annotation-spacing.test.ts b/packages/eslint-plugin/tests/rules/type-annotation-spacing.test.ts index ff745515c4be..5a6fcaedefec 100644 --- a/packages/eslint-plugin/tests/rules/type-annotation-spacing.test.ts +++ b/packages/eslint-plugin/tests/rules/type-annotation-spacing.test.ts @@ -6564,435 +6564,426 @@ type Foo = { const operators = ['+?:', '-?:']; ruleTester.run('type-annotation-spacing', rule, { - valid: operators.reduce[]>( - (validCases, operator) => - validCases.concat([ - { - code: `type Foo = { [P in keyof T]${operator} T[P] }`, - options: [], - }, - { - code: `type Foo = { [P in keyof T]${operator} T[P] }`, - options: [{ after: true }], - }, - { - code: `type Foo = { [P in keyof T]${operator} T[P] }`, - options: [{ before: false }], - }, - { - code: `type Foo = { [P in keyof T]${operator} T[P] }`, - options: [{ after: true, before: false }], - }, - { - code: `type Foo = { [P in keyof T] ${operator}T[P] }`, - options: [{ after: false, before: true }], - }, - { - code: `type Foo = { [P in keyof T] ${operator} T[P] }`, - options: [{ before: true }], - }, - { - code: `type Foo = { [P in keyof T] ${operator} T[P] }`, - options: [{ after: true, before: true }], - }, - { - code: `type Foo = { [P in keyof T]${operator}T[P] }`, - options: [{ after: false }], - }, - { - code: `type Foo = { [P in keyof T]${operator}T[P] }`, - options: [{ after: false, before: false }], - }, - ]), - [], - ), - invalid: operators.reduce[]>( - (invalidCases, operator) => - invalidCases.concat([ - // space before + after cases - { - code: `type Foo = { [P in keyof T] ${operator} T[P] }`, - options: [{ after: true }], - output: `type Foo = { [P in keyof T]${operator} T[P] }`, - errors: [ - { - messageId: 'unexpectedSpaceBefore', - data: { - type: operator, - }, - line: 1, - column: 32, + valid: operators.flatMap(operator => [ + { code: `type Foo = { [P in keyof T]${operator} T[P] }`, options: [] }, + { + code: `type Foo = { [P in keyof T]${operator} T[P] }`, + options: [{ after: true }], + }, + { + code: `type Foo = { [P in keyof T]${operator} T[P] }`, + options: [{ before: false }], + }, + { + code: `type Foo = { [P in keyof T]${operator} T[P] }`, + options: [{ after: true, before: false }], + }, + { + code: `type Foo = { [P in keyof T] ${operator}T[P] }`, + options: [{ after: false, before: true }], + }, + { + code: `type Foo = { [P in keyof T] ${operator} T[P] }`, + options: [{ before: true }], + }, + { + code: `type Foo = { [P in keyof T] ${operator} T[P] }`, + options: [{ after: true, before: true }], + }, + { + code: `type Foo = { [P in keyof T]${operator}T[P] }`, + options: [{ after: false }], + }, + { + code: `type Foo = { [P in keyof T]${operator}T[P] }`, + options: [{ after: false, before: false }], + }, + ]), + invalid: operators.flatMap>( + operator => [ + // space before + after cases + { + code: `type Foo = { [P in keyof T] ${operator} T[P] }`, + options: [{ after: true }], + output: `type Foo = { [P in keyof T]${operator} T[P] }`, + errors: [ + { + messageId: 'unexpectedSpaceBefore', + data: { + type: operator, }, - ], - }, - { - code: `type Foo = { [P in keyof T] ${operator} T[P] }`, - options: [{ before: false }], - output: `type Foo = { [P in keyof T]${operator} T[P] }`, - errors: [ - { - messageId: 'unexpectedSpaceBefore', - data: { - type: operator, - }, - line: 1, - column: 32, + line: 1, + column: 32, + }, + ], + }, + { + code: `type Foo = { [P in keyof T] ${operator} T[P] }`, + options: [{ before: false }], + output: `type Foo = { [P in keyof T]${operator} T[P] }`, + errors: [ + { + messageId: 'unexpectedSpaceBefore', + data: { + type: operator, }, - ], - }, - { - code: `type Foo = { [P in keyof T] ${operator} T[P] }`, - options: [{ after: true, before: false }], - output: `type Foo = { [P in keyof T]${operator} T[P] }`, - errors: [ - { - messageId: 'unexpectedSpaceBefore', - data: { - type: operator, - }, - line: 1, - column: 32, + line: 1, + column: 32, + }, + ], + }, + { + code: `type Foo = { [P in keyof T] ${operator} T[P] }`, + options: [{ after: true, before: false }], + output: `type Foo = { [P in keyof T]${operator} T[P] }`, + errors: [ + { + messageId: 'unexpectedSpaceBefore', + data: { + type: operator, }, - ], - }, - { - code: `type Foo = { [P in keyof T] ${operator} T[P] }`, - options: [{ after: false }], - output: `type Foo = { [P in keyof T]${operator}T[P] }`, - errors: [ - { - messageId: 'unexpectedSpaceBefore', - data: { - type: operator, - }, - line: 1, - column: 32, + line: 1, + column: 32, + }, + ], + }, + { + code: `type Foo = { [P in keyof T] ${operator} T[P] }`, + options: [{ after: false }], + output: `type Foo = { [P in keyof T]${operator}T[P] }`, + errors: [ + { + messageId: 'unexpectedSpaceBefore', + data: { + type: operator, }, - { - messageId: 'unexpectedSpaceAfter', - data: { - type: operator, - }, - line: 1, - column: 34, + line: 1, + column: 32, + }, + { + messageId: 'unexpectedSpaceAfter', + data: { + type: operator, }, - ], - }, - { - code: `type Foo = { [P in keyof T] ${operator} T[P] }`, - options: [{ after: false, before: false }], - output: `type Foo = { [P in keyof T]${operator}T[P] }`, - errors: [ - { - messageId: 'unexpectedSpaceBefore', - data: { - type: operator, - }, - line: 1, - column: 32, + line: 1, + column: 34, + }, + ], + }, + { + code: `type Foo = { [P in keyof T] ${operator} T[P] }`, + options: [{ after: false, before: false }], + output: `type Foo = { [P in keyof T]${operator}T[P] }`, + errors: [ + { + messageId: 'unexpectedSpaceBefore', + data: { + type: operator, }, - { - messageId: 'unexpectedSpaceAfter', - data: { - type: operator, - }, - line: 1, - column: 34, + line: 1, + column: 32, + }, + { + messageId: 'unexpectedSpaceAfter', + data: { + type: operator, }, - ], - }, - { - code: `type Foo = { [P in keyof T] ${operator} T[P] }`, - options: [{ after: false, before: true }], - output: `type Foo = { [P in keyof T] ${operator}T[P] }`, - errors: [ - { - messageId: 'unexpectedSpaceAfter', - data: { - type: operator, - }, - line: 1, - column: 34, + line: 1, + column: 34, + }, + ], + }, + { + code: `type Foo = { [P in keyof T] ${operator} T[P] }`, + options: [{ after: false, before: true }], + output: `type Foo = { [P in keyof T] ${operator}T[P] }`, + errors: [ + { + messageId: 'unexpectedSpaceAfter', + data: { + type: operator, }, - ], - }, - // no space cases - { - code: `type Foo = { [P in keyof T]${operator}T[P] }`, - options: [{ after: true }], - output: `type Foo = { [P in keyof T]${operator} T[P] }`, - errors: [ - { - messageId: 'expectedSpaceAfter', - data: { - type: operator, - }, - line: 1, - column: 33, + line: 1, + column: 34, + }, + ], + }, + // no space cases + { + code: `type Foo = { [P in keyof T]${operator}T[P] }`, + options: [{ after: true }], + output: `type Foo = { [P in keyof T]${operator} T[P] }`, + errors: [ + { + messageId: 'expectedSpaceAfter', + data: { + type: operator, }, - ], - }, - { - code: `type Foo = { [P in keyof T]${operator}T[P] }`, - options: [{ before: true }], - output: `type Foo = { [P in keyof T] ${operator} T[P] }`, - errors: [ - { - messageId: 'expectedSpaceBefore', - data: { - type: operator, - }, - line: 1, - column: 31, + line: 1, + column: 33, + }, + ], + }, + { + code: `type Foo = { [P in keyof T]${operator}T[P] }`, + options: [{ before: true }], + output: `type Foo = { [P in keyof T] ${operator} T[P] }`, + errors: [ + { + messageId: 'expectedSpaceBefore', + data: { + type: operator, }, - { - messageId: 'expectedSpaceAfter', - data: { - type: operator, - }, - line: 1, - column: 33, + line: 1, + column: 31, + }, + { + messageId: 'expectedSpaceAfter', + data: { + type: operator, }, - ], - }, - { - code: `type Foo = { [P in keyof T]${operator}T[P] }`, - options: [{ after: true, before: true }], - output: `type Foo = { [P in keyof T] ${operator} T[P] }`, - errors: [ - { - messageId: 'expectedSpaceBefore', - data: { - type: operator, - }, - line: 1, - column: 31, + line: 1, + column: 33, + }, + ], + }, + { + code: `type Foo = { [P in keyof T]${operator}T[P] }`, + options: [{ after: true, before: true }], + output: `type Foo = { [P in keyof T] ${operator} T[P] }`, + errors: [ + { + messageId: 'expectedSpaceBefore', + data: { + type: operator, }, - { - messageId: 'expectedSpaceAfter', - data: { - type: operator, - }, - line: 1, - column: 33, + line: 1, + column: 31, + }, + { + messageId: 'expectedSpaceAfter', + data: { + type: operator, }, - ], - }, - { - code: `type Foo = { [P in keyof T]${operator}T[P] }`, - options: [{ after: true, before: false }], - output: `type Foo = { [P in keyof T]${operator} T[P] }`, - errors: [ - { - messageId: 'expectedSpaceAfter', - data: { - type: operator, - }, - line: 1, - column: 33, + line: 1, + column: 33, + }, + ], + }, + { + code: `type Foo = { [P in keyof T]${operator}T[P] }`, + options: [{ after: true, before: false }], + output: `type Foo = { [P in keyof T]${operator} T[P] }`, + errors: [ + { + messageId: 'expectedSpaceAfter', + data: { + type: operator, }, - ], - }, - { - code: `type Foo = { [P in keyof T]${operator}T[P] }`, - options: [{ after: false, before: true }], - output: `type Foo = { [P in keyof T] ${operator}T[P] }`, - errors: [ - { - messageId: 'expectedSpaceBefore', - data: { - type: operator, - }, - line: 1, - column: 31, + line: 1, + column: 33, + }, + ], + }, + { + code: `type Foo = { [P in keyof T]${operator}T[P] }`, + options: [{ after: false, before: true }], + output: `type Foo = { [P in keyof T] ${operator}T[P] }`, + errors: [ + { + messageId: 'expectedSpaceBefore', + data: { + type: operator, }, - ], - }, - // space before cases - { - code: `type Foo = { [P in keyof T] ${operator}T[P] }`, - options: [], - output: `type Foo = { [P in keyof T]${operator} T[P] }`, - errors: [ - { - messageId: 'unexpectedSpaceBefore', - data: { - type: operator, - }, - line: 1, - column: 32, + line: 1, + column: 31, + }, + ], + }, + // space before cases + { + code: `type Foo = { [P in keyof T] ${operator}T[P] }`, + options: [], + output: `type Foo = { [P in keyof T]${operator} T[P] }`, + errors: [ + { + messageId: 'unexpectedSpaceBefore', + data: { + type: operator, }, - { - messageId: 'expectedSpaceAfter', - data: { - type: operator, - }, - line: 1, - column: 34, + line: 1, + column: 32, + }, + { + messageId: 'expectedSpaceAfter', + data: { + type: operator, }, - ], - }, - { - code: `type Foo = { [P in keyof T] ${operator}T[P] }`, - options: [{ after: true }], - output: `type Foo = { [P in keyof T]${operator} T[P] }`, - errors: [ - { - messageId: 'unexpectedSpaceBefore', - data: { - type: operator, - }, - line: 1, - column: 32, + line: 1, + column: 34, + }, + ], + }, + { + code: `type Foo = { [P in keyof T] ${operator}T[P] }`, + options: [{ after: true }], + output: `type Foo = { [P in keyof T]${operator} T[P] }`, + errors: [ + { + messageId: 'unexpectedSpaceBefore', + data: { + type: operator, }, - { - messageId: 'expectedSpaceAfter', - data: { - type: operator, - }, - line: 1, - column: 34, + line: 1, + column: 32, + }, + { + messageId: 'expectedSpaceAfter', + data: { + type: operator, }, - ], - }, - { - code: `type Foo = { [P in keyof T] ${operator}T[P] }`, - options: [{ before: true }], - output: `type Foo = { [P in keyof T] ${operator} T[P] }`, - errors: [ - { - messageId: 'expectedSpaceAfter', - data: { - type: operator, - }, - line: 1, - column: 34, + line: 1, + column: 34, + }, + ], + }, + { + code: `type Foo = { [P in keyof T] ${operator}T[P] }`, + options: [{ before: true }], + output: `type Foo = { [P in keyof T] ${operator} T[P] }`, + errors: [ + { + messageId: 'expectedSpaceAfter', + data: { + type: operator, }, - ], - }, - { - code: `type Foo = { [P in keyof T] ${operator}T[P] }`, - options: [{ after: true, before: false }], - output: `type Foo = { [P in keyof T]${operator} T[P] }`, - errors: [ - { - messageId: 'unexpectedSpaceBefore', - data: { - type: operator, - }, - line: 1, - column: 32, + line: 1, + column: 34, + }, + ], + }, + { + code: `type Foo = { [P in keyof T] ${operator}T[P] }`, + options: [{ after: true, before: false }], + output: `type Foo = { [P in keyof T]${operator} T[P] }`, + errors: [ + { + messageId: 'unexpectedSpaceBefore', + data: { + type: operator, }, - { - messageId: 'expectedSpaceAfter', - data: { - type: operator, - }, - line: 1, - column: 34, + line: 1, + column: 32, + }, + { + messageId: 'expectedSpaceAfter', + data: { + type: operator, }, - ], - }, - { - code: `type Foo = { [P in keyof T] ${operator}T[P] }`, - options: [{ after: true, before: true }], - output: `type Foo = { [P in keyof T] ${operator} T[P] }`, - errors: [ - { - messageId: 'expectedSpaceAfter', - data: { - type: operator, - }, - line: 1, - column: 34, + line: 1, + column: 34, + }, + ], + }, + { + code: `type Foo = { [P in keyof T] ${operator}T[P] }`, + options: [{ after: true, before: true }], + output: `type Foo = { [P in keyof T] ${operator} T[P] }`, + errors: [ + { + messageId: 'expectedSpaceAfter', + data: { + type: operator, }, - ], - }, - // space after cases - { - code: `type Foo = { [P in keyof T]${operator} T[P] }`, - options: [{ after: false }], - output: `type Foo = { [P in keyof T]${operator}T[P] }`, - errors: [ - { - messageId: 'unexpectedSpaceAfter', - data: { - type: operator, - }, - line: 1, - column: 33, + line: 1, + column: 34, + }, + ], + }, + // space after cases + { + code: `type Foo = { [P in keyof T]${operator} T[P] }`, + options: [{ after: false }], + output: `type Foo = { [P in keyof T]${operator}T[P] }`, + errors: [ + { + messageId: 'unexpectedSpaceAfter', + data: { + type: operator, }, - ], - }, - { - code: `type Foo = { [P in keyof T]${operator} T[P] }`, - options: [{ before: true }], - output: `type Foo = { [P in keyof T] ${operator} T[P] }`, - errors: [ - { - messageId: 'expectedSpaceBefore', - data: { - type: operator, - }, - line: 1, - column: 31, + line: 1, + column: 33, + }, + ], + }, + { + code: `type Foo = { [P in keyof T]${operator} T[P] }`, + options: [{ before: true }], + output: `type Foo = { [P in keyof T] ${operator} T[P] }`, + errors: [ + { + messageId: 'expectedSpaceBefore', + data: { + type: operator, }, - ], - }, - { - code: `type Foo = { [P in keyof T]${operator} T[P] }`, - options: [{ after: true, before: true }], - output: `type Foo = { [P in keyof T] ${operator} T[P] }`, - errors: [ - { - messageId: 'expectedSpaceBefore', - data: { - type: operator, - }, - line: 1, - column: 31, + line: 1, + column: 31, + }, + ], + }, + { + code: `type Foo = { [P in keyof T]${operator} T[P] }`, + options: [{ after: true, before: true }], + output: `type Foo = { [P in keyof T] ${operator} T[P] }`, + errors: [ + { + messageId: 'expectedSpaceBefore', + data: { + type: operator, }, - ], - }, - { - code: `type Foo = { [P in keyof T]${operator} T[P] }`, - options: [{ after: false, before: true }], - output: `type Foo = { [P in keyof T] ${operator}T[P] }`, - errors: [ - { - messageId: 'expectedSpaceBefore', - data: { - type: operator, - }, - line: 1, - column: 31, + line: 1, + column: 31, + }, + ], + }, + { + code: `type Foo = { [P in keyof T]${operator} T[P] }`, + options: [{ after: false, before: true }], + output: `type Foo = { [P in keyof T] ${operator}T[P] }`, + errors: [ + { + messageId: 'expectedSpaceBefore', + data: { + type: operator, }, - { - messageId: 'unexpectedSpaceAfter', - data: { - type: operator, - }, - line: 1, - column: 33, + line: 1, + column: 31, + }, + { + messageId: 'unexpectedSpaceAfter', + data: { + type: operator, }, - ], - }, - { - code: `type Foo = { [P in keyof T]${operator} T[P] }`, - options: [{ after: false, before: false }], - output: `type Foo = { [P in keyof T]${operator}T[P] }`, - errors: [ - { - messageId: 'unexpectedSpaceAfter', - data: { - type: operator, - }, - line: 1, - column: 33, + line: 1, + column: 33, + }, + ], + }, + { + code: `type Foo = { [P in keyof T]${operator} T[P] }`, + options: [{ after: false, before: false }], + output: `type Foo = { [P in keyof T]${operator}T[P] }`, + errors: [ + { + messageId: 'unexpectedSpaceAfter', + data: { + type: operator, }, - ], - }, - ]), - [], + line: 1, + column: 33, + }, + ], + }, + ], ), }); From ff9812f7a4a493c2b8144528f93172de4aa46705 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 25 Jul 2024 07:08:10 -0500 Subject: [PATCH 17/40] indent.test --- .../tests/rules/indent/indent.test.ts | 109 ++++++++---------- 1 file changed, 49 insertions(+), 60 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/indent/indent.test.ts b/packages/eslint-plugin/tests/rules/indent/indent.test.ts index 5974cb5198ee..760fbbf01207 100644 --- a/packages/eslint-plugin/tests/rules/indent/indent.test.ts +++ b/packages/eslint-plugin/tests/rules/indent/indent.test.ts @@ -8,13 +8,9 @@ import type { TSESLint } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import rule from '../../../src/rules/indent'; -import type { - InferMessageIdsTypeFromRule, - InferOptionsTypeFromRule, -} from '../../../src/util'; +import type { InferMessageIdsTypeFromRule } from '../../../src/util'; type MessageIds = InferMessageIdsTypeFromRule; -type Options = InferOptionsTypeFromRule; /** * Marks a test case as a plain javascript case which should be indented the same @@ -23,7 +19,9 @@ function nonTsTestCase(example: TemplateStringsArray): string { return [`// Non-TS Test Case`, example].join('\n'); } -const individualNodeTests = [ +const validCases = []; +const invalidCases = []; +for (const testCase of [ { node: AST_NODE_TYPES.ClassDeclaration, code: [ @@ -593,63 +591,54 @@ type Foo = string | { `, ], }, -].reduce>( - (acc, testCase) => { - const indent = ' '; +]) { + const indent = ' '; - const validCases = [...acc.valid]; - const invalidCases = [...acc.invalid]; - - const codeCases = testCase.code.map(code => - [ - '', // newline to make test error messages nicer - `// ${testCase.node}`, // add comment to easily identify which node a test belongs to - code.trim(), // remove leading/trailing spaces from the case - ].join('\n'), - ); + const codeCases = testCase.code.map(code => + [ + '', // newline to make test error messages nicer + `// ${testCase.node}`, // add comment to easily identify which node a test belongs to + code.trim(), // remove leading/trailing spaces from the case + ].join('\n'), + ); - codeCases.forEach(code => { - // valid test case is just the code - validCases.push(code); + for (const code of codeCases) { + // valid test case is just the code + validCases.push(code); - const invalid = { - // test the fixer by removing all the spaces - code: code.replace(new RegExp(indent, 'g'), ''), - output: code, - errors: code - .split('\n') - .map | null>((line, lineNum) => { - const indentCount = line.split(indent).length - 1; - const spaceCount = indentCount * indent.length; + const invalid = { + // test the fixer by removing all the spaces + code: code.replace(new RegExp(indent, 'g'), ''), + output: code, + errors: code + .split('\n') + .map | null>((line, lineNum) => { + const indentCount = line.split(indent).length - 1; + const spaceCount = indentCount * indent.length; - if (indentCount < 1) { - return null; - } + if (indentCount < 1) { + return null; + } - return { - messageId: 'wrongIndentation', - data: { - expected: `${spaceCount} spaces`, - actual: 0, - }, - line: lineNum + 1, - column: 1, - }; - }) - .filter( - (error): error is TSESLint.TestCaseError => - error != null, - ), - }; - if (invalid.errors.length > 0) { - invalidCases.push(invalid); - } - }); - - return { ...acc, valid: validCases, invalid: invalidCases }; - }, - { valid: [], invalid: [] }, -); + return { + messageId: 'wrongIndentation', + data: { + expected: `${spaceCount} spaces`, + actual: 0, + }, + line: lineNum + 1, + column: 1, + }; + }) + .filter( + (error): error is TSESLint.TestCaseError => error != null, + ), + }; + if (invalid.errors.length > 0) { + invalidCases.push(invalid); + } + } +} const ruleTester = new RuleTester({ parserOptions: { @@ -662,7 +651,7 @@ const ruleTester = new RuleTester({ ruleTester.run('indent', rule, { valid: [ - ...individualNodeTests.valid, + ...validCases, ` @Component({ components: { @@ -765,7 +754,7 @@ const div: JQuery = $('
') }, ], invalid: [ - ...individualNodeTests.invalid, + ...invalidCases, { code: ` type Foo = { From a6e57adce2be005f202e13cd06e798f223471b56 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 25 Jul 2024 07:10:24 -0500 Subject: [PATCH 18/40] parser --- packages/parser/src/parser.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/parser/src/parser.ts b/packages/parser/src/parser.ts index ea2107e67aa1..ec11aa4a5dcd 100644 --- a/packages/parser/src/parser.ts +++ b/packages/parser/src/parser.ts @@ -42,13 +42,9 @@ function validateBoolean( const LIB_FILENAME_REGEX = /lib\.(.+)\.d\.[cm]?ts$/; function getLib(compilerOptions: ts.CompilerOptions): Lib[] { if (compilerOptions.lib) { - return compilerOptions.lib.reduce((acc, lib) => { + return compilerOptions.lib.flatMap(lib => { const match = LIB_FILENAME_REGEX.exec(lib.toLowerCase()); - if (match) { - acc.push(match[1] as Lib); - } - - return acc; + return match ? (match[1] as Lib) : []; }, []); } From 7b5953e15e34eda0520683d717b226e7beee259a Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 25 Jul 2024 07:11:22 -0500 Subject: [PATCH 19/40] configs.test --- .../typescript-eslint/tests/configs.test.ts | 51 ++++++++++++------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/packages/typescript-eslint/tests/configs.test.ts b/packages/typescript-eslint/tests/configs.test.ts index 6d8add503b4b..210ae56da119 100644 --- a/packages/typescript-eslint/tests/configs.test.ts +++ b/packages/typescript-eslint/tests/configs.test.ts @@ -19,13 +19,6 @@ const EXTENSION_RULES = Object.entries(rules) ] as const, ); -function entriesToObject(value: [string, T][]): Record { - return value.reduce>((accum, [k, v]) => { - accum[k] = v; - return accum; - }, {}); -} - function filterRules( values: FlatConfig.Rules | undefined, ): [string, FlatConfig.RuleEntry][] { @@ -122,7 +115,9 @@ describe('all.ts', () => { excludeDeprecated: true, }); - expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + expect(Object.fromEntries(ruleConfigs)).toEqual( + Object.fromEntries(configRules), + ); }); itHasBaseRulesOverriden(unfilteredConfigRules); @@ -138,7 +133,9 @@ describe('disable-type-checked.ts', () => { .filter(([, rule]) => rule.meta.docs?.requiresTypeChecking) .map(([name]) => [`${RULE_NAME_PREFIX}${name}`, 'off']); - expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + expect(Object.fromEntries(ruleConfigs)).toEqual( + Object.fromEntries(configRules), + ); }); }); @@ -153,7 +150,9 @@ describe('recommended.ts', () => { recommendations: ['recommended'], }); - expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + expect(Object.fromEntries(ruleConfigs)).toEqual( + Object.fromEntries(configRules), + ); }); itHasBaseRulesOverriden(unfilteredConfigRules); @@ -169,7 +168,9 @@ describe('recommended-type-checked.ts', () => { recommendations: ['recommended'], }); - expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + expect(Object.fromEntries(ruleConfigs)).toEqual( + Object.fromEntries(configRules), + ); }); itHasBaseRulesOverriden(unfilteredConfigRules); @@ -187,7 +188,9 @@ describe('recommended-type-checked-only.ts', () => { recommendations: ['recommended'], }).filter(([ruleName]) => ruleName); - expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + expect(Object.fromEntries(ruleConfigs)).toEqual( + Object.fromEntries(configRules), + ); }); itHasBaseRulesOverriden(unfilteredConfigRules); @@ -205,7 +208,9 @@ describe('strict.ts', () => { recommendations: ['recommended', 'strict'], }); - expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + expect(Object.fromEntries(ruleConfigs)).toEqual( + Object.fromEntries(configRules), + ); }); itHasBaseRulesOverriden(unfilteredConfigRules); @@ -221,7 +226,9 @@ describe('strict-type-checked.ts', () => { excludeDeprecated: true, recommendations: ['recommended', 'strict'], }); - expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + expect(Object.fromEntries(ruleConfigs)).toEqual( + Object.fromEntries(configRules), + ); }); itHasBaseRulesOverriden(unfilteredConfigRules); @@ -239,7 +246,9 @@ describe('strict-type-checked-only.ts', () => { recommendations: ['recommended', 'strict'], }).filter(([ruleName]) => ruleName); - expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + expect(Object.fromEntries(ruleConfigs)).toEqual( + Object.fromEntries(configRules), + ); }); itHasBaseRulesOverriden(unfilteredConfigRules); @@ -256,7 +265,9 @@ describe('stylistic.ts', () => { recommendations: ['stylistic'], }); - expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + expect(Object.fromEntries(ruleConfigs)).toEqual( + Object.fromEntries(configRules), + ); }); itHasBaseRulesOverriden(unfilteredConfigRules); @@ -271,7 +282,9 @@ describe('stylistic-type-checked.ts', () => { }); it('contains all stylistic rules, excluding deprecated ones', () => { - expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + expect(Object.fromEntries(ruleConfigs)).toEqual( + Object.fromEntries(configRules), + ); }); itHasBaseRulesOverriden(unfilteredConfigRules); @@ -289,7 +302,9 @@ describe('stylistic-type-checked-only.ts', () => { recommendations: ['stylistic'], }).filter(([ruleName]) => ruleName); - expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + expect(Object.fromEntries(ruleConfigs)).toEqual( + Object.fromEntries(configRules), + ); }); itHasBaseRulesOverriden(unfilteredConfigRules); From 072ba3058b57aceb0b290c591ce64b2633a69d2a Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 25 Jul 2024 07:12:38 -0500 Subject: [PATCH 20/40] resolveProjectList --- .../src/parseSettings/resolveProjectList.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/typescript-estree/src/parseSettings/resolveProjectList.ts b/packages/typescript-estree/src/parseSettings/resolveProjectList.ts index 4b24608b6ff8..d7fb755aefd7 100644 --- a/packages/typescript-estree/src/parseSettings/resolveProjectList.ts +++ b/packages/typescript-estree/src/parseSettings/resolveProjectList.ts @@ -57,12 +57,7 @@ export function resolveProjectList( const projectFolderIgnoreList = ( options.projectFolderIgnoreList ?? ['**/node_modules/**'] ) - .reduce((acc, folder) => { - if (typeof folder === 'string') { - acc.push(folder); - } - return acc; - }, []) + .filter(folder => typeof folder === 'string') // prefix with a ! for not match glob .map(folder => (folder.startsWith('!') ? folder : `!${folder}`)); From 867feb6555dc9046e6a0f67fe3580a3b23c1934b Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 25 Jul 2024 07:17:29 -0500 Subject: [PATCH 21/40] generate-configs --- packages/repo-tools/src/generate-configs.mts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/repo-tools/src/generate-configs.mts b/packages/repo-tools/src/generate-configs.mts index 60093aea19d9..e26ecab32781 100644 --- a/packages/repo-tools/src/generate-configs.mts +++ b/packages/repo-tools/src/generate-configs.mts @@ -72,9 +72,8 @@ async function main(): Promise { } const RULE_NAME_PREFIX = '@typescript-eslint/'; - const MAX_RULE_NAME_LENGTH = Object.keys(eslintPlugin.rules).reduce( - (acc, name) => Math.max(acc, name.length), - 0, + const MAX_RULE_NAME_LENGTH = Math.max( + ...Object.keys(eslintPlugin.rules).map(name => name.length), ); const BASE_RULES_TO_BE_OVERRIDDEN = new Map( Object.entries(eslintPlugin.rules) From 88c8064d40972a696420b5cb647fb5999b919bad Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 25 Jul 2024 07:23:01 -0500 Subject: [PATCH 22/40] deepMerge --- packages/utils/src/eslint-utils/deepMerge.ts | 41 ++++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/packages/utils/src/eslint-utils/deepMerge.ts b/packages/utils/src/eslint-utils/deepMerge.ts index a4ace614df5d..07d4c4e08f49 100644 --- a/packages/utils/src/eslint-utils/deepMerge.ts +++ b/packages/utils/src/eslint-utils/deepMerge.ts @@ -22,28 +22,27 @@ export function deepMerge( // get the unique set of keys across both objects const keys = new Set(Object.keys(first).concat(Object.keys(second))); - return Array.from(keys).reduce((acc, key) => { - const firstHasKey = key in first; - const secondHasKey = key in second; - const firstValue = first[key]; - const secondValue = second[key]; + return Object.fromEntries( + Array.from(keys).map(key => { + const firstHasKey = key in first; + const secondHasKey = key in second; + const firstValue = first[key]; + const secondValue = second[key]; - if (firstHasKey && secondHasKey) { - if (isObjectNotArray(firstValue) && isObjectNotArray(secondValue)) { - // object type - acc[key] = deepMerge(firstValue, secondValue); - } else { - // value type - acc[key] = secondValue; - } - } else if (firstHasKey) { - acc[key] = firstValue; - } else { - acc[key] = secondValue; - } - - return acc; - }, {}); + return [ + key, + firstHasKey && secondHasKey + ? isObjectNotArray(firstValue) && isObjectNotArray(secondValue) + ? // object type + deepMerge(firstValue, secondValue) + : // value type + secondValue + : firstHasKey + ? firstValue + : secondValue, + ]; + }), + ); } export { isObjectNotArray }; From cd96c131f66f9ec2cd799d240502ed37090c2f36 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 25 Jul 2024 07:26:06 -0500 Subject: [PATCH 23/40] jsonSchema --- .../website/src/components/lib/jsonSchema.ts | 49 ++++++++++--------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/packages/website/src/components/lib/jsonSchema.ts b/packages/website/src/components/lib/jsonSchema.ts index a8a845565ec9..c76dd62562fd 100644 --- a/packages/website/src/components/lib/jsonSchema.ts +++ b/packages/website/src/components/lib/jsonSchema.ts @@ -176,30 +176,33 @@ export function getTypescriptOptions(): DescribedOptionDeclaration[] { * Get the JSON schema for the typescript config */ export function getTypescriptJsonSchema(): JSONSchema4 { - const properties = getTypescriptOptions().reduce((options, item) => { - if (item.type === 'boolean') { - options[item.name] = { - type: 'boolean', - description: item.description.message, - }; - } else if (item.type === 'list' && item.element?.type instanceof Map) { - options[item.name] = { - type: 'array', - items: { + const properties = Object.fromEntries( + getTypescriptOptions().flatMap(item => { + let value; + if (item.type === 'boolean') { + value = { + type: 'boolean', + description: item.description.message, + }; + } else if (item.type === 'list' && item.element?.type instanceof Map) { + value = { + type: 'array', + items: { + type: 'string', + enum: Array.from(item.element.type.keys()), + }, + description: item.description.message, + }; + } else if (item.type instanceof Map) { + value = { type: 'string', - enum: Array.from(item.element.type.keys()), - }, - description: item.description.message, - }; - } else if (item.type instanceof Map) { - options[item.name] = { - type: 'string', - description: item.description.message, - enum: Array.from(item.type.keys()), - }; - } - return options; - }, {}); + description: item.description.message, + enum: Array.from(item.type.keys()), + }; + } + return value ? [[item.name, value]] : []; + }), + ); return { type: 'object', From b5b6b4b1a94b462c6aa953f64b903342a8ad5e60 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 25 Jul 2024 07:29:05 -0500 Subject: [PATCH 24/40] ConfigTypeScript --- .../components/config/ConfigTypeScript.tsx | 47 ++++++++----------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/packages/website/src/components/config/ConfigTypeScript.tsx b/packages/website/src/components/config/ConfigTypeScript.tsx index 1f45569df9c8..48711488a6e2 100644 --- a/packages/website/src/components/config/ConfigTypeScript.tsx +++ b/packages/website/src/components/config/ConfigTypeScript.tsx @@ -31,33 +31,26 @@ function ConfigTypeScript(props: ConfigTypeScriptProps): React.JSX.Element { }, [config]); const options = useMemo((): ConfigOptionsType[] => { - return Object.values( - getTypescriptOptions().reduce>( - (group, item) => { - const category = item.category.message; - group[category] ??= { - heading: category, - fields: [], - }; - if (item.type === 'boolean') { - group[category].fields.push({ - key: item.name, - type: 'boolean', - label: item.description.message, - }); - } else if (item.type instanceof Map) { - group[category].fields.push({ - key: item.name, - type: 'string', - label: item.description.message, - enum: ['', ...Array.from(item.type.keys())], - }); - } - return group; - }, - {}, - ), - ); + const group: Record = {}; + for (const item of getTypescriptOptions()) { + const category = item.category.message; + group[category] ??= { heading: category, fields: [] }; + if (item.type === 'boolean') { + group[category].fields.push({ + key: item.name, + type: 'boolean', + label: item.description.message, + }); + } else if (item.type instanceof Map) { + group[category].fields.push({ + key: item.name, + type: 'string', + label: item.description.message, + enum: ['', ...Array.from(item.type.keys())], + }); + } + } + return Object.values(group); }, []); const onChange = useCallback( From b2c0ed512def7653b462b8b76cea9d9b45dc33f3 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 25 Jul 2024 07:34:20 -0500 Subject: [PATCH 25/40] generate-sponsors --- packages/repo-tools/src/generate-sponsors.mts | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/packages/repo-tools/src/generate-sponsors.mts b/packages/repo-tools/src/generate-sponsors.mts index 1a7b600758ec..b007e13d9489 100644 --- a/packages/repo-tools/src/generate-sponsors.mts +++ b/packages/repo-tools/src/generate-sponsors.mts @@ -118,25 +118,19 @@ async function main(): Promise { requestGraphql('collective'), ]); - const accountsById = account.orders.nodes.reduce< - Record - >((accumulator, account) => { - const name = account.fromAccount.name || account.fromAccount.id; - accumulator[name] = { - ...accumulator[name], - ...account.fromAccount, - }; - return accumulator; - }, {}); - - const totalDonationsById = collective.members.nodes.reduce< - Record - >((accumulator, member) => { + const accountsById = Object.fromEntries( + account.orders.nodes.map(account => [ + account.fromAccount.name || account.fromAccount.id, + account.fromAccount, + ]), + ); + + const totalDonationsById: Record = {}; + for (const member of collective.members.nodes) { const name = member.account.name || member.account.id; - accumulator[name] ||= 0; - accumulator[name] += member.totalDonations.valueInCents; - return accumulator; - }, {}); + totalDonationsById[name] = + (totalDonationsById[name] ?? 0) + member.totalDonations.valueInCents; + } const uniqueNames = new Set(excludedNames); const allSponsorsConfig = collective.members.nodes From c46f70c859d8566c75be8c4e99f5325dc00107df Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 25 Jul 2024 07:44:05 -0500 Subject: [PATCH 26/40] generate-configs --- packages/repo-tools/src/generate-configs.mts | 62 +++++++++----------- 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/packages/repo-tools/src/generate-configs.mts b/packages/repo-tools/src/generate-configs.mts index e26ecab32781..2cc495f60ada 100644 --- a/packages/repo-tools/src/generate-configs.mts +++ b/packages/repo-tools/src/generate-configs.mts @@ -110,13 +110,13 @@ async function main(): Promise { /** * Helper function reduces records to key - value pairs. */ - function reducer( - config: LinterConfigRules, + function addRule( + rules: LinterConfigRules, [key, value]: RuleEntry, settings: ConfigRuleSettings = {}, - ): LinterConfigRules { + ): void { if (settings.deprecated && value.meta.deprecated) { - return config; + return; } // Explicitly exclude rules requiring type-checking @@ -124,14 +124,14 @@ async function main(): Promise { settings.typeChecked === 'exclude' && value.meta.docs?.requiresTypeChecking === true ) { - return config; + return; } if ( settings.typeChecked === 'include-only' && value.meta.docs?.requiresTypeChecking !== true ) { - return config; + return; } const ruleName = `${RULE_NAME_PREFIX}${key}`; @@ -149,7 +149,7 @@ async function main(): Promise { '=', chalk.green('off'), ); - config[baseRuleName] = 'off'; + rules[baseRuleName] = 'off'; } console.log( `${chalk.dim(RULE_NAME_PREFIX)}${key.padEnd(MAX_RULE_NAME_LENGTH)}`, @@ -160,12 +160,10 @@ async function main(): Promise { const ruleLevel = settings.forcedRuleLevel ?? 'error'; const ruleOptions = settings.getOptions?.(value); - config[ruleName] = + rules[ruleName] = ruleOptions && ruleOptions !== true ? [ruleLevel, ...ruleOptions] : ruleLevel; - - return config; } /** @@ -271,16 +269,13 @@ async function main(): Promise { ruleEntries, settings, }: ExtendedConfigSettings): Promise { - await writeConfig( - () => ({ - extends: [...CLASSIC_EXTENDS], - rules: ruleEntries.reduce( - (config, entry) => reducer(config, entry, settings), - {}, - ), - }), - name, - ); + await writeConfig(() => { + const rules = {}; + for (const entry of ruleEntries) { + addRule(rules, entry, settings); + } + return { extends: [...CLASSIC_EXTENDS], rules }; + }, name); } function filterRuleEntriesTo( @@ -385,25 +380,24 @@ async function main(): Promise { ruleEntries: filterRuleEntriesTo('stylistic'), }); - await writeConfig( - () => ({ + await writeConfig(() => { + const rules = {}; + for (const entry of allRuleEntries) { + addRule(rules, entry, { + typeChecked: 'include-only', + baseRuleForExtensionRule: 'exclude', + forcedRuleLevel: 'off', + }); + } + return { parserOptions: { project: false, program: null, EXPERIMENTAL_useProjectService: false, }, - rules: allRuleEntries.reduce( - (config, entry) => - reducer(config, entry, { - typeChecked: 'include-only', - baseRuleForExtensionRule: 'exclude', - forcedRuleLevel: 'off', - }), - {}, - ), - }), - 'disable-type-checked', - ); + rules, + }; + }, 'disable-type-checked'); } main().catch((error: unknown) => { From 9b4c6ad8a3fde1770d29a4fcf2f3d4897b24dcc3 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 25 Jul 2024 12:54:23 -0500 Subject: [PATCH 27/40] fix semi --- packages/eslint-plugin/src/rules/semi.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin/src/rules/semi.ts b/packages/eslint-plugin/src/rules/semi.ts index fc1e565e78c6..b6bea24854c8 100644 --- a/packages/eslint-plugin/src/rules/semi.ts +++ b/packages/eslint-plugin/src/rules/semi.ts @@ -48,8 +48,20 @@ export default createRule({ AST_NODE_TYPES.TSMethodSignature, AST_NODE_TYPES.TSPropertySignature, */ - 'PropertyDefinition, TSAbstractPropertyDefinition, TSDeclareFunction, TSExportAssignment, TSImportEqualsDeclaration, TSTypeAliasDeclaration, TSEmptyBodyFunctionExpression': - rules.ExpressionStatement as TSESLint.RuleFunction, + ...Object.fromEntries( + [ + AST_NODE_TYPES.PropertyDefinition, + AST_NODE_TYPES.TSAbstractPropertyDefinition, + AST_NODE_TYPES.TSDeclareFunction, + AST_NODE_TYPES.TSExportAssignment, + AST_NODE_TYPES.TSImportEqualsDeclaration, + AST_NODE_TYPES.TSTypeAliasDeclaration, + AST_NODE_TYPES.TSEmptyBodyFunctionExpression, + ].map(node => [ + node, + rules.ExpressionStatement as TSESLint.RuleFunction, + ]), + ), ExportDefaultDeclaration(node): void { if (node.declaration.type !== AST_NODE_TYPES.TSInterfaceDeclaration) { rules.ExportDefaultDeclaration(node); From ea3935bce6623a55536301b305ddc243037db390 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Sat, 27 Jul 2024 14:03:31 -0500 Subject: [PATCH 28/40] review except for flatMap --- .../rules/prefer-nullish-coalescing.test.ts | 8 ++--- packages/parser/src/parser.ts | 2 +- packages/repo-tools/src/generate-sponsors.mts | 4 +-- packages/utils/src/eslint-utils/deepMerge.ts | 29 ++++++++++--------- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/prefer-nullish-coalescing.test.ts b/packages/eslint-plugin/tests/rules/prefer-nullish-coalescing.test.ts index 65395eb4b17a..25ebf99492e3 100644 --- a/packages/eslint-plugin/tests/rules/prefer-nullish-coalescing.test.ts +++ b/packages/eslint-plugin/tests/rules/prefer-nullish-coalescing.test.ts @@ -28,14 +28,14 @@ function typeValidTest( ): (TSESLint.ValidTestCase | string)[] { return types.map(type => cb(type)); } -const nullishTypeTest = < +function nullishTypeTest< T extends | TSESLint.ValidTestCase | TSESLint.InvalidTestCase | string, ->( - cb: (nullish: string, type: string) => T, -): T[] => nullishTypes.flatMap(nullish => types.map(type => cb(nullish, type))); +>(cb: (nullish: string, type: string) => T): T[] { + return nullishTypes.flatMap(nullish => types.map(type => cb(nullish, type))); +} ruleTester.run('prefer-nullish-coalescing', rule, { valid: [ diff --git a/packages/parser/src/parser.ts b/packages/parser/src/parser.ts index ec11aa4a5dcd..996af5ec49c3 100644 --- a/packages/parser/src/parser.ts +++ b/packages/parser/src/parser.ts @@ -45,7 +45,7 @@ function getLib(compilerOptions: ts.CompilerOptions): Lib[] { return compilerOptions.lib.flatMap(lib => { const match = LIB_FILENAME_REGEX.exec(lib.toLowerCase()); return match ? (match[1] as Lib) : []; - }, []); + }); } const target = compilerOptions.target ?? ScriptTarget.ES5; diff --git a/packages/repo-tools/src/generate-sponsors.mts b/packages/repo-tools/src/generate-sponsors.mts index b007e13d9489..45486b28b51f 100644 --- a/packages/repo-tools/src/generate-sponsors.mts +++ b/packages/repo-tools/src/generate-sponsors.mts @@ -128,8 +128,8 @@ async function main(): Promise { const totalDonationsById: Record = {}; for (const member of collective.members.nodes) { const name = member.account.name || member.account.id; - totalDonationsById[name] = - (totalDonationsById[name] ?? 0) + member.totalDonations.valueInCents; + totalDonationsById[name] ||= 0; + totalDonationsById[name] += member.totalDonations.valueInCents; } const uniqueNames = new Set(excludedNames); diff --git a/packages/utils/src/eslint-utils/deepMerge.ts b/packages/utils/src/eslint-utils/deepMerge.ts index 07d4c4e08f49..34f748ba467c 100644 --- a/packages/utils/src/eslint-utils/deepMerge.ts +++ b/packages/utils/src/eslint-utils/deepMerge.ts @@ -23,24 +23,27 @@ export function deepMerge( const keys = new Set(Object.keys(first).concat(Object.keys(second))); return Object.fromEntries( - Array.from(keys).map(key => { + Array.from(keys, key => { const firstHasKey = key in first; const secondHasKey = key in second; const firstValue = first[key]; const secondValue = second[key]; - return [ - key, - firstHasKey && secondHasKey - ? isObjectNotArray(firstValue) && isObjectNotArray(secondValue) - ? // object type - deepMerge(firstValue, secondValue) - : // value type - secondValue - : firstHasKey - ? firstValue - : secondValue, - ]; + let value; + if (firstHasKey && secondHasKey) { + if (isObjectNotArray(firstValue) && isObjectNotArray(secondValue)) { + // object type + value = deepMerge(firstValue, secondValue); + } else { + // value type + value = secondValue; + } + } else if (firstHasKey) { + value = firstValue; + } else { + value = secondValue; + } + return [key, value]; }), ); } From c29870e329b7c03254cb2cb2afa74b73d2d6a38a Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Sat, 27 Jul 2024 14:13:48 -0500 Subject: [PATCH 29/40] no longer using flatMap as filter --- packages/parser/src/parser.ts | 7 ++- .../website/src/components/lib/jsonSchema.ts | 50 ++++++++++--------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/packages/parser/src/parser.ts b/packages/parser/src/parser.ts index 996af5ec49c3..45f9ece1062d 100644 --- a/packages/parser/src/parser.ts +++ b/packages/parser/src/parser.ts @@ -42,10 +42,9 @@ function validateBoolean( const LIB_FILENAME_REGEX = /lib\.(.+)\.d\.[cm]?ts$/; function getLib(compilerOptions: ts.CompilerOptions): Lib[] { if (compilerOptions.lib) { - return compilerOptions.lib.flatMap(lib => { - const match = LIB_FILENAME_REGEX.exec(lib.toLowerCase()); - return match ? (match[1] as Lib) : []; - }); + return compilerOptions.lib + .map(lib => LIB_FILENAME_REGEX.exec(lib.toLowerCase())?.[1]) + .filter((lib): lib is Lib => !!lib); } const target = compilerOptions.target ?? ScriptTarget.ES5; diff --git a/packages/website/src/components/lib/jsonSchema.ts b/packages/website/src/components/lib/jsonSchema.ts index c76dd62562fd..0d8bb242845e 100644 --- a/packages/website/src/components/lib/jsonSchema.ts +++ b/packages/website/src/components/lib/jsonSchema.ts @@ -177,31 +177,33 @@ export function getTypescriptOptions(): DescribedOptionDeclaration[] { */ export function getTypescriptJsonSchema(): JSONSchema4 { const properties = Object.fromEntries( - getTypescriptOptions().flatMap(item => { - let value; - if (item.type === 'boolean') { - value = { - type: 'boolean', - description: item.description.message, - }; - } else if (item.type === 'list' && item.element?.type instanceof Map) { - value = { - type: 'array', - items: { + getTypescriptOptions() + .map(item => { + let value; + if (item.type === 'boolean') { + value = { + type: 'boolean', + description: item.description.message, + }; + } else if (item.type === 'list' && item.element?.type instanceof Map) { + value = { + type: 'array', + items: { + type: 'string', + enum: Array.from(item.element.type.keys()), + }, + description: item.description.message, + }; + } else if (item.type instanceof Map) { + value = { type: 'string', - enum: Array.from(item.element.type.keys()), - }, - description: item.description.message, - }; - } else if (item.type instanceof Map) { - value = { - type: 'string', - description: item.description.message, - enum: Array.from(item.type.keys()), - }; - } - return value ? [[item.name, value]] : []; - }), + description: item.description.message, + enum: Array.from(item.type.keys()), + }; + } + return [item.name, value] as const; + }) + .filter(([, value]) => value), ); return { From dc47bf105198e05d7156d4038f1a689d2d1cfcc9 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Sat, 27 Jul 2024 14:15:13 -0500 Subject: [PATCH 30/40] fix variable name --- packages/repo-tools/src/generate-sponsors.mts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/repo-tools/src/generate-sponsors.mts b/packages/repo-tools/src/generate-sponsors.mts index 45486b28b51f..73e73cea1089 100644 --- a/packages/repo-tools/src/generate-sponsors.mts +++ b/packages/repo-tools/src/generate-sponsors.mts @@ -119,9 +119,9 @@ async function main(): Promise { ]); const accountsById = Object.fromEntries( - account.orders.nodes.map(account => [ - account.fromAccount.name || account.fromAccount.id, - account.fromAccount, + account.orders.nodes.map(order => [ + order.fromAccount.name || order.fromAccount.id, + order.fromAccount, ]), ); From 9df95fd03d9b194e4d2b3dc593d54f2f30bc01d3 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Fri, 2 Aug 2024 07:40:20 -0400 Subject: [PATCH 31/40] additional fix --- .../src/rules/no-duplicate-type-constituents.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-duplicate-type-constituents.ts b/packages/eslint-plugin/src/rules/no-duplicate-type-constituents.ts index f32317cbed2e..4de77cb8fba1 100644 --- a/packages/eslint-plugin/src/rules/no-duplicate-type-constituents.ts +++ b/packages/eslint-plugin/src/rules/no-duplicate-type-constituents.ts @@ -110,11 +110,11 @@ export default createRule({ const cachedTypeMap = new Map(); const uniqueConstituents: TSESTree.TypeNode[] = []; for (const constituentNode of node.types) { - const constituentNodeType = - parserServices.getTypeAtLocation(constituentNode); - if (tsutils.isIntrinsicErrorType(constituentNodeType)) { - return uniqueConstituents; - } + const constituentNodeType = + parserServices.getTypeAtLocation(constituentNode); + if (tsutils.isIntrinsicErrorType(constituentNodeType)) { + continue; + } const duplicatedPreviousConstituentInAst = uniqueConstituents.find( ele => isSameAstNode(ele, constituentNode), ); From b91620f47cf3b5df65830ce7e529e246ae339a0f Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Fri, 2 Aug 2024 07:44:24 -0400 Subject: [PATCH 32/40] prettier --- packages/eslint-plugin/tests/rules/no-explicit-any.test.ts | 2 +- packages/eslint-plugin/tests/rules/no-inferrable-types.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/no-explicit-any.test.ts b/packages/eslint-plugin/tests/rules/no-explicit-any.test.ts index 1c98b6097575..d49d03cc62de 100644 --- a/packages/eslint-plugin/tests/rules/no-explicit-any.test.ts +++ b/packages/eslint-plugin/tests/rules/no-explicit-any.test.ts @@ -1232,7 +1232,7 @@ const test = >() => {}; line: err.line + 1, suggestions: err.suggestions?.map( - (s): RuleSuggestionOutput => ({ + (s): RuleSuggestionOutput => ({ ...s, output: `// fixToUnknown: true\n${s.output}`, }), diff --git a/packages/eslint-plugin/tests/rules/no-inferrable-types.test.ts b/packages/eslint-plugin/tests/rules/no-inferrable-types.test.ts index 161446a708f7..6d9ad9cce982 100644 --- a/packages/eslint-plugin/tests/rules/no-inferrable-types.test.ts +++ b/packages/eslint-plugin/tests/rules/no-inferrable-types.test.ts @@ -86,7 +86,7 @@ const invalidTestCases: InvalidTestCase[] = }, ], })), -); + ); const ruleTester = new RuleTester(); From dc33d9ec4c376e5ac6da9bbeccf4175805cbb5d0 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Mon, 5 Aug 2024 07:14:31 -0500 Subject: [PATCH 33/40] refactor member-ordering --- .../src/rules/member-ordering.ts | 134 +++++++++--------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/packages/eslint-plugin/src/rules/member-ordering.ts b/packages/eslint-plugin/src/rules/member-ordering.ts index cc6744452e9f..2dafb08c568f 100644 --- a/packages/eslint-plugin/src/rules/member-ordering.ts +++ b/packages/eslint-plugin/src/rules/member-ordering.ts @@ -299,73 +299,73 @@ export const defaultOrder: MemberType[] = [ 'method', ]; -const all = new Set(); -for (const type of [ - 'readonly-signature', - 'signature', - 'readonly-field', - 'field', - 'method', - 'call-signature', - 'constructor', - 'accessor', - 'get', - 'set', - 'static-initialization', -] as const) { - all.add(type); - - for (const accessibility of [ - 'public', - 'protected', - 'private', - '#private', - ] as const) { - if ( - type !== 'readonly-signature' && - type !== 'signature' && - type !== 'static-initialization' && - type !== 'call-signature' && - !(type === 'constructor' && accessibility === '#private') - ) { - all.add(`${accessibility}-${type}`); // e.g. `public-field` - } - - // Only class instance fields, methods, accessors, get and set can have decorators attached to them - if ( - accessibility !== '#private' && - (type === 'readonly-field' || - type === 'field' || - type === 'method' || - type === 'accessor' || - type === 'get' || - type === 'set') - ) { - all.add(`${accessibility}-decorated-${type}`); - all.add(`decorated-${type}`); - } - - if ( - type !== 'constructor' && - type !== 'readonly-signature' && - type !== 'signature' && - type !== 'call-signature' - ) { - // There is no `static-constructor` or `instance-constructor` or `abstract-constructor` - for (const scope of [ - 'static', - 'instance', - ...(accessibility === '#private' || accessibility === 'private' - ? [] - : (['abstract'] as const)), - ] as const) { - all.add(`${scope}-${type}`); - all.add(`${accessibility}-${scope}-${type}`); - } - } - } -} -const allMemberTypes = [...all]; +const allMemberTypes = [ + ...new Set( + ( + [ + 'readonly-signature', + 'signature', + 'readonly-field', + 'field', + 'method', + 'call-signature', + 'constructor', + 'accessor', + 'get', + 'set', + 'static-initialization', + ] as const + ).flatMap(type => [ + type, + + ...(['public', 'protected', 'private', '#private'] as const) + .flatMap(accessibility => [ + type !== 'readonly-signature' && + type !== 'signature' && + type !== 'static-initialization' && + type !== 'call-signature' && + !(type === 'constructor' && accessibility === '#private') + ? `${accessibility}-${type}` // e.g. `public-field` + : [], + + // Only class instance fields, methods, accessors, get and set can have decorators attached to them + accessibility !== '#private' && + (type === 'readonly-field' || + type === 'field' || + type === 'method' || + type === 'accessor' || + type === 'get' || + type === 'set') + ? [`${accessibility}-decorated-${type}`, `decorated-${type}`] + : [], + + type !== 'constructor' && + type !== 'readonly-signature' && + type !== 'signature' && + type !== 'call-signature' + ? // There is no `static-constructor` or `instance-constructor` or `abstract-constructor` + ( + [ + 'static', + 'instance', + ...(accessibility === '#private' || + accessibility === 'private' + ? [] + : (['abstract'] as const)), + ] as const + ).flatMap( + scope => + [ + `${scope}-${type}`, + `${accessibility}-${scope}-${type}`, + ] as const, + ) + : [], + ]) + .flat(), + ]), + ), +]; const functionExpressions = [ AST_NODE_TYPES.FunctionExpression, From 36c4d399cba5f72526f922ee75c18a77a0bc087e Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Mon, 5 Aug 2024 07:16:03 -0500 Subject: [PATCH 34/40] move comment --- packages/eslint-plugin/src/rules/member-ordering.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin/src/rules/member-ordering.ts b/packages/eslint-plugin/src/rules/member-ordering.ts index 2dafb08c568f..540a51bc0d67 100644 --- a/packages/eslint-plugin/src/rules/member-ordering.ts +++ b/packages/eslint-plugin/src/rules/member-ordering.ts @@ -343,11 +343,11 @@ const allMemberTypes = [ type !== 'readonly-signature' && type !== 'signature' && type !== 'call-signature' - ? // There is no `static-constructor` or `instance-constructor` or `abstract-constructor` - ( + ? ( [ 'static', 'instance', + // There is no `static-constructor` or `instance-constructor` or `abstract-constructor` ...(accessibility === '#private' || accessibility === 'private' ? [] From fda2a0ddcbe9bcec018f83cb69e8670cc0bfcf79 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Wed, 7 Aug 2024 16:35:07 -0500 Subject: [PATCH 35/40] revert no-duplicate-type-constituents --- .../rules/no-duplicate-type-constituents.ts | 71 ++++++++++--------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-duplicate-type-constituents.ts b/packages/eslint-plugin/src/rules/no-duplicate-type-constituents.ts index 4de77cb8fba1..d92de657aa47 100644 --- a/packages/eslint-plugin/src/rules/no-duplicate-type-constituents.ts +++ b/packages/eslint-plugin/src/rules/no-duplicate-type-constituents.ts @@ -108,41 +108,44 @@ export default createRule({ node: TSESTree.TSIntersectionType | TSESTree.TSUnionType, ): void { const cachedTypeMap = new Map(); - const uniqueConstituents: TSESTree.TypeNode[] = []; - for (const constituentNode of node.types) { - const constituentNodeType = - parserServices.getTypeAtLocation(constituentNode); - if (tsutils.isIntrinsicErrorType(constituentNodeType)) { - continue; - } - const duplicatedPreviousConstituentInAst = uniqueConstituents.find( - ele => isSameAstNode(ele, constituentNode), - ); - if (duplicatedPreviousConstituentInAst) { - reportDuplicate( - { - duplicated: constituentNode, - duplicatePrevious: duplicatedPreviousConstituentInAst, - }, - node, - ); - continue; - } - const duplicatedPreviousConstituentInType = - cachedTypeMap.get(constituentNodeType); - if (duplicatedPreviousConstituentInType) { - reportDuplicate( - { - duplicated: constituentNode, - duplicatePrevious: duplicatedPreviousConstituentInType, - }, - node, + node.types.reduce( + (uniqueConstituents, constituentNode) => { + const constituentNodeType = + parserServices.getTypeAtLocation(constituentNode); + if (tsutils.isIntrinsicErrorType(constituentNodeType)) { + return uniqueConstituents; + } + + const duplicatedPreviousConstituentInAst = uniqueConstituents.find( + ele => isSameAstNode(ele, constituentNode), ); - continue; - } - cachedTypeMap.set(constituentNodeType, constituentNode); - uniqueConstituents.push(constituentNode); - } + if (duplicatedPreviousConstituentInAst) { + reportDuplicate( + { + duplicated: constituentNode, + duplicatePrevious: duplicatedPreviousConstituentInAst, + }, + node, + ); + return uniqueConstituents; + } + const duplicatedPreviousConstituentInType = + cachedTypeMap.get(constituentNodeType); + if (duplicatedPreviousConstituentInType) { + reportDuplicate( + { + duplicated: constituentNode, + duplicatePrevious: duplicatedPreviousConstituentInType, + }, + node, + ); + return uniqueConstituents; + } + cachedTypeMap.set(constituentNodeType, constituentNode); + return [...uniqueConstituents, constituentNode]; + }, + [], + ); } function reportDuplicate( duplicateConstituent: { From 4d9c2f63940729ae27f7d6ef093b50a55d495b48 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Wed, 7 Aug 2024 16:55:44 -0500 Subject: [PATCH 36/40] ConfigTypeScript --- .../components/config/ConfigTypeScript.tsx | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/packages/website/src/components/config/ConfigTypeScript.tsx b/packages/website/src/components/config/ConfigTypeScript.tsx index 48711488a6e2..95f1ff998f63 100644 --- a/packages/website/src/components/config/ConfigTypeScript.tsx +++ b/packages/website/src/components/config/ConfigTypeScript.tsx @@ -4,7 +4,6 @@ import { ensureObject, parseJSONObject, toJson } from '../lib/json'; import { getTypescriptOptions } from '../lib/jsonSchema'; import { shallowEqual } from '../lib/shallowEqual'; import type { ConfigModel } from '../types'; -import type { ConfigOptionsType } from './ConfigEditor'; import ConfigEditor from './ConfigEditor'; interface ConfigTypeScriptProps { @@ -30,28 +29,31 @@ function ConfigTypeScript(props: ConfigTypeScriptProps): React.JSX.Element { }); }, [config]); - const options = useMemo((): ConfigOptionsType[] => { - const group: Record = {}; - for (const item of getTypescriptOptions()) { - const category = item.category.message; - group[category] ??= { heading: category, fields: [] }; - if (item.type === 'boolean') { - group[category].fields.push({ - key: item.name, - type: 'boolean', - label: item.description.message, - }); - } else if (item.type instanceof Map) { - group[category].fields.push({ - key: item.name, - type: 'string', - label: item.description.message, - enum: ['', ...Array.from(item.type.keys())], - }); - } - } - return Object.values(group); - }, []); + const options = useMemo( + () => + Object.entries( + Object.groupBy( + getTypescriptOptions(), + ({ category }) => category.message, + ), + ).map(([heading, group]) => ({ + heading, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + fields: group! + .map(({ name, description, type }) => ({ + key: name, + label: description.message, + ...(type === 'boolean' + ? { type: 'boolean' as const } + : type instanceof Map && { + type: 'string' as const, + enum: ['', ...Array.from(type.keys())], + }), + })) + .filter(item => 'type' in item), + })), + [], + ); const onChange = useCallback( (newConfig: Record) => { From 297ce7ed8f420cc93a8f902b3d9e1b1cf23d0f8f Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 8 Aug 2024 09:08:22 -0500 Subject: [PATCH 37/40] revert generate-sponsors --- tools/scripts/generate-sponsors.mts | 30 +++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/tools/scripts/generate-sponsors.mts b/tools/scripts/generate-sponsors.mts index efc5a57e2a6e..a75b0d550c91 100644 --- a/tools/scripts/generate-sponsors.mts +++ b/tools/scripts/generate-sponsors.mts @@ -119,19 +119,25 @@ async function main(): Promise { requestGraphql('collective'), ]); - const accountsById = Object.fromEntries( - account.orders.nodes.map(order => [ - order.fromAccount.name || order.fromAccount.id, - order.fromAccount, - ]), - ); - - const totalDonationsById: Record = {}; - for (const member of collective.members.nodes) { + const accountsById = account.orders.nodes.reduce< + Record + >((accumulator, account) => { + const name = account.fromAccount.name || account.fromAccount.id; + accumulator[name] = { + ...accumulator[name], + ...account.fromAccount, + }; + return accumulator; + }, {}); + + const totalDonationsById = collective.members.nodes.reduce< + Record + >((accumulator, member) => { const name = member.account.name || member.account.id; - totalDonationsById[name] ||= 0; - totalDonationsById[name] += member.totalDonations.valueInCents; - } + accumulator[name] ||= 0; + accumulator[name] += member.totalDonations.valueInCents; + return accumulator; + }, {}); const uniqueNames = new Set(excludedNames); const allSponsorsConfig = collective.members.nodes From 7ca4fac98bc457ce5edbea7cdbd2f008c3cd801b Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 8 Aug 2024 15:53:49 -0500 Subject: [PATCH 38/40] revert --- tools/scripts/generate-configs.mts | 62 ++++++++++++++++-------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/tools/scripts/generate-configs.mts b/tools/scripts/generate-configs.mts index b93176e51cf7..741302a6a96c 100644 --- a/tools/scripts/generate-configs.mts +++ b/tools/scripts/generate-configs.mts @@ -110,13 +110,13 @@ async function main(): Promise { /** * Helper function reduces records to key - value pairs. */ - function addRule( - rules: LinterConfigRules, + function reducer( + config: LinterConfigRules, [key, value]: RuleEntry, settings: ConfigRuleSettings = {}, - ): void { + ): LinterConfigRules { if (settings.deprecated && value.meta.deprecated) { - return; + return config; } // Explicitly exclude rules requiring type-checking @@ -124,14 +124,14 @@ async function main(): Promise { settings.typeChecked === 'exclude' && value.meta.docs.requiresTypeChecking === true ) { - return; + return config; } if ( settings.typeChecked === 'include-only' && value.meta.docs.requiresTypeChecking !== true ) { - return; + return config; } const ruleName = `${RULE_NAME_PREFIX}${key}`; @@ -149,7 +149,7 @@ async function main(): Promise { '=', chalk.green('off'), ); - rules[baseRuleName] = 'off'; + config[baseRuleName] = 'off'; } console.log( `${chalk.dim(RULE_NAME_PREFIX)}${key.padEnd(MAX_RULE_NAME_LENGTH)}`, @@ -160,10 +160,12 @@ async function main(): Promise { const ruleLevel = settings.forcedRuleLevel ?? 'error'; const ruleOptions = settings.getOptions?.(value); - rules[ruleName] = + config[ruleName] = ruleOptions && ruleOptions !== true ? [ruleLevel, ...ruleOptions] : ruleLevel; + + return config; } /** @@ -269,13 +271,16 @@ async function main(): Promise { ruleEntries, settings, }: ExtendedConfigSettings): Promise { - await writeConfig(() => { - const rules = {}; - for (const entry of ruleEntries) { - addRule(rules, entry, settings); - } - return { extends: [...CLASSIC_EXTENDS], rules }; - }, name); + await writeConfig( + () => ({ + extends: [...CLASSIC_EXTENDS], + rules: ruleEntries.reduce( + (config, entry) => reducer(config, entry, settings), + {}, + ), + }), + name, + ); } function filterRuleEntriesTo( @@ -380,24 +385,25 @@ async function main(): Promise { ruleEntries: filterRuleEntriesTo('stylistic'), }); - await writeConfig(() => { - const rules = {}; - for (const entry of allRuleEntries) { - addRule(rules, entry, { - typeChecked: 'include-only', - baseRuleForExtensionRule: 'exclude', - forcedRuleLevel: 'off', - }); - } - return { + await writeConfig( + () => ({ parserOptions: { project: false, program: null, projectService: false, }, - rules, - }; - }, 'disable-type-checked'); + rules: allRuleEntries.reduce( + (config, entry) => + reducer(config, entry, { + typeChecked: 'include-only', + baseRuleForExtensionRule: 'exclude', + forcedRuleLevel: 'off', + }), + {}, + ), + }), + 'disable-type-checked', + ); } main().catch((error: unknown) => { From c7d5cf863e7c9b2afc7fcc71711a0411191a3ee1 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Thu, 8 Aug 2024 15:54:17 -0500 Subject: [PATCH 39/40] remove lint rule --- eslint.config.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index d1fb4cc06698..17c8c44f2c18 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -320,7 +320,6 @@ export default tseslint.config( // 'jsdoc/informative-docs': 'error', - 'unicorn/no-array-reduce': 'error', 'unicorn/no-typeof-undefined': 'error', 'unicorn/no-useless-spread': 'error', 'unicorn/prefer-regexp-test': 'error', From 6b79534e65be0b79503333668a7bc88d32e2b2dc Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Tue, 27 Aug 2024 07:03:35 -0500 Subject: [PATCH 40/40] remove Object.groupBy because lib was downleveled --- .../components/config/ConfigTypeScript.tsx | 55 ++++++++++--------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/packages/website/src/components/config/ConfigTypeScript.tsx b/packages/website/src/components/config/ConfigTypeScript.tsx index 95f1ff998f63..1f45569df9c8 100644 --- a/packages/website/src/components/config/ConfigTypeScript.tsx +++ b/packages/website/src/components/config/ConfigTypeScript.tsx @@ -4,6 +4,7 @@ import { ensureObject, parseJSONObject, toJson } from '../lib/json'; import { getTypescriptOptions } from '../lib/jsonSchema'; import { shallowEqual } from '../lib/shallowEqual'; import type { ConfigModel } from '../types'; +import type { ConfigOptionsType } from './ConfigEditor'; import ConfigEditor from './ConfigEditor'; interface ConfigTypeScriptProps { @@ -29,31 +30,35 @@ function ConfigTypeScript(props: ConfigTypeScriptProps): React.JSX.Element { }); }, [config]); - const options = useMemo( - () => - Object.entries( - Object.groupBy( - getTypescriptOptions(), - ({ category }) => category.message, - ), - ).map(([heading, group]) => ({ - heading, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - fields: group! - .map(({ name, description, type }) => ({ - key: name, - label: description.message, - ...(type === 'boolean' - ? { type: 'boolean' as const } - : type instanceof Map && { - type: 'string' as const, - enum: ['', ...Array.from(type.keys())], - }), - })) - .filter(item => 'type' in item), - })), - [], - ); + const options = useMemo((): ConfigOptionsType[] => { + return Object.values( + getTypescriptOptions().reduce>( + (group, item) => { + const category = item.category.message; + group[category] ??= { + heading: category, + fields: [], + }; + if (item.type === 'boolean') { + group[category].fields.push({ + key: item.name, + type: 'boolean', + label: item.description.message, + }); + } else if (item.type instanceof Map) { + group[category].fields.push({ + key: item.name, + type: 'string', + label: item.description.message, + enum: ['', ...Array.from(item.type.keys())], + }); + } + return group; + }, + {}, + ), + ); + }, []); const onChange = useCallback( (newConfig: Record) => {