From 7ba67ce1a6d276b3ea0258b51928c4f8bfbe0f4b Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Fri, 9 Dec 2022 16:15:06 +0000 Subject: [PATCH] feat(rome_js_analyze): complete `useAriaPropTypes` rule (#3959) --- .cargo/config.toml | 1 - Cargo.lock | 11 +- Cargo.toml | 3 +- crates/rome_aria/Cargo.toml | 2 +- crates/rome_aria/src/constants.rs | 140 ---- crates/rome_aria/src/generated.rs | 603 ------------------ crates/rome_aria/src/lib.rs | 6 +- crates/rome_aria/src/macros.rs | 31 +- crates/rome_aria/src/properties.rs | 430 +++++++++++-- crates/rome_aria/src/roles.rs | 41 +- crates/rome_aria_metadata/Cargo.toml | 17 + .../rome_aria_metadata/build.rs | 160 ++++- crates/rome_aria_metadata/src/lib.rs | 1 + .../nursery/use_aria_prop_types.rs | 169 ++++- crates/rome_js_analyze/src/lib.rs | 8 +- .../tests/specs/nursery/useAriaPropTypes.jsx | 1 - .../specs/nursery/useAriaPropTypes.jsx.snap | 32 - .../nursery/useAriaPropTypes/invalid.jsx | 15 + .../nursery/useAriaPropTypes/invalid.jsx.snap | 295 +++++++++ .../specs/nursery/useAriaPropTypes/valid.jsx | 19 + .../nursery/useAriaPropTypes/valid.jsx.snap | 29 + .../src/pages/lint/rules/useAriaPropTypes.md | 96 ++- xtask/codegen/Cargo.toml | 1 - xtask/codegen/src/main.rs | 7 - 24 files changed, 1204 insertions(+), 914 deletions(-) delete mode 100644 crates/rome_aria/src/constants.rs delete mode 100644 crates/rome_aria/src/generated.rs create mode 100644 crates/rome_aria_metadata/Cargo.toml rename xtask/codegen/src/generate_aria.rs => crates/rome_aria_metadata/build.rs (53%) create mode 100644 crates/rome_aria_metadata/src/lib.rs delete mode 100644 crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes.jsx delete mode 100644 crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes.jsx.snap create mode 100644 crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes/invalid.jsx create mode 100644 crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes/invalid.jsx.snap create mode 100644 crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes/valid.jsx create mode 100644 crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes/valid.jsx.snap diff --git a/.cargo/config.toml b/.cargo/config.toml index 0bdd71c311c..a6d9c212b10 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -5,7 +5,6 @@ codegen = "run -p xtask_codegen --" codegen-configuration = "run -p xtask_codegen --features configuration -- configuration" codegen-schema = "run -p xtask_codegen --features schema -- schema" codegen-bindings = "run -p xtask_codegen --features schema -- bindings" -codegen-aria = "run -p xtask_codegen --features aria -- aria" lintdoc = "run -p xtask_lintdoc --" documentation = """ doc \ diff --git a/Cargo.lock b/Cargo.lock index 8030ec0ca48..920b176e5e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1515,7 +1515,16 @@ dependencies = [ name = "rome_aria" version = "0.0.0" dependencies = [ - "rustc-hash", + "rome_aria_metadata", +] + +[[package]] +name = "rome_aria_metadata" +version = "0.0.0" +dependencies = [ + "case", + "proc-macro2", + "quote", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 61173c0f3b8..e55c7d52201 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,5 @@ dashmap = "5.4.0" rustc-hash = "1.1.0" countme = "3.0.1" tokio = { version = "1.15.0" } -insta = "1.21.2" \ No newline at end of file +insta = "1.21.2" +quote = { version = "1.0.21" } diff --git a/crates/rome_aria/Cargo.toml b/crates/rome_aria/Cargo.toml index dae915e266b..2ce444464ef 100644 --- a/crates/rome_aria/Cargo.toml +++ b/crates/rome_aria/Cargo.toml @@ -9,4 +9,4 @@ license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -rustc-hash = "1.1.0" \ No newline at end of file +rome_aria_metadata = { path = "../rome_aria_metadata" } \ No newline at end of file diff --git a/crates/rome_aria/src/constants.rs b/crates/rome_aria/src/constants.rs deleted file mode 100644 index d35e6e83df9..00000000000 --- a/crates/rome_aria/src/constants.rs +++ /dev/null @@ -1,140 +0,0 @@ -//! Metadata of: -//! - ARIA properties -//! - ARIA property types -//! - ARIA roles - -pub const ARIA_PROPERTIES: [&str; 48] = [ - "aria-activedescendant", - "aria-atomic", - "aria-autocomplete", - "aria-busy", - "aria-checked", - "aria-colcount", - "aria-colindex", - "aria-colspan", - "aria-controls", - "aria-current", - "aria-describedby", - "aria-details", - "aria-disabled", - "aria-dropeffect", - "aria-errormessage", - "aria-expanded", - "aria-flowto", - "aria-grabbed", - "aria-haspopup", - "aria-hidden", - "aria-invalid", - "aria-keyshortcuts", - "aria-label", - "aria-labelledby", - "aria-level", - "aria-live", - "aria-modal", - "aria-multiline", - "aria-multiselectable", - "aria-orientation", - "aria-owns", - "aria-placeholder", - "aria-posinset", - "aria-pressed", - "aria-readonly", - "aria-relevant", - "aria-required", - "aria-roledescription", - "aria-rowcount", - "aria-rowindex", - "aria-rowspan", - "aria-selected", - "aria-setsize", - "aria-sort", - "aria-valuemax", - "aria-valuemin", - "aria-valuenow", - "aria-valuetext", -]; - -pub const ARIA_PROPERTY_TYPE: [&str; 9] = [ - "boolean", - "id", - "idlist", - "integer", - "number", - "string", - "token", - "tokenlist", - "tristate", -]; - -pub const ARIA_WIDGET_ROLES: [&str; 27] = [ - "alert", - "alertdialog", - "button", - "checkbox", - "dialog", - "gridcell", - "link", - "log", - "marquee", - "menuitem", - "menuitemcheckbox", - "menuitemradio", - "option", - "progressbar", - "radio", - "scrollbar", - "searchbox", - "slider", - "spinbutton", - "status", - "switch", - "tab", - "tabpanel", - "textbox", - "timer", - "tooltip", - "treeitem", -]; - -pub const ARIA_ABSTRACT_ROLES: [&str; 12] = [ - "command", - "composite", - "input", - "landmark", - "range", - "roletype", - "section", - "sectionhead", - "select", - "structure", - "widget", - "window", -]; - -pub const ARIA_DOCUMENT_STRUCTURE_ROLES: [&str; 25] = [ - "article", - "cell", - "columnheader", - "definition", - "directory", - "document", - "feed", - "figure", - "group", - "heading", - "img", - "list", - "listitem", - "math", - "none", - "note", - "presentation", - "region", - "row", - "rowgroup", - "rowheader", - "separator", - "table", - "term", - "toolbar", -]; diff --git a/crates/rome_aria/src/generated.rs b/crates/rome_aria/src/generated.rs deleted file mode 100644 index 4a32c05e972..00000000000 --- a/crates/rome_aria/src/generated.rs +++ /dev/null @@ -1,603 +0,0 @@ -//! Generated file, do not edit by hand, see `xtask/codegen` - -#![allow(clippy::enum_variant_names)] -use std::str::FromStr; -#[derive(Debug, Eq, PartialEq)] -pub enum AriaPropertiesEnum { - AriaActivedescendant, - AriaAtomic, - AriaAutocomplete, - AriaBusy, - AriaChecked, - AriaColcount, - AriaColindex, - AriaColspan, - AriaControls, - AriaCurrent, - AriaDescribedby, - AriaDetails, - AriaDisabled, - AriaDropeffect, - AriaErrormessage, - AriaExpanded, - AriaFlowto, - AriaGrabbed, - AriaHaspopup, - AriaHidden, - AriaInvalid, - AriaKeyshortcuts, - AriaLabel, - AriaLabelledby, - AriaLevel, - AriaLive, - AriaModal, - AriaMultiline, - AriaMultiselectable, - AriaOrientation, - AriaOwns, - AriaPlaceholder, - AriaPosinset, - AriaPressed, - AriaReadonly, - AriaRelevant, - AriaRequired, - AriaRoledescription, - AriaRowcount, - AriaRowindex, - AriaRowspan, - AriaSelected, - AriaSetsize, - AriaSort, - AriaValuemax, - AriaValuemin, - AriaValuenow, - AriaValuetext, -} -impl From for &str { - fn from(property: AriaPropertiesEnum) -> Self { - match property { - AriaPropertiesEnum::AriaActivedescendant => "aria-activedescendant", - AriaPropertiesEnum::AriaAtomic => "aria-atomic", - AriaPropertiesEnum::AriaAutocomplete => "aria-autocomplete", - AriaPropertiesEnum::AriaBusy => "aria-busy", - AriaPropertiesEnum::AriaChecked => "aria-checked", - AriaPropertiesEnum::AriaColcount => "aria-colcount", - AriaPropertiesEnum::AriaColindex => "aria-colindex", - AriaPropertiesEnum::AriaColspan => "aria-colspan", - AriaPropertiesEnum::AriaControls => "aria-controls", - AriaPropertiesEnum::AriaCurrent => "aria-current", - AriaPropertiesEnum::AriaDescribedby => "aria-describedby", - AriaPropertiesEnum::AriaDetails => "aria-details", - AriaPropertiesEnum::AriaDisabled => "aria-disabled", - AriaPropertiesEnum::AriaDropeffect => "aria-dropeffect", - AriaPropertiesEnum::AriaErrormessage => "aria-errormessage", - AriaPropertiesEnum::AriaExpanded => "aria-expanded", - AriaPropertiesEnum::AriaFlowto => "aria-flowto", - AriaPropertiesEnum::AriaGrabbed => "aria-grabbed", - AriaPropertiesEnum::AriaHaspopup => "aria-haspopup", - AriaPropertiesEnum::AriaHidden => "aria-hidden", - AriaPropertiesEnum::AriaInvalid => "aria-invalid", - AriaPropertiesEnum::AriaKeyshortcuts => "aria-keyshortcuts", - AriaPropertiesEnum::AriaLabel => "aria-label", - AriaPropertiesEnum::AriaLabelledby => "aria-labelledby", - AriaPropertiesEnum::AriaLevel => "aria-level", - AriaPropertiesEnum::AriaLive => "aria-live", - AriaPropertiesEnum::AriaModal => "aria-modal", - AriaPropertiesEnum::AriaMultiline => "aria-multiline", - AriaPropertiesEnum::AriaMultiselectable => "aria-multiselectable", - AriaPropertiesEnum::AriaOrientation => "aria-orientation", - AriaPropertiesEnum::AriaOwns => "aria-owns", - AriaPropertiesEnum::AriaPlaceholder => "aria-placeholder", - AriaPropertiesEnum::AriaPosinset => "aria-posinset", - AriaPropertiesEnum::AriaPressed => "aria-pressed", - AriaPropertiesEnum::AriaReadonly => "aria-readonly", - AriaPropertiesEnum::AriaRelevant => "aria-relevant", - AriaPropertiesEnum::AriaRequired => "aria-required", - AriaPropertiesEnum::AriaRoledescription => "aria-roledescription", - AriaPropertiesEnum::AriaRowcount => "aria-rowcount", - AriaPropertiesEnum::AriaRowindex => "aria-rowindex", - AriaPropertiesEnum::AriaRowspan => "aria-rowspan", - AriaPropertiesEnum::AriaSelected => "aria-selected", - AriaPropertiesEnum::AriaSetsize => "aria-setsize", - AriaPropertiesEnum::AriaSort => "aria-sort", - AriaPropertiesEnum::AriaValuemax => "aria-valuemax", - AriaPropertiesEnum::AriaValuemin => "aria-valuemin", - AriaPropertiesEnum::AriaValuenow => "aria-valuenow", - AriaPropertiesEnum::AriaValuetext => "aria-valuetext", - } - } -} -impl FromStr for AriaPropertiesEnum { - type Err = String; - fn from_str(s: &str) -> Result { - match s { - "aria-activedescendant" => Ok(AriaPropertiesEnum::AriaActivedescendant), - "aria-atomic" => Ok(AriaPropertiesEnum::AriaAtomic), - "aria-autocomplete" => Ok(AriaPropertiesEnum::AriaAutocomplete), - "aria-busy" => Ok(AriaPropertiesEnum::AriaBusy), - "aria-checked" => Ok(AriaPropertiesEnum::AriaChecked), - "aria-colcount" => Ok(AriaPropertiesEnum::AriaColcount), - "aria-colindex" => Ok(AriaPropertiesEnum::AriaColindex), - "aria-colspan" => Ok(AriaPropertiesEnum::AriaColspan), - "aria-controls" => Ok(AriaPropertiesEnum::AriaControls), - "aria-current" => Ok(AriaPropertiesEnum::AriaCurrent), - "aria-describedby" => Ok(AriaPropertiesEnum::AriaDescribedby), - "aria-details" => Ok(AriaPropertiesEnum::AriaDetails), - "aria-disabled" => Ok(AriaPropertiesEnum::AriaDisabled), - "aria-dropeffect" => Ok(AriaPropertiesEnum::AriaDropeffect), - "aria-errormessage" => Ok(AriaPropertiesEnum::AriaErrormessage), - "aria-expanded" => Ok(AriaPropertiesEnum::AriaExpanded), - "aria-flowto" => Ok(AriaPropertiesEnum::AriaFlowto), - "aria-grabbed" => Ok(AriaPropertiesEnum::AriaGrabbed), - "aria-haspopup" => Ok(AriaPropertiesEnum::AriaHaspopup), - "aria-hidden" => Ok(AriaPropertiesEnum::AriaHidden), - "aria-invalid" => Ok(AriaPropertiesEnum::AriaInvalid), - "aria-keyshortcuts" => Ok(AriaPropertiesEnum::AriaKeyshortcuts), - "aria-label" => Ok(AriaPropertiesEnum::AriaLabel), - "aria-labelledby" => Ok(AriaPropertiesEnum::AriaLabelledby), - "aria-level" => Ok(AriaPropertiesEnum::AriaLevel), - "aria-live" => Ok(AriaPropertiesEnum::AriaLive), - "aria-modal" => Ok(AriaPropertiesEnum::AriaModal), - "aria-multiline" => Ok(AriaPropertiesEnum::AriaMultiline), - "aria-multiselectable" => Ok(AriaPropertiesEnum::AriaMultiselectable), - "aria-orientation" => Ok(AriaPropertiesEnum::AriaOrientation), - "aria-owns" => Ok(AriaPropertiesEnum::AriaOwns), - "aria-placeholder" => Ok(AriaPropertiesEnum::AriaPlaceholder), - "aria-posinset" => Ok(AriaPropertiesEnum::AriaPosinset), - "aria-pressed" => Ok(AriaPropertiesEnum::AriaPressed), - "aria-readonly" => Ok(AriaPropertiesEnum::AriaReadonly), - "aria-relevant" => Ok(AriaPropertiesEnum::AriaRelevant), - "aria-required" => Ok(AriaPropertiesEnum::AriaRequired), - "aria-roledescription" => Ok(AriaPropertiesEnum::AriaRoledescription), - "aria-rowcount" => Ok(AriaPropertiesEnum::AriaRowcount), - "aria-rowindex" => Ok(AriaPropertiesEnum::AriaRowindex), - "aria-rowspan" => Ok(AriaPropertiesEnum::AriaRowspan), - "aria-selected" => Ok(AriaPropertiesEnum::AriaSelected), - "aria-setsize" => Ok(AriaPropertiesEnum::AriaSetsize), - "aria-sort" => Ok(AriaPropertiesEnum::AriaSort), - "aria-valuemax" => Ok(AriaPropertiesEnum::AriaValuemax), - "aria-valuemin" => Ok(AriaPropertiesEnum::AriaValuemin), - "aria-valuenow" => Ok(AriaPropertiesEnum::AriaValuenow), - "aria-valuetext" => Ok(AriaPropertiesEnum::AriaValuetext), - _ => Err("aria property not implemented".to_string()), - } - } -} -impl AriaPropertiesEnum { - pub fn as_str(&self) -> &str { - match self { - AriaPropertiesEnum::AriaActivedescendant => "aria-activedescendant", - AriaPropertiesEnum::AriaAtomic => "aria-atomic", - AriaPropertiesEnum::AriaAutocomplete => "aria-autocomplete", - AriaPropertiesEnum::AriaBusy => "aria-busy", - AriaPropertiesEnum::AriaChecked => "aria-checked", - AriaPropertiesEnum::AriaColcount => "aria-colcount", - AriaPropertiesEnum::AriaColindex => "aria-colindex", - AriaPropertiesEnum::AriaColspan => "aria-colspan", - AriaPropertiesEnum::AriaControls => "aria-controls", - AriaPropertiesEnum::AriaCurrent => "aria-current", - AriaPropertiesEnum::AriaDescribedby => "aria-describedby", - AriaPropertiesEnum::AriaDetails => "aria-details", - AriaPropertiesEnum::AriaDisabled => "aria-disabled", - AriaPropertiesEnum::AriaDropeffect => "aria-dropeffect", - AriaPropertiesEnum::AriaErrormessage => "aria-errormessage", - AriaPropertiesEnum::AriaExpanded => "aria-expanded", - AriaPropertiesEnum::AriaFlowto => "aria-flowto", - AriaPropertiesEnum::AriaGrabbed => "aria-grabbed", - AriaPropertiesEnum::AriaHaspopup => "aria-haspopup", - AriaPropertiesEnum::AriaHidden => "aria-hidden", - AriaPropertiesEnum::AriaInvalid => "aria-invalid", - AriaPropertiesEnum::AriaKeyshortcuts => "aria-keyshortcuts", - AriaPropertiesEnum::AriaLabel => "aria-label", - AriaPropertiesEnum::AriaLabelledby => "aria-labelledby", - AriaPropertiesEnum::AriaLevel => "aria-level", - AriaPropertiesEnum::AriaLive => "aria-live", - AriaPropertiesEnum::AriaModal => "aria-modal", - AriaPropertiesEnum::AriaMultiline => "aria-multiline", - AriaPropertiesEnum::AriaMultiselectable => "aria-multiselectable", - AriaPropertiesEnum::AriaOrientation => "aria-orientation", - AriaPropertiesEnum::AriaOwns => "aria-owns", - AriaPropertiesEnum::AriaPlaceholder => "aria-placeholder", - AriaPropertiesEnum::AriaPosinset => "aria-posinset", - AriaPropertiesEnum::AriaPressed => "aria-pressed", - AriaPropertiesEnum::AriaReadonly => "aria-readonly", - AriaPropertiesEnum::AriaRelevant => "aria-relevant", - AriaPropertiesEnum::AriaRequired => "aria-required", - AriaPropertiesEnum::AriaRoledescription => "aria-roledescription", - AriaPropertiesEnum::AriaRowcount => "aria-rowcount", - AriaPropertiesEnum::AriaRowindex => "aria-rowindex", - AriaPropertiesEnum::AriaRowspan => "aria-rowspan", - AriaPropertiesEnum::AriaSelected => "aria-selected", - AriaPropertiesEnum::AriaSetsize => "aria-setsize", - AriaPropertiesEnum::AriaSort => "aria-sort", - AriaPropertiesEnum::AriaValuemax => "aria-valuemax", - AriaPropertiesEnum::AriaValuemin => "aria-valuemin", - AriaPropertiesEnum::AriaValuenow => "aria-valuenow", - AriaPropertiesEnum::AriaValuetext => "aria-valuetext", - } - } -} -#[derive(Debug, Eq, PartialEq)] -pub enum AriaPropertyTypeEnum { - Boolean, - Id, - Idlist, - Integer, - Number, - String, - Token, - Tokenlist, - Tristate, -} -impl From for &str { - fn from(property: AriaPropertyTypeEnum) -> Self { - match property { - AriaPropertyTypeEnum::Boolean => "boolean", - AriaPropertyTypeEnum::Id => "id", - AriaPropertyTypeEnum::Idlist => "idlist", - AriaPropertyTypeEnum::Integer => "integer", - AriaPropertyTypeEnum::Number => "number", - AriaPropertyTypeEnum::String => "string", - AriaPropertyTypeEnum::Token => "token", - AriaPropertyTypeEnum::Tokenlist => "tokenlist", - AriaPropertyTypeEnum::Tristate => "tristate", - } - } -} -impl FromStr for AriaPropertyTypeEnum { - type Err = String; - fn from_str(s: &str) -> Result { - match s { - "boolean" => Ok(AriaPropertyTypeEnum::Boolean), - "id" => Ok(AriaPropertyTypeEnum::Id), - "idlist" => Ok(AriaPropertyTypeEnum::Idlist), - "integer" => Ok(AriaPropertyTypeEnum::Integer), - "number" => Ok(AriaPropertyTypeEnum::Number), - "string" => Ok(AriaPropertyTypeEnum::String), - "token" => Ok(AriaPropertyTypeEnum::Token), - "tokenlist" => Ok(AriaPropertyTypeEnum::Tokenlist), - "tristate" => Ok(AriaPropertyTypeEnum::Tristate), - _ => Err("aria property not implemented".to_string()), - } - } -} -impl AriaPropertyTypeEnum { - pub fn as_str(&self) -> &str { - match self { - AriaPropertyTypeEnum::Boolean => "boolean", - AriaPropertyTypeEnum::Id => "id", - AriaPropertyTypeEnum::Idlist => "idlist", - AriaPropertyTypeEnum::Integer => "integer", - AriaPropertyTypeEnum::Number => "number", - AriaPropertyTypeEnum::String => "string", - AriaPropertyTypeEnum::Token => "token", - AriaPropertyTypeEnum::Tokenlist => "tokenlist", - AriaPropertyTypeEnum::Tristate => "tristate", - } - } -} -#[derive(Debug, Eq, PartialEq)] -pub enum AriaWidgetRolesEnum { - Alert, - Alertdialog, - Button, - Checkbox, - Dialog, - Gridcell, - Link, - Log, - Marquee, - Menuitem, - Menuitemcheckbox, - Menuitemradio, - Option, - Progressbar, - Radio, - Scrollbar, - Searchbox, - Slider, - Spinbutton, - Status, - Switch, - Tab, - Tabpanel, - Textbox, - Timer, - Tooltip, - Treeitem, -} -impl From for &str { - fn from(property: AriaWidgetRolesEnum) -> Self { - match property { - AriaWidgetRolesEnum::Alert => "alert", - AriaWidgetRolesEnum::Alertdialog => "alertdialog", - AriaWidgetRolesEnum::Button => "button", - AriaWidgetRolesEnum::Checkbox => "checkbox", - AriaWidgetRolesEnum::Dialog => "dialog", - AriaWidgetRolesEnum::Gridcell => "gridcell", - AriaWidgetRolesEnum::Link => "link", - AriaWidgetRolesEnum::Log => "log", - AriaWidgetRolesEnum::Marquee => "marquee", - AriaWidgetRolesEnum::Menuitem => "menuitem", - AriaWidgetRolesEnum::Menuitemcheckbox => "menuitemcheckbox", - AriaWidgetRolesEnum::Menuitemradio => "menuitemradio", - AriaWidgetRolesEnum::Option => "option", - AriaWidgetRolesEnum::Progressbar => "progressbar", - AriaWidgetRolesEnum::Radio => "radio", - AriaWidgetRolesEnum::Scrollbar => "scrollbar", - AriaWidgetRolesEnum::Searchbox => "searchbox", - AriaWidgetRolesEnum::Slider => "slider", - AriaWidgetRolesEnum::Spinbutton => "spinbutton", - AriaWidgetRolesEnum::Status => "status", - AriaWidgetRolesEnum::Switch => "switch", - AriaWidgetRolesEnum::Tab => "tab", - AriaWidgetRolesEnum::Tabpanel => "tabpanel", - AriaWidgetRolesEnum::Textbox => "textbox", - AriaWidgetRolesEnum::Timer => "timer", - AriaWidgetRolesEnum::Tooltip => "tooltip", - AriaWidgetRolesEnum::Treeitem => "treeitem", - } - } -} -impl FromStr for AriaWidgetRolesEnum { - type Err = String; - fn from_str(s: &str) -> Result { - match s { - "alert" => Ok(AriaWidgetRolesEnum::Alert), - "alertdialog" => Ok(AriaWidgetRolesEnum::Alertdialog), - "button" => Ok(AriaWidgetRolesEnum::Button), - "checkbox" => Ok(AriaWidgetRolesEnum::Checkbox), - "dialog" => Ok(AriaWidgetRolesEnum::Dialog), - "gridcell" => Ok(AriaWidgetRolesEnum::Gridcell), - "link" => Ok(AriaWidgetRolesEnum::Link), - "log" => Ok(AriaWidgetRolesEnum::Log), - "marquee" => Ok(AriaWidgetRolesEnum::Marquee), - "menuitem" => Ok(AriaWidgetRolesEnum::Menuitem), - "menuitemcheckbox" => Ok(AriaWidgetRolesEnum::Menuitemcheckbox), - "menuitemradio" => Ok(AriaWidgetRolesEnum::Menuitemradio), - "option" => Ok(AriaWidgetRolesEnum::Option), - "progressbar" => Ok(AriaWidgetRolesEnum::Progressbar), - "radio" => Ok(AriaWidgetRolesEnum::Radio), - "scrollbar" => Ok(AriaWidgetRolesEnum::Scrollbar), - "searchbox" => Ok(AriaWidgetRolesEnum::Searchbox), - "slider" => Ok(AriaWidgetRolesEnum::Slider), - "spinbutton" => Ok(AriaWidgetRolesEnum::Spinbutton), - "status" => Ok(AriaWidgetRolesEnum::Status), - "switch" => Ok(AriaWidgetRolesEnum::Switch), - "tab" => Ok(AriaWidgetRolesEnum::Tab), - "tabpanel" => Ok(AriaWidgetRolesEnum::Tabpanel), - "textbox" => Ok(AriaWidgetRolesEnum::Textbox), - "timer" => Ok(AriaWidgetRolesEnum::Timer), - "tooltip" => Ok(AriaWidgetRolesEnum::Tooltip), - "treeitem" => Ok(AriaWidgetRolesEnum::Treeitem), - _ => Err("aria property not implemented".to_string()), - } - } -} -impl AriaWidgetRolesEnum { - pub fn as_str(&self) -> &str { - match self { - AriaWidgetRolesEnum::Alert => "alert", - AriaWidgetRolesEnum::Alertdialog => "alertdialog", - AriaWidgetRolesEnum::Button => "button", - AriaWidgetRolesEnum::Checkbox => "checkbox", - AriaWidgetRolesEnum::Dialog => "dialog", - AriaWidgetRolesEnum::Gridcell => "gridcell", - AriaWidgetRolesEnum::Link => "link", - AriaWidgetRolesEnum::Log => "log", - AriaWidgetRolesEnum::Marquee => "marquee", - AriaWidgetRolesEnum::Menuitem => "menuitem", - AriaWidgetRolesEnum::Menuitemcheckbox => "menuitemcheckbox", - AriaWidgetRolesEnum::Menuitemradio => "menuitemradio", - AriaWidgetRolesEnum::Option => "option", - AriaWidgetRolesEnum::Progressbar => "progressbar", - AriaWidgetRolesEnum::Radio => "radio", - AriaWidgetRolesEnum::Scrollbar => "scrollbar", - AriaWidgetRolesEnum::Searchbox => "searchbox", - AriaWidgetRolesEnum::Slider => "slider", - AriaWidgetRolesEnum::Spinbutton => "spinbutton", - AriaWidgetRolesEnum::Status => "status", - AriaWidgetRolesEnum::Switch => "switch", - AriaWidgetRolesEnum::Tab => "tab", - AriaWidgetRolesEnum::Tabpanel => "tabpanel", - AriaWidgetRolesEnum::Textbox => "textbox", - AriaWidgetRolesEnum::Timer => "timer", - AriaWidgetRolesEnum::Tooltip => "tooltip", - AriaWidgetRolesEnum::Treeitem => "treeitem", - } - } -} -#[derive(Debug, Eq, PartialEq)] -pub enum AriaAbstractRolesEnum { - Command, - Composite, - Input, - Landmark, - Range, - Roletype, - Section, - Sectionhead, - Select, - Structure, - Widget, - Window, -} -impl From for &str { - fn from(property: AriaAbstractRolesEnum) -> Self { - match property { - AriaAbstractRolesEnum::Command => "command", - AriaAbstractRolesEnum::Composite => "composite", - AriaAbstractRolesEnum::Input => "input", - AriaAbstractRolesEnum::Landmark => "landmark", - AriaAbstractRolesEnum::Range => "range", - AriaAbstractRolesEnum::Roletype => "roletype", - AriaAbstractRolesEnum::Section => "section", - AriaAbstractRolesEnum::Sectionhead => "sectionhead", - AriaAbstractRolesEnum::Select => "select", - AriaAbstractRolesEnum::Structure => "structure", - AriaAbstractRolesEnum::Widget => "widget", - AriaAbstractRolesEnum::Window => "window", - } - } -} -impl FromStr for AriaAbstractRolesEnum { - type Err = String; - fn from_str(s: &str) -> Result { - match s { - "command" => Ok(AriaAbstractRolesEnum::Command), - "composite" => Ok(AriaAbstractRolesEnum::Composite), - "input" => Ok(AriaAbstractRolesEnum::Input), - "landmark" => Ok(AriaAbstractRolesEnum::Landmark), - "range" => Ok(AriaAbstractRolesEnum::Range), - "roletype" => Ok(AriaAbstractRolesEnum::Roletype), - "section" => Ok(AriaAbstractRolesEnum::Section), - "sectionhead" => Ok(AriaAbstractRolesEnum::Sectionhead), - "select" => Ok(AriaAbstractRolesEnum::Select), - "structure" => Ok(AriaAbstractRolesEnum::Structure), - "widget" => Ok(AriaAbstractRolesEnum::Widget), - "window" => Ok(AriaAbstractRolesEnum::Window), - _ => Err("aria property not implemented".to_string()), - } - } -} -impl AriaAbstractRolesEnum { - pub fn as_str(&self) -> &str { - match self { - AriaAbstractRolesEnum::Command => "command", - AriaAbstractRolesEnum::Composite => "composite", - AriaAbstractRolesEnum::Input => "input", - AriaAbstractRolesEnum::Landmark => "landmark", - AriaAbstractRolesEnum::Range => "range", - AriaAbstractRolesEnum::Roletype => "roletype", - AriaAbstractRolesEnum::Section => "section", - AriaAbstractRolesEnum::Sectionhead => "sectionhead", - AriaAbstractRolesEnum::Select => "select", - AriaAbstractRolesEnum::Structure => "structure", - AriaAbstractRolesEnum::Widget => "widget", - AriaAbstractRolesEnum::Window => "window", - } - } -} -#[derive(Debug, Eq, PartialEq)] -pub enum AriaDocumentStructureRolesEnum { - Article, - Cell, - Columnheader, - Definition, - Directory, - Document, - Feed, - Figure, - Group, - Heading, - Img, - List, - Listitem, - Math, - None, - Note, - Presentation, - Region, - Row, - Rowgroup, - Rowheader, - Separator, - Table, - Term, - Toolbar, -} -impl From for &str { - fn from(property: AriaDocumentStructureRolesEnum) -> Self { - match property { - AriaDocumentStructureRolesEnum::Article => "article", - AriaDocumentStructureRolesEnum::Cell => "cell", - AriaDocumentStructureRolesEnum::Columnheader => "columnheader", - AriaDocumentStructureRolesEnum::Definition => "definition", - AriaDocumentStructureRolesEnum::Directory => "directory", - AriaDocumentStructureRolesEnum::Document => "document", - AriaDocumentStructureRolesEnum::Feed => "feed", - AriaDocumentStructureRolesEnum::Figure => "figure", - AriaDocumentStructureRolesEnum::Group => "group", - AriaDocumentStructureRolesEnum::Heading => "heading", - AriaDocumentStructureRolesEnum::Img => "img", - AriaDocumentStructureRolesEnum::List => "list", - AriaDocumentStructureRolesEnum::Listitem => "listitem", - AriaDocumentStructureRolesEnum::Math => "math", - AriaDocumentStructureRolesEnum::None => "none", - AriaDocumentStructureRolesEnum::Note => "note", - AriaDocumentStructureRolesEnum::Presentation => "presentation", - AriaDocumentStructureRolesEnum::Region => "region", - AriaDocumentStructureRolesEnum::Row => "row", - AriaDocumentStructureRolesEnum::Rowgroup => "rowgroup", - AriaDocumentStructureRolesEnum::Rowheader => "rowheader", - AriaDocumentStructureRolesEnum::Separator => "separator", - AriaDocumentStructureRolesEnum::Table => "table", - AriaDocumentStructureRolesEnum::Term => "term", - AriaDocumentStructureRolesEnum::Toolbar => "toolbar", - } - } -} -impl FromStr for AriaDocumentStructureRolesEnum { - type Err = String; - fn from_str(s: &str) -> Result { - match s { - "article" => Ok(AriaDocumentStructureRolesEnum::Article), - "cell" => Ok(AriaDocumentStructureRolesEnum::Cell), - "columnheader" => Ok(AriaDocumentStructureRolesEnum::Columnheader), - "definition" => Ok(AriaDocumentStructureRolesEnum::Definition), - "directory" => Ok(AriaDocumentStructureRolesEnum::Directory), - "document" => Ok(AriaDocumentStructureRolesEnum::Document), - "feed" => Ok(AriaDocumentStructureRolesEnum::Feed), - "figure" => Ok(AriaDocumentStructureRolesEnum::Figure), - "group" => Ok(AriaDocumentStructureRolesEnum::Group), - "heading" => Ok(AriaDocumentStructureRolesEnum::Heading), - "img" => Ok(AriaDocumentStructureRolesEnum::Img), - "list" => Ok(AriaDocumentStructureRolesEnum::List), - "listitem" => Ok(AriaDocumentStructureRolesEnum::Listitem), - "math" => Ok(AriaDocumentStructureRolesEnum::Math), - "none" => Ok(AriaDocumentStructureRolesEnum::None), - "note" => Ok(AriaDocumentStructureRolesEnum::Note), - "presentation" => Ok(AriaDocumentStructureRolesEnum::Presentation), - "region" => Ok(AriaDocumentStructureRolesEnum::Region), - "row" => Ok(AriaDocumentStructureRolesEnum::Row), - "rowgroup" => Ok(AriaDocumentStructureRolesEnum::Rowgroup), - "rowheader" => Ok(AriaDocumentStructureRolesEnum::Rowheader), - "separator" => Ok(AriaDocumentStructureRolesEnum::Separator), - "table" => Ok(AriaDocumentStructureRolesEnum::Table), - "term" => Ok(AriaDocumentStructureRolesEnum::Term), - "toolbar" => Ok(AriaDocumentStructureRolesEnum::Toolbar), - _ => Err("aria property not implemented".to_string()), - } - } -} -impl AriaDocumentStructureRolesEnum { - pub fn as_str(&self) -> &str { - match self { - AriaDocumentStructureRolesEnum::Article => "article", - AriaDocumentStructureRolesEnum::Cell => "cell", - AriaDocumentStructureRolesEnum::Columnheader => "columnheader", - AriaDocumentStructureRolesEnum::Definition => "definition", - AriaDocumentStructureRolesEnum::Directory => "directory", - AriaDocumentStructureRolesEnum::Document => "document", - AriaDocumentStructureRolesEnum::Feed => "feed", - AriaDocumentStructureRolesEnum::Figure => "figure", - AriaDocumentStructureRolesEnum::Group => "group", - AriaDocumentStructureRolesEnum::Heading => "heading", - AriaDocumentStructureRolesEnum::Img => "img", - AriaDocumentStructureRolesEnum::List => "list", - AriaDocumentStructureRolesEnum::Listitem => "listitem", - AriaDocumentStructureRolesEnum::Math => "math", - AriaDocumentStructureRolesEnum::None => "none", - AriaDocumentStructureRolesEnum::Note => "note", - AriaDocumentStructureRolesEnum::Presentation => "presentation", - AriaDocumentStructureRolesEnum::Region => "region", - AriaDocumentStructureRolesEnum::Row => "row", - AriaDocumentStructureRolesEnum::Rowgroup => "rowgroup", - AriaDocumentStructureRolesEnum::Rowheader => "rowheader", - AriaDocumentStructureRolesEnum::Separator => "separator", - AriaDocumentStructureRolesEnum::Table => "table", - AriaDocumentStructureRolesEnum::Term => "term", - AriaDocumentStructureRolesEnum::Toolbar => "toolbar", - } - } -} diff --git a/crates/rome_aria/src/lib.rs b/crates/rome_aria/src/lib.rs index 53601c575b4..3368ec63fc7 100644 --- a/crates/rome_aria/src/lib.rs +++ b/crates/rome_aria/src/lib.rs @@ -1,17 +1,13 @@ use std::str::FromStr; -#[rustfmt::skip] -mod generated; - -pub mod constants; mod macros; pub mod properties; pub mod roles; -use crate::generated::{AriaPropertiesEnum, AriaPropertyTypeEnum}; pub use properties::AriaProperties; pub(crate) use roles::AriaRoleDefinition; pub use roles::AriaRoles; +pub use rome_aria_metadata::{AriaPropertiesEnum, AriaPropertyTypeEnum}; /// It checks if an ARIA property is valid /// diff --git a/crates/rome_aria/src/macros.rs b/crates/rome_aria/src/macros.rs index 65526e0bac7..0308a853efc 100644 --- a/crates/rome_aria/src/macros.rs +++ b/crates/rome_aria/src/macros.rs @@ -14,13 +14,40 @@ macro_rules! define_role { } impl $crate::AriaRoleDefinition for $id { - fn properties<'a>(&self) -> std::slice::Iter<'a, (&str, bool)> { + fn properties(&self) -> std::slice::Iter<(&str, bool)> { $id::PROPS.iter() } - fn roles<'a>(&self) -> std::slice::Iter<'a, &str> { + fn roles(&self) -> std::slice::Iter<&str> { $id::ROLES.iter() } } }; } + +#[macro_export] +macro_rules! define_property { + ( $id:ident { + PROPERTY_TYPE: $property_type:literal, + VALUES: $values:expr, + }) => { + #[derive(Debug)] + struct $id; + + impl $id { + const PROPERTY_TYPE: &'static str = &$property_type; + const VALUES: &[&'static str] = &$values; + } + + impl AriaPropertyDefinition for $id { + fn values(&self) -> std::slice::Iter<&'static str> { + $id::VALUES.iter() + } + + fn property_type(&self) -> $crate::AriaPropertyTypeEnum { + // SAFETY: PROPERTY_TYPE is internal and should not contain extraneous properties + $crate::AriaPropertyTypeEnum::from_str($id::PROPERTY_TYPE).unwrap() + } + } + }; +} diff --git a/crates/rome_aria/src/properties.rs b/crates/rome_aria/src/properties.rs index afa75574392..6b312ff76ed 100644 --- a/crates/rome_aria/src/properties.rs +++ b/crates/rome_aria/src/properties.rs @@ -1,38 +1,404 @@ -use crate::generated::AriaPropertyTypeEnum; -use rustc_hash::FxHashMap; +use crate::define_property; +use rome_aria_metadata::AriaPropertyTypeEnum; use std::fmt::Debug; use std::slice::Iter; use std::str::FromStr; -/// A collection of ARIA properties with their metadata, necessary to perform various operations. -#[derive(Debug)] -pub struct AriaProperties(FxHashMap<&'static str, Box>); +define_property! { + AriaActivedescendant { + PROPERTY_TYPE: "id", + VALUES: [], + } +} -impl Default for AriaProperties { - fn default() -> Self { - let list = FxHashMap::default(); - Self(list).add("aria-current", AriaCurrent) +define_property! { + AriaAtomic { + PROPERTY_TYPE: "boolean", + VALUES: [], } } -impl AriaProperties { - fn add( - mut self, - aria_property_name: &'static str, - definition: impl AriaPropertyDefinition + 'static, - ) -> Self { - self.0.insert(aria_property_name, Box::new(definition)); - self +define_property! { + AriaAutocomplete { + PROPERTY_TYPE: "token", + VALUES: ["inline", "list", "both", "none"], + } +} + +define_property! { + AriaBusy { + PROPERTY_TYPE: "boolean", + VALUES: [], + } +} + +define_property! { + AriaChecked { + PROPERTY_TYPE: "tristate", + VALUES: [], + } +} + +define_property! { + AriaColcount { + PROPERTY_TYPE: "integer", + VALUES: [], + } +} + +define_property! { + AriaColindex { + PROPERTY_TYPE: "integer", + VALUES: [], + } +} + +define_property! { + AriaColspan { + PROPERTY_TYPE: "integer", + VALUES: [], } +} + +define_property! { + AriaControls { + PROPERTY_TYPE: "idlist", + VALUES: [], + } +} + +define_property! { + AriaCurrent { + PROPERTY_TYPE: "token", + VALUES: ["page", "step", "location", "date", "time", "true", "false"], + } +} + +define_property! { + AriaDescribedby { + PROPERTY_TYPE: "idlist", + VALUES: [], + } +} + +define_property! { + AriaDetails { + PROPERTY_TYPE: "id", + VALUES: [], + } +} + +define_property! { + AriaDisabled { + PROPERTY_TYPE: "boolean", + VALUES: [], + } +} + +define_property! { + AriaDropeffect { + PROPERTY_TYPE: "tokenlist", + VALUES: ["copy", "execute", "link", "move", "none", "popup"], + } +} - pub fn get_property(&self, property_name: &str) -> Option<&dyn AriaPropertyDefinition> { - self.0.get(property_name).map(|prop| prop.as_ref()) +define_property! { + AriaErrormessage { + PROPERTY_TYPE: "id", + VALUES: [], + } +} + +define_property! { + AriaExpanded { + PROPERTY_TYPE: "boolean", + VALUES: [], + } +} + +define_property! { + AriaFlowto { + PROPERTY_TYPE: "idlist", + VALUES: [], + } +} + +define_property! { + AriaGrabbed { + PROPERTY_TYPE: "boolean", + VALUES: [], + } +} + +define_property! { + AriaHaspopup { + PROPERTY_TYPE: "token", + VALUES: ["false", "true", "menu", "listbox", "tree", "grid", "dialog"], + } +} + +define_property! { + AriaHidden { + PROPERTY_TYPE: "boolean", + VALUES: [], + } +} + +define_property! { + AriaInvalid { + PROPERTY_TYPE: "token", + VALUES: ["grammar", "false", "spelling", "true"], + } +} + +define_property! { + AriaKeyshortcuts { + PROPERTY_TYPE: "string", + VALUES: [], + } +} + +define_property! { + AriaLabel { + PROPERTY_TYPE: "string", + VALUES: [], + } +} + +define_property! { + AriaLabelledby { + PROPERTY_TYPE: "idlist", + VALUES: [], + } +} + +define_property! { + AriaLevel { + PROPERTY_TYPE: "integer", + VALUES: [], + } +} + +define_property! { + AriaLive { + PROPERTY_TYPE: "token", + VALUES: ["assertive", "off", "polite"], + } +} + +define_property! { + AriaModal { + PROPERTY_TYPE: "boolean", + VALUES: [], + } +} +define_property! { + AriaMultiline { + PROPERTY_TYPE: "boolean", + VALUES: [], + } +} +define_property! { + AriaMultiselectable { + PROPERTY_TYPE: "boolean", + VALUES: [], + } +} + +define_property! { + AriaOrientation { + PROPERTY_TYPE: "token", + VALUES: ["vertical", "undefined", "horizontal"], + } +} + +define_property! { + AriaOwns { + PROPERTY_TYPE: "idlist", + VALUES: [], + } +} + +define_property! { + AriaPlaceholder { + PROPERTY_TYPE: "string", + VALUES: [], + } +} + +define_property! { + AriaPosinset { + PROPERTY_TYPE: "integer", + VALUES: [], + } +} + +define_property! { + AriaPressed { + PROPERTY_TYPE: "tristate", + VALUES: [], + } +} + +define_property! { + AriaReadonly { + PROPERTY_TYPE: "boolean", + VALUES: [], + } +} + +define_property! { + AriaRelevant { + PROPERTY_TYPE: "tokenlist", + VALUES: ["additions", "all", "removals", "text"], + } +} + +define_property! { + AriaRequired { + PROPERTY_TYPE: "boolean", + VALUES: [], + } +} + +define_property! { + AriaRoledescription { + PROPERTY_TYPE: "string", + VALUES: [], + } +} + +define_property! { + AriaRowcount { + PROPERTY_TYPE: "integer", + VALUES: [], + } +} + +define_property! { + AriaRowindex { + PROPERTY_TYPE: "integer", + VALUES: [], + } +} + +define_property! { + AriaRowspan { + PROPERTY_TYPE: "integer", + VALUES: [], + } +} + +define_property! { + AriaSelected { + PROPERTY_TYPE: "boolean", + VALUES: [], + } +} + +define_property! { + AriaSetsize { + PROPERTY_TYPE: "integer", + VALUES: [], + } +} + +define_property! { + AriaSort { + PROPERTY_TYPE: "token", + VALUES: ["ascending", "descending", "none", "other"], + } +} + +define_property! { + AriaValuemax { + PROPERTY_TYPE: "number", + VALUES: [], + } +} + +define_property! { + AriaValuemin { + PROPERTY_TYPE: "number", + VALUES: [], + } +} + +define_property! { + AriaValuenow { + PROPERTY_TYPE: "number", + VALUES: [], + } +} + +define_property! { + AriaValuetext { + PROPERTY_TYPE: "string", + VALUES: [], + } +} +/// A collection of ARIA properties with their metadata, necessary to perform various operations. +#[derive(Debug, Default)] +pub struct AriaProperties; + +impl AriaProperties { + pub fn get_property<'a>(&self, property_name: &str) -> Option<&'a dyn AriaPropertyDefinition> { + Some(match property_name { + "aria-activedescendant" => &AriaActivedescendant as &dyn AriaPropertyDefinition, + "aria-autocomplete" => &AriaAutocomplete as &dyn AriaPropertyDefinition, + "aria-busy" => &AriaBusy as &dyn AriaPropertyDefinition, + "aria-checked" => &AriaChecked as &dyn AriaPropertyDefinition, + "aria-colcount" => &AriaColcount as &dyn AriaPropertyDefinition, + "aria-colindex" => &AriaColindex as &dyn AriaPropertyDefinition, + "aria-colspan" => &AriaColspan as &dyn AriaPropertyDefinition, + "aria-controls" => &AriaControls as &dyn AriaPropertyDefinition, + "aria-current" => &AriaCurrent as &dyn AriaPropertyDefinition, + "aria-describedby" => &AriaDescribedby as &dyn AriaPropertyDefinition, + "aria-details" => &AriaDetails as &dyn AriaPropertyDefinition, + "aria-disabled" => &AriaDisabled as &dyn AriaPropertyDefinition, + "aria-dropeffect" => &AriaDropeffect as &dyn AriaPropertyDefinition, + "aria-errormessage" => &AriaErrormessage as &dyn AriaPropertyDefinition, + "aria-expanded" => &AriaExpanded as &dyn AriaPropertyDefinition, + "aria-flowto" => &AriaFlowto as &dyn AriaPropertyDefinition, + "aria-grabbed" => &AriaGrabbed as &dyn AriaPropertyDefinition, + "aria-haspopup" => &AriaHaspopup as &dyn AriaPropertyDefinition, + "aria-hidden" => &AriaHidden as &dyn AriaPropertyDefinition, + "aria-invalid" => &AriaInvalid as &dyn AriaPropertyDefinition, + "aria-keyshortcuts" => &AriaKeyshortcuts as &dyn AriaPropertyDefinition, + "aria-label" => &AriaLabel as &dyn AriaPropertyDefinition, + "aria-labelledby" => &AriaLabelledby as &dyn AriaPropertyDefinition, + "aria-level" => &AriaLevel as &dyn AriaPropertyDefinition, + "aria-live" => &AriaLive as &dyn AriaPropertyDefinition, + "aria-modal" => &AriaModal as &dyn AriaPropertyDefinition, + "aria-multiline" => &AriaMultiline as &dyn AriaPropertyDefinition, + "aria-multiselectable" => &AriaMultiselectable as &dyn AriaPropertyDefinition, + "aria-orientation" => &AriaOrientation as &dyn AriaPropertyDefinition, + "aria-owns" => &AriaOwns as &dyn AriaPropertyDefinition, + "aria-placeholder" => &AriaPlaceholder as &dyn AriaPropertyDefinition, + "aria-posinset" => &AriaPosinset as &dyn AriaPropertyDefinition, + "aria-pressed" => &AriaPressed as &dyn AriaPropertyDefinition, + "aria-readonly" => &AriaReadonly as &dyn AriaPropertyDefinition, + "aria-relevant" => &AriaRelevant as &dyn AriaPropertyDefinition, + "aria-required" => &AriaRequired as &dyn AriaPropertyDefinition, + "aria-roledescription" => &AriaRoledescription as &dyn AriaPropertyDefinition, + "aria-rowcount" => &AriaRowcount as &dyn AriaPropertyDefinition, + "aria-rowindex" => &AriaRowindex as &dyn AriaPropertyDefinition, + "aria-rowspan" => &AriaRowspan as &dyn AriaPropertyDefinition, + "aria-selected" => &AriaSelected as &dyn AriaPropertyDefinition, + "aria-setsize" => &AriaSetsize as &dyn AriaPropertyDefinition, + "aria-sort" => &AriaSort as &dyn AriaPropertyDefinition, + "aria-valuemax" => &AriaValuemax as &dyn AriaPropertyDefinition, + "aria-valuemin" => &AriaValuemin as &dyn AriaPropertyDefinition, + "aria-valuenow" => &AriaValuenow as &dyn AriaPropertyDefinition, + "aria-valuetext" => &AriaValuetext as &dyn AriaPropertyDefinition, + _ => return None, + }) } } pub trait AriaPropertyDefinition: Debug { /// Returns the allowed values by this property - fn values<'a>(&self) -> Iter<'a, &str>; + fn values(&self) -> Iter<&str>; /// Returns the property type fn property_type(&self) -> AriaPropertyTypeEnum; @@ -56,6 +422,9 @@ pub trait AriaPropertyDefinition: Debug { /// assert!(!aria_current.contains_correct_value("something_not_allowed")); /// ``` fn contains_correct_value(&self, input_value: &str) -> bool { + if input_value.is_empty() { + return false; + } match self.property_type() { AriaPropertyTypeEnum::String | AriaPropertyTypeEnum::Id => { input_value.parse::().is_err() @@ -74,7 +443,7 @@ pub trait AriaPropertyDefinition: Debug { .any(|allowed_token| *allowed_token == input_value), AriaPropertyTypeEnum::Tokenlist => input_value.split(' ').all(|input_token| { self.values() - .any(|allowed_token| *allowed_token == input_token) + .any(|allowed_token| allowed_token.trim() == input_token) }), AriaPropertyTypeEnum::Tristate => { matches!(input_value, "false" | "true" | "mixed") @@ -82,22 +451,3 @@ pub trait AriaPropertyDefinition: Debug { } } } - -#[derive(Debug)] -struct AriaCurrent; - -impl AriaCurrent { - const PROPERTY_TYPE: &'static str = "token"; - const VALUES: [&'static str; 7] = ["page", "step", "location", "date", "time", "true", "false"]; -} - -impl AriaPropertyDefinition for AriaCurrent { - fn values<'a>(&self) -> Iter<'a, &str> { - AriaCurrent::VALUES.iter() - } - - fn property_type(&self) -> AriaPropertyTypeEnum { - // SAFETY: PROPERTY_TYPE is internal and should not contain extraneous properties - AriaPropertyTypeEnum::from_str(AriaCurrent::PROPERTY_TYPE).unwrap() - } -} diff --git a/crates/rome_aria/src/roles.rs b/crates/rome_aria/src/roles.rs index 3063e36b421..60bca860811 100644 --- a/crates/rome_aria/src/roles.rs +++ b/crates/rome_aria/src/roles.rs @@ -1,42 +1,9 @@ -use crate::generated::{ - AriaAbstractRolesEnum, AriaDocumentStructureRolesEnum, AriaPropertiesEnum, AriaWidgetRolesEnum, -}; use crate::{define_role, is_aria_property_valid}; +use rome_aria_metadata::AriaPropertiesEnum; use std::fmt::Debug; use std::slice::Iter; use std::str::FromStr; -#[derive(Debug)] -pub enum AriaRole { - Widget(AriaWidgetRolesEnum), - Document(AriaDocumentStructureRolesEnum), - Abstract(AriaAbstractRolesEnum), -} - -impl From for &str { - fn from(s: AriaRole) -> Self { - match s { - AriaRole::Widget(widget) => widget.into(), - AriaRole::Document(document) => document.into(), - AriaRole::Abstract(abs) => abs.into(), - } - } -} - -impl FromStr for AriaRole { - type Err = String; - - fn from_str(s: &str) -> Result { - AriaWidgetRolesEnum::from_str(s) - .map(Self::Widget) - .or_else(|_| { - AriaAbstractRolesEnum::from_str(s) - .map(Self::Abstract) - .or_else(|_| AriaDocumentStructureRolesEnum::from_str(s).map(Self::Document)) - }) - } -} - pub trait AriaRoleDefinition: Debug { /// It returns an iterator over the properties of the current role /// @@ -51,10 +18,10 @@ pub trait AriaRoleDefinition: Debug { /// let properties = checkbox_role.properties(); /// assert_eq!(properties.len(), 2); /// ``` - fn properties<'a>(&self) -> Iter<'a, (&str, bool)>; + fn properties(&self) -> Iter<(&str, bool)>; /// It returns an iterator over the possible roles of this definition - fn roles<'a>(&self) -> Iter<'a, &str>; + fn roles(&self) -> Iter<&str>; /// Given a [aria property](ARIA_PROPERTIES) as input, it checks if it's required /// for the current role. @@ -78,7 +45,7 @@ pub trait AriaRoleDefinition: Debug { if is_aria_property_valid(property_to_check) { let property_to_check = AriaPropertiesEnum::from_str(property_to_check); if let Ok(property_to_check) = property_to_check { - for (property, required) in self.properties().as_ref() { + for (property, required) in self.properties() { let property = AriaPropertiesEnum::from_str(property).unwrap(); if property == property_to_check { return *required; diff --git a/crates/rome_aria_metadata/Cargo.toml b/crates/rome_aria_metadata/Cargo.toml new file mode 100644 index 00000000000..a02817b4f35 --- /dev/null +++ b/crates/rome_aria_metadata/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "rome_aria_metadata" +version = "0.0.0" +edition = "2021" +authors = ["Rome Tools Developers and Contributors"] +repository = "https://github.com/rome/tools" +license = "MIT" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + + +[build-dependencies] +quote = { workspace = true } +proc-macro2 = { version = "1.0.36", features = ["span-locations"] } +case = "1.0.0" diff --git a/xtask/codegen/src/generate_aria.rs b/crates/rome_aria_metadata/build.rs similarity index 53% rename from xtask/codegen/src/generate_aria.rs rename to crates/rome_aria_metadata/build.rs index 45a3355099f..68eed084288 100644 --- a/xtask/codegen/src/generate_aria.rs +++ b/crates/rome_aria_metadata/build.rs @@ -1,20 +1,154 @@ +//! Metadata of: +//! - ARIA properties +//! - ARIA property types +//! - ARIA roles + use case::CaseExt; use proc_macro2::{Ident, Literal, Span, TokenStream}; use quote::quote; -use rome_aria::constants::{ - ARIA_ABSTRACT_ROLES, ARIA_DOCUMENT_STRUCTURE_ROLES, ARIA_PROPERTIES, ARIA_PROPERTY_TYPE, - ARIA_WIDGET_ROLES, -}; -use xtask::*; -use xtask::{project_root, Mode}; -use xtask_codegen::update; - -pub(crate) fn generate_aria(mode: Mode) -> Result<()> { - let config_root = project_root().join("crates/rome_aria/src"); +use std::path::PathBuf; +use std::{env, fs, io}; + +pub const ARIA_PROPERTIES: [&str; 48] = [ + "aria-activedescendant", + "aria-atomic", + "aria-autocomplete", + "aria-busy", + "aria-checked", + "aria-colcount", + "aria-colindex", + "aria-colspan", + "aria-controls", + "aria-current", + "aria-describedby", + "aria-details", + "aria-disabled", + "aria-dropeffect", + "aria-errormessage", + "aria-expanded", + "aria-flowto", + "aria-grabbed", + "aria-haspopup", + "aria-hidden", + "aria-invalid", + "aria-keyshortcuts", + "aria-label", + "aria-labelledby", + "aria-level", + "aria-live", + "aria-modal", + "aria-multiline", + "aria-multiselectable", + "aria-orientation", + "aria-owns", + "aria-placeholder", + "aria-posinset", + "aria-pressed", + "aria-readonly", + "aria-relevant", + "aria-required", + "aria-roledescription", + "aria-rowcount", + "aria-rowindex", + "aria-rowspan", + "aria-selected", + "aria-setsize", + "aria-sort", + "aria-valuemax", + "aria-valuemin", + "aria-valuenow", + "aria-valuetext", +]; + +pub const ARIA_PROPERTY_TYPE: [&str; 9] = [ + "boolean", + "id", + "idlist", + "integer", + "number", + "string", + "token", + "tokenlist", + "tristate", +]; + +pub const ARIA_WIDGET_ROLES: [&str; 27] = [ + "alert", + "alertdialog", + "button", + "checkbox", + "dialog", + "gridcell", + "link", + "log", + "marquee", + "menuitem", + "menuitemcheckbox", + "menuitemradio", + "option", + "progressbar", + "radio", + "scrollbar", + "searchbox", + "slider", + "spinbutton", + "status", + "switch", + "tab", + "tabpanel", + "textbox", + "timer", + "tooltip", + "treeitem", +]; + +pub const ARIA_ABSTRACT_ROLES: [&str; 12] = [ + "command", + "composite", + "input", + "landmark", + "range", + "roletype", + "section", + "sectionhead", + "select", + "structure", + "widget", + "window", +]; + +pub const ARIA_DOCUMENT_STRUCTURE_ROLES: [&str; 25] = [ + "article", + "cell", + "columnheader", + "definition", + "directory", + "document", + "feed", + "figure", + "group", + "heading", + "img", + "list", + "listitem", + "math", + "none", + "note", + "presentation", + "region", + "row", + "rowgroup", + "rowheader", + "separator", + "table", + "term", + "toolbar", +]; + +fn main() -> io::Result<()> { let aria_properties = generate_properties(); let aria_roles = generate_roles(); let tokens = quote! { - #![allow(clippy::enum_variant_names)] use std::str::FromStr; @@ -22,9 +156,9 @@ pub(crate) fn generate_aria(mode: Mode) -> Result<()> { #aria_roles }; let ast = tokens.to_string(); - let pretty = xtask::reformat(ast)?; - update(&config_root.join("generated.rs"), &pretty, &mode)?; + let out_dir = env::var("OUT_DIR").unwrap(); + fs::write(PathBuf::from(out_dir).join("enums.rs"), ast)?; Ok(()) } diff --git a/crates/rome_aria_metadata/src/lib.rs b/crates/rome_aria_metadata/src/lib.rs new file mode 100644 index 00000000000..d0499b9e433 --- /dev/null +++ b/crates/rome_aria_metadata/src/lib.rs @@ -0,0 +1 @@ +include!(concat!(env!("OUT_DIR"), "/enums.rs")); diff --git a/crates/rome_js_analyze/src/aria_analyzers/nursery/use_aria_prop_types.rs b/crates/rome_js_analyze/src/aria_analyzers/nursery/use_aria_prop_types.rs index 1aa9ad3f0b6..0acc342889f 100644 --- a/crates/rome_js_analyze/src/aria_analyzers/nursery/use_aria_prop_types.rs +++ b/crates/rome_js_analyze/src/aria_analyzers/nursery/use_aria_prop_types.rs @@ -1,15 +1,56 @@ use crate::aria_services::Aria; use rome_analyze::context::RuleContext; use rome_analyze::{declare_rule, Rule, RuleDiagnostic}; +use rome_aria::AriaPropertyTypeEnum; use rome_console::markup; -use rome_js_syntax::{JsSyntaxToken, JsxAttribute, TextRange}; -use rome_rowan::AstNode; +use rome_js_syntax::{ + AnyJsExpression, AnyJsLiteralExpression, AnyJsxAttributeValue, JsSyntaxToken, JsxAttribute, + TextRange, +}; +use rome_rowan::{AstNode, AstNodeList}; +use std::slice::Iter; declare_rule! { /// Enforce that ARIA state and property values are valid. /// + /// + /// ## Examples + /// + /// ### Invalid + /// + /// ```jsx, expect_diagnostic + /// some text + /// ``` + /// + /// ```jsx, expect_diagnostic + /// some text + /// ``` + /// + /// ```jsx, expect_diagnostic + /// some text + /// ``` + /// + /// ```jsx, expect_diagnostic + /// some text + /// ``` + /// + /// ### Valid + /// + /// ```jsx + /// <> + /// some text + /// some text + /// + /// ``` + /// + /// ## Accessibility guidelines + /// - [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value) + /// + /// ### Resources + /// - [ARIA Spec, States and Properties](https://www.w3.org/TR/wai-aria/#states_and_properties) + /// - [Chrome Audit Rules, AX_ARIA_04](https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#ax_aria_04) pub(crate) UseAriaPropTypes { - version: "11.0.0", + version: "12.0.0", name: "useAriaPropTypes", recommended: false, } @@ -17,8 +58,9 @@ declare_rule! { pub(crate) struct UseAriaProptypesState { attribute_value_range: TextRange, - allowed_values: Vec, + allowed_values: Iter<'static, &'static str>, attribute_name: JsSyntaxToken, + property_type: AriaPropertyTypeEnum, } impl Rule for UseAriaPropTypes { @@ -35,17 +77,46 @@ impl Rule for UseAriaPropTypes { if let Some(aria_property) = aria_properties.get_property(attribute_name.text_trimmed()) { let attribute_value = node.initializer()?.value().ok()?; - let attribute_value = attribute_value.as_jsx_string()?; + let attribute_value_range = node.range(); + let attribute_text = match attribute_value { + AnyJsxAttributeValue::JsxString(string) => Some(string.inner_string_text().ok()?), + AnyJsxAttributeValue::JsxExpressionAttributeValue(expression) => { + match expression.expression().ok()? { + AnyJsExpression::JsTemplateExpression(template) => { + if template.elements().is_empty() { + // Early error, the template literal is empty + return Some(UseAriaProptypesState { + attribute_value_range, + allowed_values: aria_property.values(), + attribute_name, + property_type: aria_property.property_type(), + }); + } + template.elements().iter().next().and_then(|chunk| { + Some( + chunk + .as_js_template_chunk_element()? + .template_chunk_token() + .ok()? + .token_text_trimmed(), + ) + }) + } + AnyJsExpression::AnyJsLiteralExpression( + AnyJsLiteralExpression::JsStringLiteralExpression(string), + ) => Some(string.inner_string_text().ok()?), + _ => None, + } + } + _ => return None, + }?; - let attribute_text = attribute_value.inner_string_text().ok()?; if !aria_property.contains_correct_value(attribute_text.text()) { return Some(UseAriaProptypesState { - attribute_value_range: attribute_value.range(), - allowed_values: aria_property - .values() - .map(|value| value.to_string()) - .collect::>(), + attribute_value_range, + allowed_values: aria_property.values(), attribute_name, + property_type: aria_property.property_type(), }); } } @@ -55,19 +126,73 @@ impl Rule for UseAriaPropTypes { fn diagnostic(_ctx: &RuleContext, state: &Self::State) -> Option { let attribute_name = state.attribute_name.text_trimmed(); - Some( - RuleDiagnostic::new( - rule_category!(), - state.attribute_value_range, - markup! { + let diagnostic = RuleDiagnostic::new( + rule_category!(), + state.attribute_value_range, + markup! { "The value of the ARIA attribute "{attribute_name}" is not correct." }, - ).footer_list( - markup!{ - "The supported values for the "{attribute_name}" attribute are:" + ); + + let diagnostic = match state.property_type { + AriaPropertyTypeEnum::Boolean => { + diagnostic.footer_list( + markup!{ + "The only supported values for the "{attribute_name}" property is one of the following:" + }, + &["true", "false"] + ) + } + AriaPropertyTypeEnum::Integer => { + diagnostic.note( + markup!{ + "The only value supported is a number without fractional components." + } + ) + } + AriaPropertyTypeEnum::Id | + AriaPropertyTypeEnum::Idlist | + AriaPropertyTypeEnum::String => { + diagnostic.note( + markup!{ + "The only supported value is text." + } + ) + } + + AriaPropertyTypeEnum::Number => { + diagnostic.note( + markup!{ + "The only supported value is number." + } + ) + } + AriaPropertyTypeEnum::Token => { + diagnostic.footer_list( + markup!{ + "The only supported value for the "{attribute_name}" property is one of the following:" }, - &state.allowed_values - ) - ) + state.allowed_values.as_slice() + ) + } + AriaPropertyTypeEnum::Tokenlist => { + diagnostic.footer_list( + markup!{ + "The values supported for "{attribute_name}" property are one or more of the following:" + }, + state.allowed_values.as_slice() + ) + } + AriaPropertyTypeEnum::Tristate => { + diagnostic.footer_list( + markup!{ + "The only supported value for the "{attribute_name}" property one of the following:" + }, + &["true", "false", "mixed"] + ) + } + }; + + Some(diagnostic) } } diff --git a/crates/rome_js_analyze/src/lib.rs b/crates/rome_js_analyze/src/lib.rs index 24572b915ca..5129656a82e 100644 --- a/crates/rome_js_analyze/src/lib.rs +++ b/crates/rome_js_analyze/src/lib.rs @@ -226,17 +226,13 @@ mod tests { String::from_utf8(buffer).unwrap() } - const SOURCE: &str = r#"something.forEach((Element, index) => { - return
foo
-
; -})"#; + const SOURCE: &str = r#""#; let parsed = parse(SOURCE, FileId::zero(), SourceType::jsx()); let mut error_ranges: Vec = Vec::new(); let options = AnalyzerOptions::default(); - let rule_filter = RuleFilter::Rule("suspicious", "noArrayIndexKey"); + let rule_filter = RuleFilter::Rule("nursery", "useAriaPropTypes"); analyze( FileId::zero(), &parsed.tree(), diff --git a/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes.jsx b/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes.jsx deleted file mode 100644 index 12143798f89..00000000000 --- a/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes.jsx +++ /dev/null @@ -1 +0,0 @@ -let a = \ No newline at end of file diff --git a/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes.jsx.snap b/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes.jsx.snap deleted file mode 100644 index c992a090322..00000000000 --- a/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes.jsx.snap +++ /dev/null @@ -1,32 +0,0 @@ ---- -source: crates/rome_js_analyze/tests/spec_tests.rs -expression: useAriaPropTypes.jsx ---- -# Input -```js -let a = -``` - -# Diagnostics -``` -useAriaPropTypes.jsx:1:28 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! The value of the ARIA attribute aria-current is not correct. - - > 1 │ let a = - │ ^^^^^^^^^^^^^^^ - - i The supported values for the aria-current attribute are: - - - page - - step - - location - - date - - time - - true - - false - - -``` - - diff --git a/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes/invalid.jsx b/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes/invalid.jsx new file mode 100644 index 00000000000..9b8143c60ad --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes/invalid.jsx @@ -0,0 +1,15 @@ +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; diff --git a/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes/invalid.jsx.snap b/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes/invalid.jsx.snap new file mode 100644 index 00000000000..d8d17b8adcd --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes/invalid.jsx.snap @@ -0,0 +1,295 @@ +--- +source: crates/rome_js_analyze/tests/spec_tests.rs +expression: invalid.jsx +--- +# Input +```js +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; + +``` + +# Diagnostics +``` +invalid.jsx:1:31 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The value of the ARIA attribute aria-checked is not correct. + + > 1 │ var a = ; + │ ^^^^^^^^^^^^^^^^^^^ + 2 │ var a = ; + 3 │ var a = ; + + i The only supported value for the aria-checked property one of the following: + + - true + - false + - mixed + + +``` + +``` +invalid.jsx:2:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The value of the ARIA attribute aria-autocomplete is not correct. + + 1 │ var a = ; + > 2 │ var a = ; + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + 3 │ var a = ; + 4 │ var a = ; + + i The only supported value for the aria-autocomplete property is one of the following: + + - inline + - list + - both + - none + + +``` + +``` +invalid.jsx:3:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The value of the ARIA attribute aria-invalid is not correct. + + 1 │ var a = ; + 2 │ var a = ; + > 3 │ var a = ; + │ ^^^^^^^^^^^^^^^^^^ + 4 │ var a = ; + 5 │ var a = ; + + i The only supported value for the aria-invalid property is one of the following: + + - grammar + - false + - spelling + - true + + +``` + +``` +invalid.jsx:5:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The value of the ARIA attribute aria-invalid is not correct. + + 3 │ var a = ; + 4 │ var a = ; + > 5 │ var a = ; + │ ^^^^^^^^^^^^^^^^^^^^ + 6 │ var a = ; + 7 │ var a = ; + + i The only supported value for the aria-invalid property is one of the following: + + - grammar + - false + - spelling + - true + + +``` + +``` +invalid.jsx:6:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The value of the ARIA attribute aria-errormessage is not correct. + + 4 │ var a = ; + 5 │ var a = ; + > 6 │ var a = ; + │ ^^^^^^^^^^^^^^^^^^^^ + 7 │ var a = ; + 8 │ var a = ; + + i The only supported value is text. + + +``` + +``` +invalid.jsx:7:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The value of the ARIA attribute aria-relevant is not correct. + + 5 │ var a = ; + 6 │ var a = ; + > 7 │ var a = ; + │ ^^^^^^^^^^^^^^^^^^^^^ + 8 │ var a = ; + 9 │ var a = ; + + i The values supported for aria-relevant property are one or more of the following: + + - additions + - all + - removals + - text + + +``` + +``` +invalid.jsx:8:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The value of the ARIA attribute aria-labelledby is not correct. + + 6 │ var a = ; + 7 │ var a = ; + > 8 │ var a = ; + │ ^^^^^^^^^^^^^^^^^^ + 9 │ var a = ; + 10 │ var a = ; + + i The only supported value is text. + + +``` + +``` +invalid.jsx:9:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The value of the ARIA attribute aria-labelledby is not correct. + + 7 │ var a = ; + 8 │ var a = ; + > 9 │ var a = ; + │ ^^^^^^^^^^^^^^^^^^^^ + 10 │ var a = ; + 11 │ var a = ; + + i The only supported value is text. + + +``` + +``` +invalid.jsx:10:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The value of the ARIA attribute aria-labelledby is not correct. + + 8 │ var a = ; + 9 │ var a = ; + > 10 │ var a = ; + │ ^^^^^^^^^^^^^^^^^^^^ + 11 │ var a = ; + 12 │ var a = ; + + i The only supported value is text. + + +``` + +``` +invalid.jsx:11:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The value of the ARIA attribute aria-details is not correct. + + 9 │ var a = ; + 10 │ var a = ; + > 11 │ var a = ; + │ ^^^^^^^^^^^^^^^ + 12 │ var a = ; + 13 │ var a = ; + + i The only supported value is text. + + +``` + +``` +invalid.jsx:12:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The value of the ARIA attribute aria-setsize is not correct. + + 10 │ var a = ; + 11 │ var a = ; + > 12 │ var a = ; + │ ^^^^^^^^^^^^^^^^^^ + 13 │ var a = ; + 14 │ var a = ; + + i The only value supported is a number without fractional components. + + +``` + +``` +invalid.jsx:13:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The value of the ARIA attribute aria-valuemax is not correct. + + 11 │ var a = ; + 12 │ var a = ; + > 13 │ var a = ; + │ ^^^^^^^^^^^^^^^^^^^ + 14 │ var a = ; + 15 │ var a = ; + + i The only supported value is number. + + +``` + +``` +invalid.jsx:14:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The value of the ARIA attribute aria-dropeffect is not correct. + + 12 │ var a = ; + 13 │ var a = ; + > 14 │ var a = ; + │ ^^^^^^^^^^^^^^^^^^^^^ + 15 │ var a = ; + 16 │ + + i The values supported for aria-dropeffect property are one or more of the following: + + - copy + - execute + - link + - move + - none + - popup + + +``` + +``` +invalid.jsx:15:15 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The value of the ARIA attribute aria-orientation is not correct. + + 13 │ var a = ; + 14 │ var a = ; + > 15 │ var a = ; + │ ^^^^^^^^^^^^^^^^^^^^^^ + 16 │ + + i The only supported value for the aria-orientation property is one of the following: + + - vertical + - undefined + - horizontal + + +``` + + diff --git a/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes/valid.jsx b/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes/valid.jsx new file mode 100644 index 00000000000..13cbd9cf5fc --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes/valid.jsx @@ -0,0 +1,19 @@ +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; diff --git a/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes/valid.jsx.snap b/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes/valid.jsx.snap new file mode 100644 index 00000000000..2db689e205e --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/nursery/useAriaPropTypes/valid.jsx.snap @@ -0,0 +1,29 @@ +--- +source: crates/rome_js_analyze/tests/spec_tests.rs +expression: valid.jsx +--- +# Input +```js +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; +var a = ; + +``` + + diff --git a/website/src/pages/lint/rules/useAriaPropTypes.md b/website/src/pages/lint/rules/useAriaPropTypes.md index 4467f5d6a79..b0ab906ec74 100644 --- a/website/src/pages/lint/rules/useAriaPropTypes.md +++ b/website/src/pages/lint/rules/useAriaPropTypes.md @@ -3,7 +3,101 @@ title: Lint Rule useAriaPropTypes parent: lint/rules/index --- -# useAriaPropTypes (since v11.0.0) +# useAriaPropTypes (since v12.0.0) Enforce that ARIA state and property values are valid. +## Examples + +### Invalid + +```jsx +some text +``` + +
nursery/useAriaPropTypes.js:1:23 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+   The value of the ARIA attribute aria-checked is not correct.
+  
+  > 1 │ <span role="checkbox" aria-checked="test">some text</span>
+                         ^^^^^^^^^^^^^^^^^^^
+    2 │ 
+  
+   The only supported value for the aria-checked property one of the following:
+  
+  - true
+  - false
+  - mixed
+  
+
+ +```jsx +some text +``` + +
nursery/useAriaPropTypes.js:1:7 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+   The value of the ARIA attribute aria-labelledby is not correct.
+  
+  > 1 │ <span aria-labelledby="">some text</span>
+         ^^^^^^^^^^^^^^^^^^
+    2 │ 
+  
+   The only supported value is text.
+  
+
+ +```jsx +some text +``` + +
nursery/useAriaPropTypes.js:1:7 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+   The value of the ARIA attribute aria-valuemax is not correct.
+  
+  > 1 │ <span aria-valuemax="hey">some text</span>
+         ^^^^^^^^^^^^^^^^^^^
+    2 │ 
+  
+   The only supported value is number.
+  
+
+ +```jsx +some text +``` + +
nursery/useAriaPropTypes.js:1:7 lint/nursery/useAriaPropTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+   The value of the ARIA attribute aria-orientation is not correct.
+  
+  > 1 │ <span aria-orientation="hey">some text</span>
+         ^^^^^^^^^^^^^^^^^^^^^^
+    2 │ 
+  
+   The only supported value for the aria-orientation property is one of the following:
+  
+  - vertical
+  - undefined
+  - horizontal
+  
+
+ +### Valid + +```jsx +<> + some text + some text + +``` + +## Accessibility guidelines + +- [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value) + +### Resources + +- [ARIA Spec, States and Properties](https://www.w3.org/TR/wai-aria/#states_and_properties) +- [Chrome Audit Rules, AX_ARIA_04](https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#ax_aria_04) + diff --git a/xtask/codegen/Cargo.toml b/xtask/codegen/Cargo.toml index 3fe65ed1928..9aa571308ba 100644 --- a/xtask/codegen/Cargo.toml +++ b/xtask/codegen/Cargo.toml @@ -32,5 +32,4 @@ rome_service = { path = "../../crates/rome_service", features = ["schemars"], op [features] configuration = ["rome_analyze", "rome_js_analyze", "rome_js_syntax", "pulldown-cmark"] -aria = ["rome_aria"] schema = ["schemars", "serde_json", "rome_rowan", "rome_service", "rome_js_syntax", "rome_js_factory", "rome_js_formatter"] diff --git a/xtask/codegen/src/main.rs b/xtask/codegen/src/main.rs index c146c21f73f..a7728027e2e 100644 --- a/xtask/codegen/src/main.rs +++ b/xtask/codegen/src/main.rs @@ -1,5 +1,3 @@ -#[cfg(feature = "aria")] -mod generate_aria; #[cfg(feature = "schema")] mod generate_bindings; #[cfg(feature = "configuration")] @@ -71,11 +69,6 @@ fn main() -> Result<()> { generate_workspace_bindings(Mode::Overwrite)?; Ok(()) } - #[cfg(feature = "aria")] - "aria" => { - generate_aria(Mode::Overwrite)?; - Ok(()) - } "all" => { generate_tables()?; generate_grammar(args);