fix(linter): parse jsx-a11y/control-has-associated-label config with DefaultRuleConfig (#23373)

- Used `DefaultRuleConfig` to parse the `serde_json` value
- Moved the 25 cap, to the `Deserializer`
- added schema validation + deny unknown fields
- removed test cases where the upstream rule auto-generated options for every rule
This commit is contained in:
Sysix committed 2026-06-13 15:30:37 +00:00
1 parent e3fd735e59
commit 40556ad8b5
5 files changed
+704 -535

No files matched your search

+29 -1
View File
@@ -928,7 +928,10 @@ export interface DummyRuleMap {
"jsx-a11y/aria-unsupported-elements"?: RuleNoConfig;
"jsx-a11y/autocomplete-valid"?: AllowWarnDeny | [AllowWarnDeny] | [AllowWarnDeny, AutocompleteValidConfig];
"jsx-a11y/click-events-have-key-events"?: RuleNoConfig;
"jsx-a11y/control-has-associated-label"?: DummyRule;
"jsx-a11y/control-has-associated-label"?:
| AllowWarnDeny
| [AllowWarnDeny]
| [AllowWarnDeny, ControlHasAssociatedLabelConfig];
"jsx-a11y/heading-has-content"?: AllowWarnDeny | [AllowWarnDeny] | [AllowWarnDeny, HeadingHasContentConfig];
"jsx-a11y/html-has-lang"?: RuleNoConfig;
"jsx-a11y/iframe-has-title"?: RuleNoConfig;
@@ -2476,6 +2479,31 @@ export interface AutocompleteValidConfig {
*/
inputComponents?: string[];
}
export interface ControlHasAssociatedLabelConfig {
/**
* Custom JSX components to be treated as interactive controls.
*/
controlComponents?: string[];
/**
* Maximum depth to search for an accessible label within the element.
* Defaults to `2`.
*/
depth?: number;
/**
* Elements to ignore.
* Defaults to `["audio", "canvas", "embed", "input", "textarea", "tr", "video"]`.
*/
ignoreElements?: string[];
/**
* Interactive roles to ignore.
* Defaults to `["grid", "listbox", "menu", "menubar", "radiogroup", "row", "tablist", "toolbar", "tree", "treegrid"]`.
*/
ignoreRoles?: string[];
/**
* Additional attributes to check for accessible label text.
*/
labelAttributes?: string[];
}
export interface HeadingHasContentConfig {
/**
* Additional custom component names to treat as heading elements.
File diff suppressed because it is too large. Load diff
+1 -2
View File
@@ -13,7 +13,7 @@ use crate::rules::RuleEnum;
/// These should return the DummyRule struct/schema instead of the actual rule struct/schema,
/// until we verify that the rule's schema is valid and can be generated by schemars.
#[cfg(feature = "ruledocs")]
const NO_VERIFIED_VALID_SCHEMA: [&str; 27] = [
const NO_VERIFIED_VALID_SCHEMA: [&str; 26] = [
"eslint/func-name-matching", // second config can be on first position like [AllowWarnDeny, Config1, Config2] | [AllowWarnDeny, Config1] | [AllowWarnDeny, Config2]
"eslint/no-restricted-globals", // uses spread syntax like [AllowWarnDeny, ...Config]
"eslint/no-restricted-imports", // uses spread syntax like [AllowWarnDeny, ...Config]
@@ -27,7 +27,6 @@ const NO_VERIFIED_VALID_SCHEMA: [&str; 27] = [
"vitest/valid-title", // has options from other rule
"jsdoc/require-param",
"jsx_a11y/anchor-is-valid",
"jsx_a11y/control-has-associated-label",
"jsx_a11y/img-redundant-alt",
"jsx_a11y/label-has-associated-control",
"jsx_a11y/media-has-caption",
+87 -1
View File
@@ -1608,6 +1608,75 @@
},
"additionalProperties": false
},
"ControlHasAssociatedLabelConfig": {
"type": "object",
"properties": {
"controlComponents": {
"description": "Custom JSX components to be treated as interactive controls.",
"default": [],
"type": "array",
"items": {
"type": "string"
},
"markdownDescription": "Custom JSX components to be treated as interactive controls."
},
"depth": {
"description": "Maximum depth to search for an accessible label within the element.\nDefaults to `2`.",
"default": 2,
"type": "integer",
"format": "uint8",
"minimum": 0.0,
"markdownDescription": "Maximum depth to search for an accessible label within the element.\nDefaults to `2`."
},
"ignoreElements": {
"description": "Elements to ignore.\nDefaults to `[\"audio\", \"canvas\", \"embed\", \"input\", \"textarea\", \"tr\", \"video\"]`.",
"default": [
"audio",
"canvas",
"embed",
"input",
"textarea",
"tr",
"video"
],
"type": "array",
"items": {
"type": "string"
},
"markdownDescription": "Elements to ignore.\nDefaults to `[\"audio\", \"canvas\", \"embed\", \"input\", \"textarea\", \"tr\", \"video\"]`."
},
"ignoreRoles": {
"description": "Interactive roles to ignore.\nDefaults to `[\"grid\", \"listbox\", \"menu\", \"menubar\", \"radiogroup\", \"row\", \"tablist\", \"toolbar\", \"tree\", \"treegrid\"]`.",
"default": [
"grid",
"listbox",
"menu",
"menubar",
"radiogroup",
"row",
"tablist",
"toolbar",
"tree",
"treegrid"
],
"type": "array",
"items": {
"type": "string"
},
"markdownDescription": "Interactive roles to ignore.\nDefaults to `[\"grid\", \"listbox\", \"menu\", \"menubar\", \"radiogroup\", \"row\", \"tablist\", \"toolbar\", \"tree\", \"treegrid\"]`."
},
"labelAttributes": {
"description": "Additional attributes to check for accessible label text.",
"default": [],
"type": "array",
"items": {
"type": "string"
},
"markdownDescription": "Additional attributes to check for accessible label text."
}
},
"additionalProperties": false
},
"CountThis": {
"oneOf": [
{
@@ -3411,7 +3480,24 @@
"$ref": "#/definitions/RuleNoConfig"
},
"jsx-a11y/control-has-associated-label": {
"$ref": "#/definitions/DummyRule"
"anyOf": [
{
"$ref": "#/definitions/AllowWarnDeny"
},
{
"type": "array",
"items": [
{
"$ref": "#/definitions/AllowWarnDeny"
},
{
"$ref": "#/definitions/ControlHasAssociatedLabelConfig"
}
],
"maxItems": 2,
"minItems": 1
}
]
},
"jsx-a11y/heading-has-content": {
"anyOf": [
@@ -1612,6 +1612,75 @@ expression: json
},
"additionalProperties": false
},
"ControlHasAssociatedLabelConfig": {
"type": "object",
"properties": {
"controlComponents": {
"description": "Custom JSX components to be treated as interactive controls.",
"default": [],
"type": "array",
"items": {
"type": "string"
},
"markdownDescription": "Custom JSX components to be treated as interactive controls."
},
"depth": {
"description": "Maximum depth to search for an accessible label within the element.\nDefaults to `2`.",
"default": 2,
"type": "integer",
"format": "uint8",
"minimum": 0.0,
"markdownDescription": "Maximum depth to search for an accessible label within the element.\nDefaults to `2`."
},
"ignoreElements": {
"description": "Elements to ignore.\nDefaults to `[\"audio\", \"canvas\", \"embed\", \"input\", \"textarea\", \"tr\", \"video\"]`.",
"default": [
"audio",
"canvas",
"embed",
"input",
"textarea",
"tr",
"video"
],
"type": "array",
"items": {
"type": "string"
},
"markdownDescription": "Elements to ignore.\nDefaults to `[\"audio\", \"canvas\", \"embed\", \"input\", \"textarea\", \"tr\", \"video\"]`."
},
"ignoreRoles": {
"description": "Interactive roles to ignore.\nDefaults to `[\"grid\", \"listbox\", \"menu\", \"menubar\", \"radiogroup\", \"row\", \"tablist\", \"toolbar\", \"tree\", \"treegrid\"]`.",
"default": [
"grid",
"listbox",
"menu",
"menubar",
"radiogroup",
"row",
"tablist",
"toolbar",
"tree",
"treegrid"
],
"type": "array",
"items": {
"type": "string"
},
"markdownDescription": "Interactive roles to ignore.\nDefaults to `[\"grid\", \"listbox\", \"menu\", \"menubar\", \"radiogroup\", \"row\", \"tablist\", \"toolbar\", \"tree\", \"treegrid\"]`."
},
"labelAttributes": {
"description": "Additional attributes to check for accessible label text.",
"default": [],
"type": "array",
"items": {
"type": "string"
},
"markdownDescription": "Additional attributes to check for accessible label text."
}
},
"additionalProperties": false
},
"CountThis": {
"oneOf": [
{
@@ -3415,7 +3484,24 @@ expression: json
"$ref": "#/definitions/RuleNoConfig"
},
"jsx-a11y/control-has-associated-label": {
"$ref": "#/definitions/DummyRule"
"anyOf": [
{
"$ref": "#/definitions/AllowWarnDeny"
},
{
"type": "array",
"items": [
{
"$ref": "#/definitions/AllowWarnDeny"
},
{
"$ref": "#/definitions/ControlHasAssociatedLabelConfig"
}
],
"maxItems": 2,
"minItems": 1
}
]
},
"jsx-a11y/heading-has-content": {
"anyOf": [