Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(typescript-eslint): make exported configs/plugin compatible with defineConfig() #11190

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 17 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions packages/eslint-plugin/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, ClassicConfig.Config>;
meta: FlatConfig.PluginMeta;
rules: typeof rules;
};
export = cjsExport;
63 changes: 46 additions & 17 deletions packages/eslint-plugin/raw-plugin.d.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,54 @@
import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint';
import type * as parserBase from '@typescript-eslint/parser';
import type { Linter } from '@typescript-eslint/utils/ts-eslint';

import type plugin from './index';
export interface TSESLintParser {
meta: typeof parserBase.meta;
parseForESLint: typeof parserBase.parseForESLint;
}

export interface TSESLintConfig {
name?: string;
rules?: Record<string, ['error' | 'off', ...unknown[]]>;
files?: string[];
languageOptions?: {
sourceType?: 'module';
parser?: TSESLintParser;
parserOptions?: Record<string, unknown>;
};

// causes type errors :shrug:
// plugins?: {
// '@typescript-eslint': TSESLintPlugin;
// };
}

export interface TSESLintPlugin {
configs: Record<string, TSESLintConfig | TSESLintConfig[]>;
meta: {
name: string;
version: string;
};
rules: Linter.PluginRules;
}

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': 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;
parser: TSESLintParser;
plugin: TSESLintPlugin;
};

export = cjsExport;
1 change: 1 addition & 0 deletions packages/typescript-eslint/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,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": "*",
Expand Down
3 changes: 2 additions & 1 deletion packages/typescript-eslint/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"outputs": ["{options.outputFile}"]
},
"test": {
"executor": "@nx/vite:test"
"executor": "@nx/vite:test",
"dependsOn": ["^build", "typecheck"]
}
}
}
29 changes: 22 additions & 7 deletions packages/typescript-eslint/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
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 { 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<eslintConfigHelpers.Config['languageOptions']>['parser']
>;

export const parser = rawPlugin.parser satisfies ESLintParserType;

/*
we could build a plugin object here without the `configs` key - but if we do
Expand All @@ -31,10 +39,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: TSESLint.FlatConfig.Plugin = pluginBase as Omit<
typeof pluginBase,
'configs'
>;
export const plugin = rawPlugin.plugin satisfies eslintConfigHelpers.Plugin;

export const configs = {
/**
Expand Down Expand Up @@ -120,8 +125,18 @@ export const configs = {
*/
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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't remember -- is this the loose type or the strict type?
Perhaps we should deprecate this to rename it? Or change the type to the ESLint types?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a loose one; it's just a T | Promise<T> version of the type of the parameter type of tseslint.config(...configs).

I think deprecating makes sense 👍


/*
Expand Down
28 changes: 28 additions & 0 deletions packages/typescript-eslint/tests/defineConfig-interop.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { defineConfig } from 'eslint/config';

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', () => {
expectTypeOf(defineConfig).toBeCallableWith(tseslint.configs.recommended);
});

it('should allow manual assignment of the plugin', () => {
expectTypeOf(defineConfig).toBeCallableWith({
plugins: {
ts: tseslint.plugin,
},
});
});

it('should allow manual assignment of the parser', () => {
expectTypeOf(defineConfig).toBeCallableWith({
languageOptions: {
parser: tseslint.parser,
},
});
});
});
5 changes: 5 additions & 0 deletions packages/typescript-eslint/vitest.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
},
},
}),
);
Expand Down
2 changes: 1 addition & 1 deletion packages/utils/src/ts-eslint/Parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
}
}
7 changes: 4 additions & 3 deletions packages/visitor-keys/src/visitor-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, readonly string[] | undefined>;
export type VisitorKeys = Record<string, string[]>;

type GetNodeTypeKeys<T extends AST_NODE_TYPES> = Exclude<
keyof Extract<TSESTree.Node, { type: T }>,
Expand Down Expand Up @@ -268,5 +268,6 @@ const additionalKeys: AdditionalKeys = {
TSVoidKeyword: [],
};

export const visitorKeys: VisitorKeys =
eslintVisitorKeys.unionWith(additionalKeys);
export const visitorKeys: VisitorKeys = eslintVisitorKeys.unionWith(
additionalKeys,
) satisfies Record<string, readonly string[]> as Record<string, string[]>;
3 changes: 2 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3448,7 +3448,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
Expand Down Expand Up @@ -18952,6 +18952,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.1
"@typescript-eslint/parser": 8.32.1
"@typescript-eslint/utils": 8.32.1
Expand Down