diff --git a/packages/rule-tester/src/RuleTester.ts b/packages/rule-tester/src/RuleTester.ts index dfac9486ddb0..8bf01345594d 100644 --- a/packages/rule-tester/src/RuleTester.ts +++ b/packages/rule-tester/src/RuleTester.ts @@ -427,6 +427,7 @@ export class RuleTester extends TestFramework { defineRule(name: string, rule: AnyRuleModule): void { this.#rules[name] = { ...rule, + name, // Create a wrapper rule that freezes the `context` properties. create(context): RuleListener { freezeDeeply(context.options); diff --git a/packages/utils/src/eslint-utils/RuleCreator.ts b/packages/utils/src/eslint-utils/RuleCreator.ts index cd06aee6231f..ec1ceec5b381 100644 --- a/packages/utils/src/eslint-utils/RuleCreator.ts +++ b/packages/utils/src/eslint-utils/RuleCreator.ts @@ -36,6 +36,7 @@ export interface RuleWithMeta< Docs = unknown, > extends RuleCreateAndOptions { meta: RuleMetaData; + name?: string; } export interface RuleWithMetaAndName< @@ -47,6 +48,15 @@ export interface RuleWithMetaAndName< name: string; } +type RuleModuleWithName< + MessageIds extends string, + Options extends readonly unknown[] = [], + Docs = unknown, + ExtendedRuleListener extends RuleListener = RuleListener, +> = RuleModule & { + name: string; +}; + /** * Creates reusable function to create rules with default options and docs URLs. * @@ -67,8 +77,8 @@ export function RuleCreator( ...rule }: Readonly< RuleWithMetaAndName - >): RuleModule { - return createRule({ + >): RuleModuleWithName { + const ruleWithDocs = createRule({ meta: { ...meta, docs: { @@ -76,8 +86,11 @@ export function RuleCreator( url: urlCreator(name), }, }, + name, ...rule, }); + + return ruleWithDocs as RuleModuleWithName; }; } @@ -89,6 +102,7 @@ function createRule< create, defaultOptions, meta, + name, }: Readonly>): RuleModule< MessageIds, Options, @@ -101,6 +115,7 @@ function createRule< }, defaultOptions, meta, + name, }; } diff --git a/packages/utils/src/ts-eslint/Rule.ts b/packages/utils/src/ts-eslint/Rule.ts index 5f4395e36974..7b9b9c6961ca 100644 --- a/packages/utils/src/ts-eslint/Rule.ts +++ b/packages/utils/src/ts-eslint/Rule.ts @@ -737,6 +737,11 @@ export interface RuleModule< * Metadata about the rule */ meta: RuleMetaData; + + /** + * Rule name + */ + name?: string; } export type AnyRuleModule = RuleModule; diff --git a/packages/utils/tests/eslint-utils/RuleCreator.test.ts b/packages/utils/tests/eslint-utils/RuleCreator.test.ts index 625ee3646f2f..314402fcde07 100644 --- a/packages/utils/tests/eslint-utils/RuleCreator.test.ts +++ b/packages/utils/tests/eslint-utils/RuleCreator.test.ts @@ -42,5 +42,55 @@ describe(ESLintUtils.RuleCreator, () => { schema: [], type: 'problem', }); + expect(rule.name).toBe('test'); + }); + + it('withoutDocs should work without a `name`', () => { + const rule = ESLintUtils.RuleCreator.withoutDocs({ + create() { + return {}; + }, + defaultOptions: [], + meta: { + docs: { + description: 'some description', + }, + messages: { + foo: 'some message', + }, + schema: [], + type: 'problem', + }, + }); + + expect(rule.meta.docs).toEqual({ + description: 'some description', + }); + expect(rule.name).toBeUndefined(); + }); + + it('withoutDocs should work with a `name`', () => { + const rule = ESLintUtils.RuleCreator.withoutDocs({ + create() { + return {}; + }, + defaultOptions: [], + meta: { + docs: { + description: 'some description', + }, + messages: { + foo: 'some message', + }, + schema: [], + type: 'problem', + }, + name: 'some-name', + }); + + expect(rule.meta.docs).toEqual({ + description: 'some description', + }); + expect(rule.name).toBe('some-name'); }); });