From e09866f028e9a9ef60b299ae4a0e5f87c1f18ce8 Mon Sep 17 00:00:00 2001 From: Rel1cx Date: Thu, 19 Jun 2025 18:19:28 +0800 Subject: [PATCH] feat: add suggest to 'react-x/no-missing-button-type' --- .../src/rules/no-missing-button-type.spec.ts | 34 +++++++++++++++++++ .../src/rules/no-missing-button-type.ts | 28 +++++++++++++-- packages/utilities/kit/src/types.ts | 6 ++++ 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-button-type.spec.ts b/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-button-type.spec.ts index 38603b4ebb..c206b5280d 100644 --- a/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-button-type.spec.ts +++ b/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-button-type.spec.ts @@ -10,6 +10,23 @@ ruleTester.run(RULE_NAME, rule, { errors: [ { messageId: "noMissingButtonType", + suggestions: [ + { + messageId: "addButtonType", + data: { type: "button" }, + output: tsx`;`, + }, + { + messageId: "addButtonType", + data: { type: "submit" }, + output: tsx`;`, + }, + { + messageId: "addButtonType", + data: { type: "reset" }, + output: tsx`;`, + }, + ], }, ], }, @@ -18,6 +35,23 @@ ruleTester.run(RULE_NAME, rule, { errors: [ { messageId: "noMissingButtonType", + suggestions: [ + { + messageId: "addButtonType", + data: { type: "button" }, + output: tsx`Click me;`, + }, + { + messageId: "addButtonType", + data: { type: "submit" }, + output: tsx`Click me;`, + }, + { + messageId: "addButtonType", + data: { type: "reset" }, + output: tsx`Click me;`, + }, + ], }, ], settings: { diff --git a/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-button-type.ts b/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-button-type.ts index 3aa7e959d6..c7fd983f7f 100644 --- a/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-button-type.ts +++ b/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-button-type.ts @@ -1,5 +1,5 @@ -import type { RuleContext, RuleFeature } from "@eslint-react/kit"; -import type { RuleListener } from "@typescript-eslint/utils/ts-eslint"; +import type { RuleContext, RuleFeature, RuleSuggest } from "@eslint-react/kit"; +import type { RuleFixer, RuleListener } from "@typescript-eslint/utils/ts-eslint"; import type { CamelCase } from "string-ts"; import * as ER from "@eslint-react/core"; import { createJsxElementResolver, createRule, findCustomComponentProp } from "../utils"; @@ -8,7 +8,11 @@ export const RULE_NAME = "no-missing-button-type"; export const RULE_FEATURES = [] as const satisfies RuleFeature[]; -export type MessageID = CamelCase; +export const BUTTON_TYPES = ["button", "submit", "reset"] as const; + +export type MessageID = + | CamelCase + | "addButtonType"; export default createRule<[], MessageID>({ meta: { @@ -17,7 +21,9 @@ export default createRule<[], MessageID>({ description: "Enforces explicit `type` attribute for `button` elements.", [Symbol.for("rule_features")]: RULE_FEATURES, }, + hasSuggestions: true, messages: { + addButtonType: "Add 'type' attribute with value '{{type}}'.", noMissingButtonType: "Add missing 'type' attribute on 'button' component.", }, schema: [], @@ -51,6 +57,9 @@ export function create(context: RuleContext): RuleListener { context.report({ messageId: "noMissingButtonType", node: attributeNode, + suggest: getSuggest((type) => (fixer: RuleFixer) => { + return fixer.replaceText(node, `${propNameOnJsx}="${type}"`); + }), }); } return; @@ -59,8 +68,21 @@ export function create(context: RuleContext): RuleListener { context.report({ messageId: "noMissingButtonType", node, + suggest: getSuggest((type) => (fixer: RuleFixer) => { + const lastToken = context.sourceCode.getLastToken(node.openingElement); + if (lastToken == null) return null; + return fixer.insertTextBefore(lastToken, ` type="${type}"`); + }), }); } }, }; } + +function getSuggest(getFix: (type: string) => RuleSuggest["fix"]): RuleSuggest[] { + return BUTTON_TYPES.map((type) => ({ + messageId: "addButtonType", + data: { type }, + fix: getFix(type), + })); +} diff --git a/packages/utilities/kit/src/types.ts b/packages/utilities/kit/src/types.ts index 9fa8cd6120..a5c584edad 100644 --- a/packages/utilities/kit/src/types.ts +++ b/packages/utilities/kit/src/types.ts @@ -44,3 +44,9 @@ export type RuleFeature = | "EXP"; // Experimental export type RulePolicy = number; + +export type RuleSuggest = { + messageId: MessageIds; + data?: Record; + fix: tseslint.ReportFixFunction; +};