From 8e157655206721f1dfe50ade6339b98448c1052b Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Wed, 7 May 2025 18:05:56 -0600 Subject: [PATCH 01/15] poc --- packages/eslint-plugin/raw-plugin.d.ts | 28 +++++---- packages/typescript-eslint/src/index.ts | 60 ++++++++++++------- .../tests/defineConfig-interop.test.ts | 16 +++++ 3 files changed, 71 insertions(+), 33 deletions(-) create mode 100644 packages/typescript-eslint/tests/defineConfig-interop.test.ts diff --git a/packages/eslint-plugin/raw-plugin.d.ts b/packages/eslint-plugin/raw-plugin.d.ts index 25a093aba902..b233c2fdac77 100644 --- a/packages/eslint-plugin/raw-plugin.d.ts +++ b/packages/eslint-plugin/raw-plugin.d.ts @@ -2,21 +2,23 @@ import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint'; import type plugin from './index'; +import type * as eslintConfigHelpers from '@eslint/config-helpers'; + declare const cjsExport: { flatConfigs: { - 'flat/all': FlatConfig.ConfigArray; - 'flat/base': FlatConfig.Config; - 'flat/disable-type-checked': FlatConfig.Config; - 'flat/eslint-recommended': FlatConfig.Config; - 'flat/recommended': FlatConfig.ConfigArray; - 'flat/recommended-type-checked': FlatConfig.ConfigArray; - 'flat/recommended-type-checked-only': FlatConfig.ConfigArray; - 'flat/strict': FlatConfig.ConfigArray; - 'flat/strict-type-checked': FlatConfig.ConfigArray; - 'flat/strict-type-checked-only': FlatConfig.ConfigArray; - 'flat/stylistic': FlatConfig.ConfigArray; - 'flat/stylistic-type-checked': FlatConfig.ConfigArray; - 'flat/stylistic-type-checked-only': FlatConfig.ConfigArray; + 'flat/all': eslintConfigHelpers.ConfigWithExtendsArray; + 'flat/base': eslintConfigHelpers.Config; + 'flat/disable-type-checked': eslintConfigHelpers.Config; + 'flat/eslint-recommended': eslintConfigHelpers.Config; + 'flat/recommended': eslintConfigHelpers.ConfigWithExtendsArray; + 'flat/recommended-type-checked': eslintConfigHelpers.ConfigWithExtendsArray; + 'flat/recommended-type-checked-only': eslintConfigHelpers.ConfigWithExtendsArray; + 'flat/strict': eslintConfigHelpers.ConfigWithExtendsArray; + 'flat/strict-type-checked': eslintConfigHelpers.ConfigWithExtendsArray; + 'flat/strict-type-checked-only': eslintConfigHelpers.ConfigWithExtendsArray; + 'flat/stylistic': eslintConfigHelpers.ConfigWithExtendsArray; + 'flat/stylistic-type-checked': eslintConfigHelpers.ConfigWithExtendsArray; + 'flat/stylistic-type-checked-only': eslintConfigHelpers.ConfigWithExtendsArray; }; parser: FlatConfig.Parser; plugin: typeof plugin; diff --git a/packages/typescript-eslint/src/index.ts b/packages/typescript-eslint/src/index.ts index 86870c855cd0..b3c8b745f4c9 100644 --- a/packages/typescript-eslint/src/index.ts +++ b/packages/typescript-eslint/src/index.ts @@ -6,6 +6,8 @@ import rawPlugin from '@typescript-eslint/eslint-plugin/use-at-your-own-risk/raw import { config } from './config-helper'; +import type * as eslintConfigHelpers from '@eslint/config-helpers'; + export const parser: TSESLint.FlatConfig.Parser = rawPlugin.parser; /* @@ -31,30 +33,31 @@ use our new package); however legacy configs consumed via `@eslint/eslintrc` would never be able to satisfy this constraint and thus users would be blocked from using them. */ -export const plugin: TSESLint.FlatConfig.Plugin = pluginBase as Omit< - typeof pluginBase, - 'configs' ->; +export const plugin = pluginBase as unknown as eslintConfigHelpers.Plugin; export const configs = { /** * Enables each the rules provided as a part of typescript-eslint. Note that many rules are not applicable in all codebases, or are meant to be configured. * @see {@link https://typescript-eslint.io/users/configs#all} */ - all: rawPlugin.flatConfigs['flat/all'], + all: rawPlugin.flatConfigs[ + 'flat/all' + ] as eslintConfigHelpers.ConfigWithExtendsArray, /** * A minimal ruleset that sets only the required parser and plugin options needed to run typescript-eslint. * We don't recommend using this directly; instead, extend from an earlier recommended rule. * @see {@link https://typescript-eslint.io/users/configs#base} */ - base: rawPlugin.flatConfigs['flat/base'], + base: rawPlugin.flatConfigs['flat/base'] as eslintConfigHelpers.Config, /** * A utility ruleset that will disable type-aware linting and all type-aware rules available in our project. * @see {@link https://typescript-eslint.io/users/configs#disable-type-checked} */ - disableTypeChecked: rawPlugin.flatConfigs['flat/disable-type-checked'], + disableTypeChecked: rawPlugin.flatConfigs[ + 'flat/disable-type-checked' + ] as eslintConfigHelpers.Config, /** * This is a compatibility ruleset that: @@ -62,64 +65,81 @@ export const configs = { * - enables rules that make sense due to TS's typechecking / transpilation. * @see {@link https://typescript-eslint.io/users/configs/#eslint-recommended} */ - eslintRecommended: rawPlugin.flatConfigs['flat/eslint-recommended'], + eslintRecommended: rawPlugin.flatConfigs[ + 'flat/eslint-recommended' + ] as eslintConfigHelpers.Config, /** * Recommended rules for code correctness that you can drop in without additional configuration. * @see {@link https://typescript-eslint.io/users/configs#recommended} */ - recommended: rawPlugin.flatConfigs['flat/recommended'], + recommended: rawPlugin.flatConfigs[ + 'flat/recommended' + ] as eslintConfigHelpers.ConfigWithExtendsArray, /** * Contains all of `recommended` along with additional recommended rules that require type information. * @see {@link https://typescript-eslint.io/users/configs#recommended-type-checked} */ - recommendedTypeChecked: - rawPlugin.flatConfigs['flat/recommended-type-checked'], + recommendedTypeChecked: rawPlugin.flatConfigs[ + 'flat/recommended-type-checked' + ] as eslintConfigHelpers.ConfigWithExtendsArray, /** * A version of `recommended` that only contains type-checked rules and disables of any corresponding core ESLint rules. * @see {@link https://typescript-eslint.io/users/configs#recommended-type-checked-only} */ - recommendedTypeCheckedOnly: - rawPlugin.flatConfigs['flat/recommended-type-checked-only'], + recommendedTypeCheckedOnly: rawPlugin.flatConfigs[ + 'flat/recommended-type-checked-only' + ] as eslintConfigHelpers.ConfigWithExtendsArray, /** * Contains all of `recommended`, as well as additional strict rules that can also catch bugs. * @see {@link https://typescript-eslint.io/users/configs#strict} */ - strict: rawPlugin.flatConfigs['flat/strict'], + strict: rawPlugin.flatConfigs[ + 'flat/strict' + ] as eslintConfigHelpers.ConfigWithExtendsArray, /** * Contains all of `recommended`, `recommended-type-checked`, and `strict`, along with additional strict rules that require type information. * @see {@link https://typescript-eslint.io/users/configs#strict-type-checked} */ - strictTypeChecked: rawPlugin.flatConfigs['flat/strict-type-checked'], + strictTypeChecked: rawPlugin.flatConfigs[ + 'flat/strict-type-checked' + ] as eslintConfigHelpers.ConfigWithExtendsArray, /** * A version of `strict` that only contains type-checked rules and disables of any corresponding core ESLint rules. * @see {@link https://typescript-eslint.io/users/configs#strict-type-checked-only} */ - strictTypeCheckedOnly: rawPlugin.flatConfigs['flat/strict-type-checked-only'], + strictTypeCheckedOnly: rawPlugin.flatConfigs[ + 'flat/strict-type-checked-only' + ] as eslintConfigHelpers.ConfigWithExtendsArray, /** * Rules considered to be best practice for modern TypeScript codebases, but that do not impact program logic. * @see {@link https://typescript-eslint.io/users/configs#stylistic} */ - stylistic: rawPlugin.flatConfigs['flat/stylistic'], + stylistic: rawPlugin.flatConfigs[ + 'flat/stylistic' + ] as eslintConfigHelpers.ConfigWithExtendsArray, /** * Contains all of `stylistic`, along with additional stylistic rules that require type information. * @see {@link https://typescript-eslint.io/users/configs#stylistic-type-checked} */ - stylisticTypeChecked: rawPlugin.flatConfigs['flat/stylistic-type-checked'], + stylisticTypeChecked: rawPlugin.flatConfigs[ + 'flat/stylistic-type-checked' + ] as eslintConfigHelpers.ConfigWithExtendsArray, /** * A version of `stylistic` that only contains type-checked rules and disables of any corresponding core ESLint rules. * @see {@link https://typescript-eslint.io/users/configs#stylistic-type-checked-only} */ - stylisticTypeCheckedOnly: - rawPlugin.flatConfigs['flat/stylistic-type-checked-only'], + stylisticTypeCheckedOnly: rawPlugin.flatConfigs[ + 'flat/stylistic-type-checked-only' + ] as eslintConfigHelpers.ConfigWithExtendsArray, }; export type Config = TSESLint.FlatConfig.ConfigFile; diff --git a/packages/typescript-eslint/tests/defineConfig-interop.test.ts b/packages/typescript-eslint/tests/defineConfig-interop.test.ts new file mode 100644 index 000000000000..e35c8723e697 --- /dev/null +++ b/packages/typescript-eslint/tests/defineConfig-interop.test.ts @@ -0,0 +1,16 @@ +import { defineConfig } from 'eslint/config'; +import * as tseslint from '../src/index'; + +describe('tseslint.config() should be replaceable with defineConfig()', () => { + it('should work with recommended setup', () => { + defineConfig(tseslint.configs.recommended); + }); + + it('should allow manual assignment of the plugin', () => { + defineConfig({ + plugins: { + ts: tseslint.plugin, + }, + }); + }); +}); From a0775f0f5277baa7df670092dc67567489c179bf Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Wed, 7 May 2025 18:16:25 -0600 Subject: [PATCH 02/15] more --- packages/eslint-plugin/raw-plugin.d.ts | 23 +++++++------- packages/typescript-eslint/src/index.ts | 30 +++++++++---------- .../typescript-eslint/tests/configs.test.ts | 26 ++++++++-------- 3 files changed, 39 insertions(+), 40 deletions(-) diff --git a/packages/eslint-plugin/raw-plugin.d.ts b/packages/eslint-plugin/raw-plugin.d.ts index b233c2fdac77..5095ecaf23f1 100644 --- a/packages/eslint-plugin/raw-plugin.d.ts +++ b/packages/eslint-plugin/raw-plugin.d.ts @@ -1,24 +1,23 @@ +import type * as eslintConfigHelpers from '@eslint/config-helpers'; import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint'; import type plugin from './index'; -import type * as eslintConfigHelpers from '@eslint/config-helpers'; - declare const cjsExport: { flatConfigs: { - 'flat/all': eslintConfigHelpers.ConfigWithExtendsArray; + 'flat/all': eslintConfigHelpers.Config[]; 'flat/base': eslintConfigHelpers.Config; 'flat/disable-type-checked': eslintConfigHelpers.Config; 'flat/eslint-recommended': eslintConfigHelpers.Config; - 'flat/recommended': eslintConfigHelpers.ConfigWithExtendsArray; - 'flat/recommended-type-checked': eslintConfigHelpers.ConfigWithExtendsArray; - 'flat/recommended-type-checked-only': eslintConfigHelpers.ConfigWithExtendsArray; - 'flat/strict': eslintConfigHelpers.ConfigWithExtendsArray; - 'flat/strict-type-checked': eslintConfigHelpers.ConfigWithExtendsArray; - 'flat/strict-type-checked-only': eslintConfigHelpers.ConfigWithExtendsArray; - 'flat/stylistic': eslintConfigHelpers.ConfigWithExtendsArray; - 'flat/stylistic-type-checked': eslintConfigHelpers.ConfigWithExtendsArray; - 'flat/stylistic-type-checked-only': eslintConfigHelpers.ConfigWithExtendsArray; + 'flat/recommended': eslintConfigHelpers.Config[]; + 'flat/recommended-type-checked': eslintConfigHelpers.Config[]; + 'flat/recommended-type-checked-only': eslintConfigHelpers.Config[]; + 'flat/strict': eslintConfigHelpers.Config[]; + 'flat/strict-type-checked': eslintConfigHelpers.Config[]; + 'flat/strict-type-checked-only': eslintConfigHelpers.Config[]; + 'flat/stylistic': eslintConfigHelpers.Config[]; + 'flat/stylistic-type-checked': eslintConfigHelpers.Config[]; + 'flat/stylistic-type-checked-only': eslintConfigHelpers.Config[]; }; parser: FlatConfig.Parser; plugin: typeof plugin; diff --git a/packages/typescript-eslint/src/index.ts b/packages/typescript-eslint/src/index.ts index b3c8b745f4c9..6f94a309eed0 100644 --- a/packages/typescript-eslint/src/index.ts +++ b/packages/typescript-eslint/src/index.ts @@ -1,3 +1,5 @@ +import type * as eslintConfigHelpers from '@eslint/config-helpers'; + // see the comment in config-helper.ts for why this doesn't use /ts-eslint import type { TSESLint } from '@typescript-eslint/utils'; @@ -6,8 +8,6 @@ import rawPlugin from '@typescript-eslint/eslint-plugin/use-at-your-own-risk/raw import { config } from './config-helper'; -import type * as eslintConfigHelpers from '@eslint/config-helpers'; - export const parser: TSESLint.FlatConfig.Parser = rawPlugin.parser; /* @@ -35,14 +35,13 @@ from using them. */ export const plugin = pluginBase as unknown as eslintConfigHelpers.Plugin; +/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion -- These "unnecessary" assertions work around "requires explicit type annotation" errors. */ export const configs = { /** * Enables each the rules provided as a part of typescript-eslint. Note that many rules are not applicable in all codebases, or are meant to be configured. * @see {@link https://typescript-eslint.io/users/configs#all} */ - all: rawPlugin.flatConfigs[ - 'flat/all' - ] as eslintConfigHelpers.ConfigWithExtendsArray, + all: rawPlugin.flatConfigs['flat/all'] as eslintConfigHelpers.Config[], /** * A minimal ruleset that sets only the required parser and plugin options needed to run typescript-eslint. @@ -75,7 +74,7 @@ export const configs = { */ recommended: rawPlugin.flatConfigs[ 'flat/recommended' - ] as eslintConfigHelpers.ConfigWithExtendsArray, + ] as eslintConfigHelpers.Config[], /** * Contains all of `recommended` along with additional recommended rules that require type information. @@ -83,7 +82,7 @@ export const configs = { */ recommendedTypeChecked: rawPlugin.flatConfigs[ 'flat/recommended-type-checked' - ] as eslintConfigHelpers.ConfigWithExtendsArray, + ] as eslintConfigHelpers.Config[], /** * A version of `recommended` that only contains type-checked rules and disables of any corresponding core ESLint rules. @@ -91,15 +90,13 @@ export const configs = { */ recommendedTypeCheckedOnly: rawPlugin.flatConfigs[ 'flat/recommended-type-checked-only' - ] as eslintConfigHelpers.ConfigWithExtendsArray, + ] as eslintConfigHelpers.Config[], /** * Contains all of `recommended`, as well as additional strict rules that can also catch bugs. * @see {@link https://typescript-eslint.io/users/configs#strict} */ - strict: rawPlugin.flatConfigs[ - 'flat/strict' - ] as eslintConfigHelpers.ConfigWithExtendsArray, + strict: rawPlugin.flatConfigs['flat/strict'] as eslintConfigHelpers.Config[], /** * Contains all of `recommended`, `recommended-type-checked`, and `strict`, along with additional strict rules that require type information. @@ -107,7 +104,7 @@ export const configs = { */ strictTypeChecked: rawPlugin.flatConfigs[ 'flat/strict-type-checked' - ] as eslintConfigHelpers.ConfigWithExtendsArray, + ] as eslintConfigHelpers.Config[], /** * A version of `strict` that only contains type-checked rules and disables of any corresponding core ESLint rules. @@ -115,7 +112,7 @@ export const configs = { */ strictTypeCheckedOnly: rawPlugin.flatConfigs[ 'flat/strict-type-checked-only' - ] as eslintConfigHelpers.ConfigWithExtendsArray, + ] as eslintConfigHelpers.Config[], /** * Rules considered to be best practice for modern TypeScript codebases, but that do not impact program logic. @@ -123,7 +120,7 @@ export const configs = { */ stylistic: rawPlugin.flatConfigs[ 'flat/stylistic' - ] as eslintConfigHelpers.ConfigWithExtendsArray, + ] as eslintConfigHelpers.Config[], /** * Contains all of `stylistic`, along with additional stylistic rules that require type information. @@ -131,7 +128,7 @@ export const configs = { */ stylisticTypeChecked: rawPlugin.flatConfigs[ 'flat/stylistic-type-checked' - ] as eslintConfigHelpers.ConfigWithExtendsArray, + ] as eslintConfigHelpers.Config[], /** * A version of `stylistic` that only contains type-checked rules and disables of any corresponding core ESLint rules. @@ -139,8 +136,9 @@ export const configs = { */ stylisticTypeCheckedOnly: rawPlugin.flatConfigs[ 'flat/stylistic-type-checked-only' - ] as eslintConfigHelpers.ConfigWithExtendsArray, + ] as eslintConfigHelpers.Config[], }; +/* eslint-enable @typescript-eslint/no-unnecessary-type-assertion */ export type Config = TSESLint.FlatConfig.ConfigFile; diff --git a/packages/typescript-eslint/tests/configs.test.ts b/packages/typescript-eslint/tests/configs.test.ts index 742178196c81..3be135ea1e57 100644 --- a/packages/typescript-eslint/tests/configs.test.ts +++ b/packages/typescript-eslint/tests/configs.test.ts @@ -5,7 +5,7 @@ import type { import rules from '@typescript-eslint/eslint-plugin/use-at-your-own-risk/rules'; -import plugin from '../src/index'; +import tseslint from '../src/index'; const RULE_NAME_PREFIX = '@typescript-eslint/'; const EXTENSION_RULES = Object.entries(rules) @@ -107,7 +107,7 @@ function itHasBaseRulesOverriden( } describe('all.ts', () => { - const unfilteredConfigRules = plugin.configs.all[2]?.rules; + const unfilteredConfigRules = tseslint.configs.all[2]?.rules; it('contains all of the rules', () => { const configRules = filterRules(unfilteredConfigRules); @@ -125,7 +125,7 @@ describe('all.ts', () => { }); describe('disable-type-checked.ts', () => { - const unfilteredConfigRules = plugin.configs.disableTypeChecked.rules; + const unfilteredConfigRules = tseslint.configs.disableTypeChecked.rules; it('disables all type checked rules', () => { const configRules = filterRules(unfilteredConfigRules); @@ -141,7 +141,7 @@ describe('disable-type-checked.ts', () => { }); describe('recommended.ts', () => { - const unfilteredConfigRules = plugin.configs.recommended[2]?.rules; + const unfilteredConfigRules = tseslint.configs.recommended[2]?.rules; it('contains all recommended rules, excluding type checked ones', () => { const configRules = filterRules(unfilteredConfigRules); @@ -160,7 +160,8 @@ describe('recommended.ts', () => { }); describe('recommended-type-checked.ts', () => { - const unfilteredConfigRules = plugin.configs.recommendedTypeChecked[2]?.rules; + const unfilteredConfigRules = + tseslint.configs.recommendedTypeChecked[2]?.rules; it('contains all recommended rules', () => { const configRules = filterRules(unfilteredConfigRules); @@ -179,7 +180,7 @@ describe('recommended-type-checked.ts', () => { describe('recommended-type-checked-only.ts', () => { const unfilteredConfigRules = - plugin.configs.recommendedTypeCheckedOnly[2]?.rules; + tseslint.configs.recommendedTypeCheckedOnly[2]?.rules; it('contains only type-checked recommended rules', () => { const configRules = filterRules(unfilteredConfigRules); @@ -198,7 +199,7 @@ describe('recommended-type-checked-only.ts', () => { }); describe('strict.ts', () => { - const unfilteredConfigRules = plugin.configs.strict[2]?.rules; + const unfilteredConfigRules = tseslint.configs.strict[2]?.rules; it('contains all strict rules, excluding type checked ones', () => { const configRules = filterRules(unfilteredConfigRules); @@ -218,7 +219,7 @@ describe('strict.ts', () => { }); describe('strict-type-checked.ts', () => { - const unfilteredConfigRules = plugin.configs.strictTypeChecked[2]?.rules; + const unfilteredConfigRules = tseslint.configs.strictTypeChecked[2]?.rules; it('contains all strict rules', () => { const configRules = filterRules(unfilteredConfigRules); @@ -236,7 +237,8 @@ describe('strict-type-checked.ts', () => { }); describe('strict-type-checked-only.ts', () => { - const unfilteredConfigRules = plugin.configs.strictTypeCheckedOnly[2]?.rules; + const unfilteredConfigRules = + tseslint.configs.strictTypeCheckedOnly[2]?.rules; it('contains only type-checked strict rules', () => { const configRules = filterRules(unfilteredConfigRules); @@ -256,7 +258,7 @@ describe('strict-type-checked-only.ts', () => { }); describe('stylistic.ts', () => { - const unfilteredConfigRules = plugin.configs.stylistic[2]?.rules; + const unfilteredConfigRules = tseslint.configs.stylistic[2]?.rules; it('contains all stylistic rules, excluding deprecated or type checked ones', () => { const configRules = filterRules(unfilteredConfigRules); @@ -275,7 +277,7 @@ describe('stylistic.ts', () => { }); describe('stylistic-type-checked.ts', () => { - const unfilteredConfigRules = plugin.configs.stylisticTypeChecked[2]?.rules; + const unfilteredConfigRules = tseslint.configs.stylisticTypeChecked[2]?.rules; const configRules = filterRules(unfilteredConfigRules); // note: include deprecated rules so that the config doesn't change between major bumps const ruleConfigs = filterAndMapRuleConfigs({ @@ -293,7 +295,7 @@ describe('stylistic-type-checked.ts', () => { describe('stylistic-type-checked-only.ts', () => { const unfilteredConfigRules = - plugin.configs.stylisticTypeCheckedOnly[2]?.rules; + tseslint.configs.stylisticTypeCheckedOnly[2]?.rules; it('contains only type-checked stylistic rules', () => { const configRules = filterRules(unfilteredConfigRules); From 1bcab8a5caddc4f51ccef6e7a181f2feef5b6b36 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Wed, 7 May 2025 18:33:15 -0600 Subject: [PATCH 03/15] lintfix --- packages/typescript-eslint/tests/defineConfig-interop.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/typescript-eslint/tests/defineConfig-interop.test.ts b/packages/typescript-eslint/tests/defineConfig-interop.test.ts index e35c8723e697..517bd47aea0d 100644 --- a/packages/typescript-eslint/tests/defineConfig-interop.test.ts +++ b/packages/typescript-eslint/tests/defineConfig-interop.test.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'eslint/config'; + import * as tseslint from '../src/index'; describe('tseslint.config() should be replaceable with defineConfig()', () => { From 54341e091d54f388f510ae4b6d78551f390d6c5f Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Wed, 7 May 2025 18:36:38 -0600 Subject: [PATCH 04/15] revert unrelated change --- .../typescript-eslint/tests/configs.test.ts | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/packages/typescript-eslint/tests/configs.test.ts b/packages/typescript-eslint/tests/configs.test.ts index 3be135ea1e57..742178196c81 100644 --- a/packages/typescript-eslint/tests/configs.test.ts +++ b/packages/typescript-eslint/tests/configs.test.ts @@ -5,7 +5,7 @@ import type { import rules from '@typescript-eslint/eslint-plugin/use-at-your-own-risk/rules'; -import tseslint from '../src/index'; +import plugin from '../src/index'; const RULE_NAME_PREFIX = '@typescript-eslint/'; const EXTENSION_RULES = Object.entries(rules) @@ -107,7 +107,7 @@ function itHasBaseRulesOverriden( } describe('all.ts', () => { - const unfilteredConfigRules = tseslint.configs.all[2]?.rules; + const unfilteredConfigRules = plugin.configs.all[2]?.rules; it('contains all of the rules', () => { const configRules = filterRules(unfilteredConfigRules); @@ -125,7 +125,7 @@ describe('all.ts', () => { }); describe('disable-type-checked.ts', () => { - const unfilteredConfigRules = tseslint.configs.disableTypeChecked.rules; + const unfilteredConfigRules = plugin.configs.disableTypeChecked.rules; it('disables all type checked rules', () => { const configRules = filterRules(unfilteredConfigRules); @@ -141,7 +141,7 @@ describe('disable-type-checked.ts', () => { }); describe('recommended.ts', () => { - const unfilteredConfigRules = tseslint.configs.recommended[2]?.rules; + const unfilteredConfigRules = plugin.configs.recommended[2]?.rules; it('contains all recommended rules, excluding type checked ones', () => { const configRules = filterRules(unfilteredConfigRules); @@ -160,8 +160,7 @@ describe('recommended.ts', () => { }); describe('recommended-type-checked.ts', () => { - const unfilteredConfigRules = - tseslint.configs.recommendedTypeChecked[2]?.rules; + const unfilteredConfigRules = plugin.configs.recommendedTypeChecked[2]?.rules; it('contains all recommended rules', () => { const configRules = filterRules(unfilteredConfigRules); @@ -180,7 +179,7 @@ describe('recommended-type-checked.ts', () => { describe('recommended-type-checked-only.ts', () => { const unfilteredConfigRules = - tseslint.configs.recommendedTypeCheckedOnly[2]?.rules; + plugin.configs.recommendedTypeCheckedOnly[2]?.rules; it('contains only type-checked recommended rules', () => { const configRules = filterRules(unfilteredConfigRules); @@ -199,7 +198,7 @@ describe('recommended-type-checked-only.ts', () => { }); describe('strict.ts', () => { - const unfilteredConfigRules = tseslint.configs.strict[2]?.rules; + const unfilteredConfigRules = plugin.configs.strict[2]?.rules; it('contains all strict rules, excluding type checked ones', () => { const configRules = filterRules(unfilteredConfigRules); @@ -219,7 +218,7 @@ describe('strict.ts', () => { }); describe('strict-type-checked.ts', () => { - const unfilteredConfigRules = tseslint.configs.strictTypeChecked[2]?.rules; + const unfilteredConfigRules = plugin.configs.strictTypeChecked[2]?.rules; it('contains all strict rules', () => { const configRules = filterRules(unfilteredConfigRules); @@ -237,8 +236,7 @@ describe('strict-type-checked.ts', () => { }); describe('strict-type-checked-only.ts', () => { - const unfilteredConfigRules = - tseslint.configs.strictTypeCheckedOnly[2]?.rules; + const unfilteredConfigRules = plugin.configs.strictTypeCheckedOnly[2]?.rules; it('contains only type-checked strict rules', () => { const configRules = filterRules(unfilteredConfigRules); @@ -258,7 +256,7 @@ describe('strict-type-checked-only.ts', () => { }); describe('stylistic.ts', () => { - const unfilteredConfigRules = tseslint.configs.stylistic[2]?.rules; + const unfilteredConfigRules = plugin.configs.stylistic[2]?.rules; it('contains all stylistic rules, excluding deprecated or type checked ones', () => { const configRules = filterRules(unfilteredConfigRules); @@ -277,7 +275,7 @@ describe('stylistic.ts', () => { }); describe('stylistic-type-checked.ts', () => { - const unfilteredConfigRules = tseslint.configs.stylisticTypeChecked[2]?.rules; + const unfilteredConfigRules = plugin.configs.stylisticTypeChecked[2]?.rules; const configRules = filterRules(unfilteredConfigRules); // note: include deprecated rules so that the config doesn't change between major bumps const ruleConfigs = filterAndMapRuleConfigs({ @@ -295,7 +293,7 @@ describe('stylistic-type-checked.ts', () => { describe('stylistic-type-checked-only.ts', () => { const unfilteredConfigRules = - tseslint.configs.stylisticTypeCheckedOnly[2]?.rules; + plugin.configs.stylisticTypeCheckedOnly[2]?.rules; it('contains only type-checked stylistic rules', () => { const configRules = filterRules(unfilteredConfigRules); From 29e647d1cfed2a9667bf779e6455ed509eb3f4d4 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Wed, 7 May 2025 18:39:15 -0600 Subject: [PATCH 05/15] dep --- packages/typescript-eslint/package.json | 1 + yarn.lock | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/typescript-eslint/package.json b/packages/typescript-eslint/package.json index e9cd6e862734..f67c5081cbb7 100644 --- a/packages/typescript-eslint/package.json +++ b/packages/typescript-eslint/package.json @@ -51,6 +51,7 @@ "check-types": "npx nx typecheck" }, "dependencies": { + "@eslint/config-helpers": "^0.2", "@typescript-eslint/eslint-plugin": "8.32.0", "@typescript-eslint/parser": "8.32.0", "@typescript-eslint/utils": "8.32.0" diff --git a/yarn.lock b/yarn.lock index 1bbd20ef8cae..51652e1b89a2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3263,7 +3263,7 @@ __metadata: languageName: node linkType: hard -"@eslint/config-helpers@npm:^0.2.1": +"@eslint/config-helpers@npm:^0.2, @eslint/config-helpers@npm:^0.2.1": version: 0.2.2 resolution: "@eslint/config-helpers@npm:0.2.2" checksum: 8a4091a2c8af5366513647ccad720f184c1b723f04c086755797a3a5cac69dc9013bc8a75453d9fc188fc4364460f0eae9f1584b77b28082e0d26bf48356ae8f @@ -18701,6 +18701,7 @@ __metadata: version: 0.0.0-use.local resolution: "typescript-eslint@workspace:packages/typescript-eslint" dependencies: + "@eslint/config-helpers": ^0.2 "@typescript-eslint/eslint-plugin": 8.32.0 "@typescript-eslint/parser": 8.32.0 "@typescript-eslint/utils": 8.32.0 From ff95b9a493cc90dc18962ac67d7a5d93cad20219 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Fri, 9 May 2025 14:30:10 -0700 Subject: [PATCH 06/15] some feedback --- packages/typescript-eslint/src/index.ts | 75 +++++++++---------- .../tests/defineConfig-interop.test.ts | 8 ++ 2 files changed, 45 insertions(+), 38 deletions(-) diff --git a/packages/typescript-eslint/src/index.ts b/packages/typescript-eslint/src/index.ts index 6f94a309eed0..e5c78408abf6 100644 --- a/packages/typescript-eslint/src/index.ts +++ b/packages/typescript-eslint/src/index.ts @@ -8,7 +8,15 @@ import rawPlugin from '@typescript-eslint/eslint-plugin/use-at-your-own-risk/raw import { config } from './config-helper'; -export const parser: TSESLint.FlatConfig.Parser = rawPlugin.parser; +// Couldn't find a way to directly get to the eslint Linter.Parser type... +// We don't want to get it directly from eslint, since the supported eslint versions include eslint 8.x as well. +// '@eslint/config-helpers' doesn't re-export the Linter namespace, just some of its contents. +// So this is a bit yucky but it should get the type we want. +type ESLintParserType = NonNullable< + NonNullable['parser'] +>; + +export const parser = rawPlugin.parser as ESLintParserType; /* we could build a plugin object here without the `configs` key - but if we do @@ -35,28 +43,25 @@ from using them. */ export const plugin = pluginBase as unknown as eslintConfigHelpers.Plugin; -/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion -- These "unnecessary" assertions work around "requires explicit type annotation" errors. */ export const configs = { /** * Enables each the rules provided as a part of typescript-eslint. Note that many rules are not applicable in all codebases, or are meant to be configured. * @see {@link https://typescript-eslint.io/users/configs#all} */ - all: rawPlugin.flatConfigs['flat/all'] as eslintConfigHelpers.Config[], + all: rawPlugin.flatConfigs['flat/all'], /** * A minimal ruleset that sets only the required parser and plugin options needed to run typescript-eslint. * We don't recommend using this directly; instead, extend from an earlier recommended rule. * @see {@link https://typescript-eslint.io/users/configs#base} */ - base: rawPlugin.flatConfigs['flat/base'] as eslintConfigHelpers.Config, + base: rawPlugin.flatConfigs['flat/base'], /** * A utility ruleset that will disable type-aware linting and all type-aware rules available in our project. * @see {@link https://typescript-eslint.io/users/configs#disable-type-checked} */ - disableTypeChecked: rawPlugin.flatConfigs[ - 'flat/disable-type-checked' - ] as eslintConfigHelpers.Config, + disableTypeChecked: rawPlugin.flatConfigs['flat/disable-type-checked'], /** * This is a compatibility ruleset that: @@ -64,82 +69,76 @@ export const configs = { * - enables rules that make sense due to TS's typechecking / transpilation. * @see {@link https://typescript-eslint.io/users/configs/#eslint-recommended} */ - eslintRecommended: rawPlugin.flatConfigs[ - 'flat/eslint-recommended' - ] as eslintConfigHelpers.Config, + eslintRecommended: rawPlugin.flatConfigs['flat/eslint-recommended'], /** * Recommended rules for code correctness that you can drop in without additional configuration. * @see {@link https://typescript-eslint.io/users/configs#recommended} */ - recommended: rawPlugin.flatConfigs[ - 'flat/recommended' - ] as eslintConfigHelpers.Config[], + recommended: rawPlugin.flatConfigs['flat/recommended'], /** * Contains all of `recommended` along with additional recommended rules that require type information. * @see {@link https://typescript-eslint.io/users/configs#recommended-type-checked} */ - recommendedTypeChecked: rawPlugin.flatConfigs[ - 'flat/recommended-type-checked' - ] as eslintConfigHelpers.Config[], + recommendedTypeChecked: + rawPlugin.flatConfigs['flat/recommended-type-checked'], /** * A version of `recommended` that only contains type-checked rules and disables of any corresponding core ESLint rules. * @see {@link https://typescript-eslint.io/users/configs#recommended-type-checked-only} */ - recommendedTypeCheckedOnly: rawPlugin.flatConfigs[ - 'flat/recommended-type-checked-only' - ] as eslintConfigHelpers.Config[], + recommendedTypeCheckedOnly: + rawPlugin.flatConfigs['flat/recommended-type-checked-only'], /** * Contains all of `recommended`, as well as additional strict rules that can also catch bugs. * @see {@link https://typescript-eslint.io/users/configs#strict} */ - strict: rawPlugin.flatConfigs['flat/strict'] as eslintConfigHelpers.Config[], + strict: rawPlugin.flatConfigs['flat/strict'], /** * Contains all of `recommended`, `recommended-type-checked`, and `strict`, along with additional strict rules that require type information. * @see {@link https://typescript-eslint.io/users/configs#strict-type-checked} */ - strictTypeChecked: rawPlugin.flatConfigs[ - 'flat/strict-type-checked' - ] as eslintConfigHelpers.Config[], + strictTypeChecked: rawPlugin.flatConfigs['flat/strict-type-checked'], /** * A version of `strict` that only contains type-checked rules and disables of any corresponding core ESLint rules. * @see {@link https://typescript-eslint.io/users/configs#strict-type-checked-only} */ - strictTypeCheckedOnly: rawPlugin.flatConfigs[ - 'flat/strict-type-checked-only' - ] as eslintConfigHelpers.Config[], + strictTypeCheckedOnly: rawPlugin.flatConfigs['flat/strict-type-checked-only'], /** * Rules considered to be best practice for modern TypeScript codebases, but that do not impact program logic. * @see {@link https://typescript-eslint.io/users/configs#stylistic} */ - stylistic: rawPlugin.flatConfigs[ - 'flat/stylistic' - ] as eslintConfigHelpers.Config[], + stylistic: rawPlugin.flatConfigs['flat/stylistic'], /** * Contains all of `stylistic`, along with additional stylistic rules that require type information. * @see {@link https://typescript-eslint.io/users/configs#stylistic-type-checked} */ - stylisticTypeChecked: rawPlugin.flatConfigs[ - 'flat/stylistic-type-checked' - ] as eslintConfigHelpers.Config[], + stylisticTypeChecked: rawPlugin.flatConfigs['flat/stylistic-type-checked'], /** * A version of `stylistic` that only contains type-checked rules and disables of any corresponding core ESLint rules. * @see {@link https://typescript-eslint.io/users/configs#stylistic-type-checked-only} */ - stylisticTypeCheckedOnly: rawPlugin.flatConfigs[ - 'flat/stylistic-type-checked-only' - ] as eslintConfigHelpers.Config[], -}; -/* eslint-enable @typescript-eslint/no-unnecessary-type-assertion */ - + stylisticTypeCheckedOnly: + rawPlugin.flatConfigs['flat/stylistic-type-checked-only'], +} satisfies Record< + string, + eslintConfigHelpers.Config | eslintConfigHelpers.Config[] +>; + +/** + * The expected shape of the default export of an eslint flat config file. + * + * @deprecated ESLint core provides their own config types, and we now recommend using them instead. + * @see {@link https://github.com/typescript-eslint/typescript-eslint/issues/10899} + * @see {@link https://github.com/eslint/eslint/pull/19487} + */ export type Config = TSESLint.FlatConfig.ConfigFile; /* diff --git a/packages/typescript-eslint/tests/defineConfig-interop.test.ts b/packages/typescript-eslint/tests/defineConfig-interop.test.ts index 517bd47aea0d..2092ca1a494a 100644 --- a/packages/typescript-eslint/tests/defineConfig-interop.test.ts +++ b/packages/typescript-eslint/tests/defineConfig-interop.test.ts @@ -14,4 +14,12 @@ describe('tseslint.config() should be replaceable with defineConfig()', () => { }, }); }); + + it('should allow manual assignment of the parser', () => { + defineConfig({ + languageOptions: { + parser: tseslint.parser, + }, + }); + }); }); From 526480d84b8920c1ce4fd00a6637904deabbb30c Mon Sep 17 00:00:00 2001 From: Arya Emami Date: Fri, 9 May 2025 18:03:38 -0500 Subject: [PATCH 07/15] add type tests for `defineConfig` --- packages/typescript-eslint/project.json | 3 +- .../tests/defineConfig-interop.test-d.ts | 43 +++++++++++++++++++ .../tests/defineConfig-interop.test.ts | 25 ----------- packages/typescript-eslint/vitest.config.mts | 5 +++ 4 files changed, 50 insertions(+), 26 deletions(-) create mode 100644 packages/typescript-eslint/tests/defineConfig-interop.test-d.ts delete mode 100644 packages/typescript-eslint/tests/defineConfig-interop.test.ts diff --git a/packages/typescript-eslint/project.json b/packages/typescript-eslint/project.json index 121581ef2b13..1203fe9cd575 100644 --- a/packages/typescript-eslint/project.json +++ b/packages/typescript-eslint/project.json @@ -10,7 +10,8 @@ "outputs": ["{options.outputFile}"] }, "test": { - "executor": "@nx/vite:test" + "executor": "@nx/vite:test", + "dependsOn": ["^build", "typecheck"] } } } diff --git a/packages/typescript-eslint/tests/defineConfig-interop.test-d.ts b/packages/typescript-eslint/tests/defineConfig-interop.test-d.ts new file mode 100644 index 000000000000..66aed7d91a4e --- /dev/null +++ b/packages/typescript-eslint/tests/defineConfig-interop.test-d.ts @@ -0,0 +1,43 @@ +import { defineConfig } from 'eslint/config'; + +import type { Linter } from 'eslint'; + +import * as tseslint from '../src/index.js'; + +describe('tseslint.config() should be replaceable with defineConfig()', () => { + it('should work with recommended setup', () => { + const eslintConfig = defineConfig(tseslint.configs.recommended); + + const tseslintConfig = tseslint.config(tseslint.configs.recommended); + + expectTypeOf(defineConfig) + .toBeCallableWith(tseslint.configs.recommended) + .returns.items.toExtend<(typeof tseslintConfig)[number]>(); + + expectTypeOf(eslintConfig).toExtend(); + + expectTypeOf(eslintConfig).items.toExtend< + (typeof tseslintConfig)[number] + >(); + }); + + it('should allow manual assignment of the plugin', () => { + expectTypeOf(defineConfig) + .toBeCallableWith({ + plugins: { + ts: tseslint.plugin, + }, + }) + .returns.items.toEqualTypeOf(); + }); + + it('should allow manual assignment of the parser', () => { + expectTypeOf(defineConfig) + .toBeCallableWith({ + languageOptions: { + parser: tseslint.parser, + }, + }) + .returns.items.toEqualTypeOf(); + }); +}); diff --git a/packages/typescript-eslint/tests/defineConfig-interop.test.ts b/packages/typescript-eslint/tests/defineConfig-interop.test.ts deleted file mode 100644 index 2092ca1a494a..000000000000 --- a/packages/typescript-eslint/tests/defineConfig-interop.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { defineConfig } from 'eslint/config'; - -import * as tseslint from '../src/index'; - -describe('tseslint.config() should be replaceable with defineConfig()', () => { - it('should work with recommended setup', () => { - defineConfig(tseslint.configs.recommended); - }); - - it('should allow manual assignment of the plugin', () => { - defineConfig({ - plugins: { - ts: tseslint.plugin, - }, - }); - }); - - it('should allow manual assignment of the parser', () => { - defineConfig({ - languageOptions: { - parser: tseslint.parser, - }, - }); - }); -}); diff --git a/packages/typescript-eslint/vitest.config.mts b/packages/typescript-eslint/vitest.config.mts index 7d1cc31b83d7..29604585074c 100644 --- a/packages/typescript-eslint/vitest.config.mts +++ b/packages/typescript-eslint/vitest.config.mts @@ -14,6 +14,11 @@ const vitestConfig = mergeConfig( dir: path.join(import.meta.dirname, 'tests'), name: packageJson.name, root: import.meta.dirname, + + typecheck: { + enabled: true, + tsconfig: path.join(import.meta.dirname, 'tsconfig.spec.json'), + }, }, }), ); From 46e2e7e81b00ecab352f21ad7b54ef7214ced773 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Mon, 12 May 2025 15:19:44 -0700 Subject: [PATCH 08/15] tweaks --- .../tests/defineConfig-interop.test-d.ts | 43 ++++++------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/packages/typescript-eslint/tests/defineConfig-interop.test-d.ts b/packages/typescript-eslint/tests/defineConfig-interop.test-d.ts index 66aed7d91a4e..b9302a17de0d 100644 --- a/packages/typescript-eslint/tests/defineConfig-interop.test-d.ts +++ b/packages/typescript-eslint/tests/defineConfig-interop.test-d.ts @@ -1,43 +1,28 @@ import { defineConfig } from 'eslint/config'; -import type { Linter } from 'eslint'; - import * as tseslint from '../src/index.js'; +// Type tests to ensure that migration from `tseslint.config()` -> `defineConfig()` +// doesn't introduce type errors for the user. We don't care about the +// `defineConfig()` -> `tseslint.config()` direction. describe('tseslint.config() should be replaceable with defineConfig()', () => { it('should work with recommended setup', () => { - const eslintConfig = defineConfig(tseslint.configs.recommended); - - const tseslintConfig = tseslint.config(tseslint.configs.recommended); - - expectTypeOf(defineConfig) - .toBeCallableWith(tseslint.configs.recommended) - .returns.items.toExtend<(typeof tseslintConfig)[number]>(); - - expectTypeOf(eslintConfig).toExtend(); - - expectTypeOf(eslintConfig).items.toExtend< - (typeof tseslintConfig)[number] - >(); + expectTypeOf(defineConfig).toBeCallableWith(tseslint.configs.recommended); }); it('should allow manual assignment of the plugin', () => { - expectTypeOf(defineConfig) - .toBeCallableWith({ - plugins: { - ts: tseslint.plugin, - }, - }) - .returns.items.toEqualTypeOf(); + expectTypeOf(defineConfig).toBeCallableWith({ + plugins: { + ts: tseslint.plugin, + }, + }); }); it('should allow manual assignment of the parser', () => { - expectTypeOf(defineConfig) - .toBeCallableWith({ - languageOptions: { - parser: tseslint.parser, - }, - }) - .returns.items.toEqualTypeOf(); + expectTypeOf(defineConfig).toBeCallableWith({ + languageOptions: { + parser: tseslint.parser, + }, + }); }); }); From 9bb9f95c55ab28281b5bd8c197de52c8bde2639f Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Fri, 16 May 2025 11:43:36 -0600 Subject: [PATCH 09/15] undeclared dep --- packages/eslint-plugin/package.json | 1 + yarn.lock | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index c27d09067eab..21d7789b4170 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -61,6 +61,7 @@ }, "dependencies": { "@eslint-community/regexpp": "^4.10.0", + "@eslint/config-helpers": "^0.2", "@typescript-eslint/scope-manager": "8.32.1", "@typescript-eslint/type-utils": "8.32.1", "@typescript-eslint/utils": "8.32.1", diff --git a/yarn.lock b/yarn.lock index 661ea94c0dca..c0363d45f8a3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5528,6 +5528,7 @@ __metadata: resolution: "@typescript-eslint/eslint-plugin@workspace:packages/eslint-plugin" dependencies: "@eslint-community/regexpp": ^4.10.0 + "@eslint/config-helpers": ^0.2 "@types/mdast": ^4.0.3 "@types/natural-compare": "*" "@typescript-eslint/rule-schema-to-typescript-types": 8.32.1 From 6730308f2600c02cf93eb067ba0a4012dc7ee8c2 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Fri, 16 May 2025 11:55:52 -0600 Subject: [PATCH 10/15] lintfix --- packages/typescript-eslint/src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/typescript-eslint/src/index.ts b/packages/typescript-eslint/src/index.ts index e5c78408abf6..5a2b930d133f 100644 --- a/packages/typescript-eslint/src/index.ts +++ b/packages/typescript-eslint/src/index.ts @@ -1,5 +1,4 @@ import type * as eslintConfigHelpers from '@eslint/config-helpers'; - // see the comment in config-helper.ts for why this doesn't use /ts-eslint import type { TSESLint } from '@typescript-eslint/utils'; From 917613d786eff22586c3cbf55330ecfdefe369e2 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Mon, 2 Jun 2025 12:25:26 -0600 Subject: [PATCH 11/15] remove dependency on eslint types --- packages/eslint-plugin/package.json | 1 - packages/eslint-plugin/raw-plugin.d.ts | 42 +++++++++++++++-------- packages/typescript-eslint/package.json | 2 +- packages/utils/src/ts-eslint/Parser.ts | 2 +- packages/visitor-keys/src/visitor-keys.ts | 7 ++-- yarn.lock | 1 - 6 files changed, 34 insertions(+), 21 deletions(-) diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 21d7789b4170..c27d09067eab 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -61,7 +61,6 @@ }, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@eslint/config-helpers": "^0.2", "@typescript-eslint/scope-manager": "8.32.1", "@typescript-eslint/type-utils": "8.32.1", "@typescript-eslint/utils": "8.32.1", diff --git a/packages/eslint-plugin/raw-plugin.d.ts b/packages/eslint-plugin/raw-plugin.d.ts index 5095ecaf23f1..6bec8524e55a 100644 --- a/packages/eslint-plugin/raw-plugin.d.ts +++ b/packages/eslint-plugin/raw-plugin.d.ts @@ -1,23 +1,37 @@ -import type * as eslintConfigHelpers from '@eslint/config-helpers'; import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint'; +import type * as parserBase from '@typescript-eslint/parser'; + import type plugin from './index'; +type TSESLintConfig = { + name?: string; + rules?: Record; + languageOptions?: { + sourceType?: 'module'; + parser?: { + meta: typeof parserBase.meta; + parseForESLint: typeof parserBase.parseForESLint; + }; + parserOptions?: Record; + }; +}; + declare const cjsExport: { flatConfigs: { - 'flat/all': eslintConfigHelpers.Config[]; - 'flat/base': eslintConfigHelpers.Config; - 'flat/disable-type-checked': eslintConfigHelpers.Config; - 'flat/eslint-recommended': eslintConfigHelpers.Config; - 'flat/recommended': eslintConfigHelpers.Config[]; - 'flat/recommended-type-checked': eslintConfigHelpers.Config[]; - 'flat/recommended-type-checked-only': eslintConfigHelpers.Config[]; - 'flat/strict': eslintConfigHelpers.Config[]; - 'flat/strict-type-checked': eslintConfigHelpers.Config[]; - 'flat/strict-type-checked-only': eslintConfigHelpers.Config[]; - 'flat/stylistic': eslintConfigHelpers.Config[]; - 'flat/stylistic-type-checked': eslintConfigHelpers.Config[]; - 'flat/stylistic-type-checked-only': eslintConfigHelpers.Config[]; + 'flat/all': TSESLintConfig[]; + 'flat/base': TSESLintConfig; + 'flat/disable-type-checked': TSESLintConfig; + 'flat/eslint-recommended': TSESLintConfig; + 'flat/recommended': TSESLintConfig[]; + 'flat/recommended-type-checked': TSESLintConfig[]; + 'flat/recommended-type-checked-only': TSESLintConfig[]; + 'flat/strict': TSESLintConfig[]; + 'flat/strict-type-checked': TSESLintConfig[]; + 'flat/strict-type-checked-only': TSESLintConfig[]; + 'flat/stylistic': TSESLintConfig[]; + 'flat/stylistic-type-checked': TSESLintConfig[]; + 'flat/stylistic-type-checked-only': TSESLintConfig[]; }; parser: FlatConfig.Parser; plugin: typeof plugin; diff --git a/packages/typescript-eslint/package.json b/packages/typescript-eslint/package.json index d568ad92d08c..5d167792ec89 100644 --- a/packages/typescript-eslint/package.json +++ b/packages/typescript-eslint/package.json @@ -50,7 +50,6 @@ "check-types": "npx nx typecheck" }, "dependencies": { - "@eslint/config-helpers": "^0.2", "@typescript-eslint/eslint-plugin": "8.32.1", "@typescript-eslint/parser": "8.32.1", "@typescript-eslint/utils": "8.32.1" @@ -60,6 +59,7 @@ "typescript": ">=4.8.4 <5.9.0" }, "devDependencies": { + "@eslint/config-helpers": "^0.2", "@vitest/coverage-v8": "^3.1.3", "prettier": "^3.2.5", "rimraf": "*", diff --git a/packages/utils/src/ts-eslint/Parser.ts b/packages/utils/src/ts-eslint/Parser.ts index ad9f5763054f..0b1017fba37a 100644 --- a/packages/utils/src/ts-eslint/Parser.ts +++ b/packages/utils/src/ts-eslint/Parser.ts @@ -101,6 +101,6 @@ export namespace Parser { // eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style export interface VisitorKeys { - [nodeType: string]: readonly string[]; + [nodeType: string]: string[]; } } diff --git a/packages/visitor-keys/src/visitor-keys.ts b/packages/visitor-keys/src/visitor-keys.ts index 283d2f36e389..64e18f2f2911 100644 --- a/packages/visitor-keys/src/visitor-keys.ts +++ b/packages/visitor-keys/src/visitor-keys.ts @@ -2,7 +2,7 @@ import type { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/types'; import * as eslintVisitorKeys from 'eslint-visitor-keys'; -export type VisitorKeys = Record; +export type VisitorKeys = Record; type GetNodeTypeKeys = Exclude< keyof Extract, @@ -268,5 +268,6 @@ const additionalKeys: AdditionalKeys = { TSVoidKeyword: [], }; -export const visitorKeys: VisitorKeys = - eslintVisitorKeys.unionWith(additionalKeys); +export const visitorKeys: VisitorKeys = eslintVisitorKeys.unionWith( + additionalKeys, +) satisfies Record as Record; diff --git a/yarn.lock b/yarn.lock index c0363d45f8a3..661ea94c0dca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5528,7 +5528,6 @@ __metadata: resolution: "@typescript-eslint/eslint-plugin@workspace:packages/eslint-plugin" dependencies: "@eslint-community/regexpp": ^4.10.0 - "@eslint/config-helpers": ^0.2 "@types/mdast": ^4.0.3 "@types/natural-compare": "*" "@typescript-eslint/rule-schema-to-typescript-types": 8.32.1 From 82aee0687e8e849fa2948af55f9980c18075b399 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Mon, 2 Jun 2025 12:35:34 -0600 Subject: [PATCH 12/15] simplify --- packages/eslint-plugin/raw-plugin.d.ts | 12 +++++++----- packages/typescript-eslint/src/index.ts | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/eslint-plugin/raw-plugin.d.ts b/packages/eslint-plugin/raw-plugin.d.ts index 6bec8524e55a..b1156f5a87d5 100644 --- a/packages/eslint-plugin/raw-plugin.d.ts +++ b/packages/eslint-plugin/raw-plugin.d.ts @@ -4,15 +4,17 @@ import type * as parserBase from '@typescript-eslint/parser'; import type plugin from './index'; +type TSESLintParser = { + meta: typeof parserBase.meta; + parseForESLint: typeof parserBase.parseForESLint; +}; + type TSESLintConfig = { name?: string; rules?: Record; languageOptions?: { sourceType?: 'module'; - parser?: { - meta: typeof parserBase.meta; - parseForESLint: typeof parserBase.parseForESLint; - }; + parser?: TSESLintParser; parserOptions?: Record; }; }; @@ -33,7 +35,7 @@ declare const cjsExport: { 'flat/stylistic-type-checked': TSESLintConfig[]; 'flat/stylistic-type-checked-only': TSESLintConfig[]; }; - parser: FlatConfig.Parser; + parser: TSESLintParser; plugin: typeof plugin; }; diff --git a/packages/typescript-eslint/src/index.ts b/packages/typescript-eslint/src/index.ts index 5a2b930d133f..6e1631e5eb8a 100644 --- a/packages/typescript-eslint/src/index.ts +++ b/packages/typescript-eslint/src/index.ts @@ -15,7 +15,7 @@ type ESLintParserType = NonNullable< NonNullable['parser'] >; -export const parser = rawPlugin.parser as ESLintParserType; +export const parser = rawPlugin.parser satisfies ESLintParserType; /* we could build a plugin object here without the `configs` key - but if we do From 493bddebcae2a69a8add25f4791bb23a3d1bdbe4 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Mon, 2 Jun 2025 13:13:15 -0600 Subject: [PATCH 13/15] fix the plugin type --- packages/eslint-plugin/index.d.ts | 8 +++----- packages/eslint-plugin/raw-plugin.d.ts | 22 +++++++++++++++++++--- packages/typescript-eslint/src/index.ts | 7 ++++--- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/packages/eslint-plugin/index.d.ts b/packages/eslint-plugin/index.d.ts index 756a025eb97d..5d22546921aa 100644 --- a/packages/eslint-plugin/index.d.ts +++ b/packages/eslint-plugin/index.d.ts @@ -4,10 +4,8 @@ import type { } from '@typescript-eslint/utils/ts-eslint'; import type rules from './rules'; +import { TSESLintPlugin } from './raw-plugin'; + +declare const cjsExport: TSESLintPlugin; -declare const cjsExport: { - configs: Record; - meta: FlatConfig.PluginMeta; - rules: typeof rules; -}; export = cjsExport; diff --git a/packages/eslint-plugin/raw-plugin.d.ts b/packages/eslint-plugin/raw-plugin.d.ts index b1156f5a87d5..4db562a4aa4d 100644 --- a/packages/eslint-plugin/raw-plugin.d.ts +++ b/packages/eslint-plugin/raw-plugin.d.ts @@ -2,21 +2,37 @@ import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint'; import type * as parserBase from '@typescript-eslint/parser'; -import type plugin from './index'; +import type * as parserBase from '@typescript-eslint/parser'; +import { Linter } from '@typescript-eslint/utils/ts-eslint'; -type TSESLintParser = { +export type TSESLintParser = { meta: typeof parserBase.meta; parseForESLint: typeof parserBase.parseForESLint; }; -type TSESLintConfig = { +export type TSESLintConfig = { name?: string; rules?: Record; + files?: string[]; languageOptions?: { sourceType?: 'module'; parser?: TSESLintParser; parserOptions?: Record; }; + + // causes type errors :shrug: + // plugins?: { + // '@typescript-eslint': TSESLintPlugin; + // }; +}; + +export type TSESLintPlugin = { + configs: Record; + meta: { + name: string; + version: string; + }; + rules: Record; }; declare const cjsExport: { diff --git a/packages/typescript-eslint/src/index.ts b/packages/typescript-eslint/src/index.ts index 6e1631e5eb8a..85578bd37e79 100644 --- a/packages/typescript-eslint/src/index.ts +++ b/packages/typescript-eslint/src/index.ts @@ -2,8 +2,9 @@ import type * as eslintConfigHelpers from '@eslint/config-helpers'; // see the comment in config-helper.ts for why this doesn't use /ts-eslint import type { TSESLint } from '@typescript-eslint/utils'; -import pluginBase from '@typescript-eslint/eslint-plugin'; -import rawPlugin from '@typescript-eslint/eslint-plugin/use-at-your-own-risk/raw-plugin'; +import rawPlugin, { + TSESLintConfig, +} from '@typescript-eslint/eslint-plugin/use-at-your-own-risk/raw-plugin'; import { config } from './config-helper'; @@ -40,7 +41,7 @@ use our new package); however legacy configs consumed via `@eslint/eslintrc` would never be able to satisfy this constraint and thus users would be blocked from using them. */ -export const plugin = pluginBase as unknown as eslintConfigHelpers.Plugin; +export const plugin = rawPlugin.plugin satisfies eslintConfigHelpers.Plugin; export const configs = { /** From 08d57bac3fb3f067249821a80e2871b788a98107 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Mon, 2 Jun 2025 13:14:45 -0600 Subject: [PATCH 14/15] whoops --- packages/eslint-plugin/raw-plugin.d.ts | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/packages/eslint-plugin/raw-plugin.d.ts b/packages/eslint-plugin/raw-plugin.d.ts index 4db562a4aa4d..70c740960e73 100644 --- a/packages/eslint-plugin/raw-plugin.d.ts +++ b/packages/eslint-plugin/raw-plugin.d.ts @@ -1,18 +1,14 @@ -import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint'; - -import type * as parserBase from '@typescript-eslint/parser'; - import type * as parserBase from '@typescript-eslint/parser'; -import { Linter } from '@typescript-eslint/utils/ts-eslint'; +import type { Linter } from '@typescript-eslint/utils/ts-eslint'; -export type TSESLintParser = { +export interface TSESLintParser { meta: typeof parserBase.meta; parseForESLint: typeof parserBase.parseForESLint; -}; +} -export type TSESLintConfig = { +export interface TSESLintConfig { name?: string; - rules?: Record; + rules?: Record; files?: string[]; languageOptions?: { sourceType?: 'module'; @@ -24,16 +20,16 @@ export type TSESLintConfig = { // plugins?: { // '@typescript-eslint': TSESLintPlugin; // }; -}; +} -export type TSESLintPlugin = { +export interface TSESLintPlugin { configs: Record; meta: { name: string; version: string; }; rules: Record; -}; +} declare const cjsExport: { flatConfigs: { From 893c9594576fcbc467fbcd14f29c1c7b4c2652d5 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Mon, 2 Jun 2025 13:34:48 -0600 Subject: [PATCH 15/15] plugin --- packages/eslint-plugin/raw-plugin.d.ts | 6 +++--- packages/typescript-eslint/src/index.ts | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/eslint-plugin/raw-plugin.d.ts b/packages/eslint-plugin/raw-plugin.d.ts index 70c740960e73..e60f412fc4f4 100644 --- a/packages/eslint-plugin/raw-plugin.d.ts +++ b/packages/eslint-plugin/raw-plugin.d.ts @@ -8,7 +8,7 @@ export interface TSESLintParser { export interface TSESLintConfig { name?: string; - rules?: Record; + rules?: Record; files?: string[]; languageOptions?: { sourceType?: 'module'; @@ -28,7 +28,7 @@ export interface TSESLintPlugin { name: string; version: string; }; - rules: Record; + rules: Linter.PluginRules; } declare const cjsExport: { @@ -48,7 +48,7 @@ declare const cjsExport: { 'flat/stylistic-type-checked-only': TSESLintConfig[]; }; parser: TSESLintParser; - plugin: typeof plugin; + plugin: TSESLintPlugin; }; export = cjsExport; diff --git a/packages/typescript-eslint/src/index.ts b/packages/typescript-eslint/src/index.ts index 85578bd37e79..833137ca9491 100644 --- a/packages/typescript-eslint/src/index.ts +++ b/packages/typescript-eslint/src/index.ts @@ -2,9 +2,7 @@ import type * as eslintConfigHelpers from '@eslint/config-helpers'; // see the comment in config-helper.ts for why this doesn't use /ts-eslint import type { TSESLint } from '@typescript-eslint/utils'; -import rawPlugin, { - TSESLintConfig, -} from '@typescript-eslint/eslint-plugin/use-at-your-own-risk/raw-plugin'; +import rawPlugin from '@typescript-eslint/eslint-plugin/use-at-your-own-risk/raw-plugin'; import { config } from './config-helper';