Skip to content

Commit

Permalink
fix(eslint-plugin-jsx-a11y(anchor-is-valid)): make the rule work corr…
Browse files Browse the repository at this point in the history
…ectly and improve error grammar (#5781)

fixes #5746.
  • Loading branch information
Arian94 authored Sep 16, 2024
1 parent 213dbc0 commit d1d54a6
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 25 deletions.
45 changes: 38 additions & 7 deletions crates/oxc_linter/src/rules/jsx_a11y/anchor_is_valid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@ use crate::{

fn missing_href_attribute(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Missing `href` attribute for the `a` element.")
.with_help("Provide an href for the `a` element.")
.with_help("Provide an `href` for the `a` element.")
.with_label(span)
}

fn incorrect_href(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Use an incorrect href for the 'a' element.")
.with_help("Provide a correct href for the `a` element.")
OxcDiagnostic::warn("Use of incorrect `href` for the 'a' element.")
.with_help("Provide a correct `href` for the `a` element.")
.with_label(span)
}

fn cant_be_anchor(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn(" The a element has `href` and `onClick`.")
OxcDiagnostic::warn("The `a` element has `href` and `onClick`.")
.with_help("Use a `button` element instead of an `a` element.")
.with_label(span)
}
Expand Down Expand Up @@ -148,7 +148,7 @@ impl Rule for AnchorIsValid {
return;
};

let is_empty = self.check_value_is_empty(value);
let is_empty = self.check_value_is_empty_or_invalid(value);
if is_empty {
if has_jsx_prop_ignore_case(&jsx_el.opening_element, "onclick").is_some() {
ctx.diagnostic(cant_be_anchor(get_span()));
Expand All @@ -173,14 +173,24 @@ impl Rule for AnchorIsValid {
}

impl AnchorIsValid {
fn check_value_is_empty(&self, value: &JSXAttributeValue) -> bool {
fn check_value_is_empty_or_invalid(&self, value: &JSXAttributeValue) -> bool {
match value {
JSXAttributeValue::Element(_) => false,
JSXAttributeValue::StringLiteral(str_lit) => self.is_invalid_href(&str_lit.value),
JSXAttributeValue::ExpressionContainer(exp) => match &exp.expression {
JSXExpression::Identifier(ident) => ident.name == "undefined",
JSXExpression::NullLiteral(_) => true,
JSXExpression::StringLiteral(str_lit) => self.is_invalid_href(&str_lit.value),
JSXExpression::TemplateLiteral(temp_lit) => {
if !temp_lit.expressions.is_empty() {
return false;
}

let Some(quasi) = temp_lit.quasi() else {
return false;
};
self.is_invalid_href(&quasi)
}
_ => false,
},
JSXAttributeValue::Fragment(_) => true,
Expand All @@ -196,7 +206,11 @@ impl AnchorIsValidConfig {
}

fn is_invalid_href(&self, href: &str) -> bool {
href.is_empty() || href == "#" || href == "javascript:void(0)" || !self.contains(href)
if self.contains(href) {
return false;
}

href.is_empty() || href == "#" || href == "javascript:void(0)"
}

fn contains(&self, href: &str) -> bool {
Expand Down Expand Up @@ -361,6 +375,22 @@ fn test() {
None,
),
(r"<a href={this} onClick={() => void 0} />", None, None),
(r"<a href='/some/valid/uri'>valid</a>", None, None),
(r"<a href={'/some/valid/uri'}>valid</a>", None, None),
(r"<a href={`/some/valid/uri`}>valid</a>", None, None),
(r"<a href='#top'>Navigate to internal page location</a>", None, None),
(r"<a href={'#top'}>Navigate to internal page location</a>", None, None),
(r"<a href={`#top`}>Navigate to internal page location</a>", None, None),
(r"<a href='https://github.com'>github</a>", None, None),
(r"<a href={'https://github.com'}>github</a>", None, None),
(r"<a href={`https://github.com`}>github</a>", None, None),
(r"<a href='#section'>section</a>", None, None),
(r"<a href={'#section'}>section</a>", None, None),
(r"<a href={`#section`}>section</a>", None, None),
(r"<a href={`${foo}`}>valid</a>", None, None),
(r"<a href={`#${foo}`}>valid</a>", None, None),
(r"<a href={`#${foo}/bar`}>valid</a>", None, None),
(r"<a href={foo + bar}>valid</a>", None, None),
// (r#"<Anchor {...props} onClick={() => void 0} />"#, Some(serde_json::json!(components))),
// (r#"<Anchor href='foo' onClick={() => void 0} />"#, Some(serde_json::json!(components))),
// (r#"<Anchor href={foo} onClick={() => void 0} />"#, Some(serde_json::json!(components))),
Expand Down Expand Up @@ -533,6 +563,7 @@ fn test() {
(r"<a href=' />;", None, None),
(r"<a href='#' />", None, None),
(r"<a href={'#'} />", None, None),
(r"<a href={`#`} />", None, None),
(r"<a href='javascript:void(0)' />", None, None),
(r"<a href={'javascript:void(0)'} />", None, None),
(r"<a onClick={() => void 0} />", None, None),
Expand Down
43 changes: 25 additions & 18 deletions crates/oxc_linter/src/snapshots/anchor_is_valid.snap
Original file line number Diff line number Diff line change
Expand Up @@ -6,85 +6,92 @@ source: crates/oxc_linter/src/tester.rs
1<a />
· ─
╰────
help: Provide an href for the `a` element.
help: Provide an `href` for the `a` element.

eslint-plugin-jsx-a11y(anchor-is-valid): Use an incorrect href for the 'a' element.
eslint-plugin-jsx-a11y(anchor-is-valid): Use of incorrect `href` for the 'a' element.
╭─[anchor_is_valid.tsx:1:2]
1<a href={undefined} />
· ─
╰────
help: Provide a correct href for the `a` element.
help: Provide a correct `href` for the `a` element.

eslint-plugin-jsx-a11y(anchor-is-valid): Use an incorrect href for the 'a' element.
eslint-plugin-jsx-a11y(anchor-is-valid): Use of incorrect `href` for the 'a' element.
╭─[anchor_is_valid.tsx:1:2]
1<a href={null} />
· ─
╰────
help: Provide a correct href for the `a` element.
help: Provide a correct `href` for the `a` element.

× Unterminated string
╭─[anchor_is_valid.tsx:1:9]
1<a href=' />;
· ─────
╰────

eslint-plugin-jsx-a11y(anchor-is-valid): Use an incorrect href for the 'a' element.
eslint-plugin-jsx-a11y(anchor-is-valid): Use of incorrect `href` for the 'a' element.
╭─[anchor_is_valid.tsx:1:2]
1<a href='#' />
· ─
╰────
help: Provide a correct href for the `a` element.
help: Provide a correct `href` for the `a` element.

eslint-plugin-jsx-a11y(anchor-is-valid): Use an incorrect href for the 'a' element.
eslint-plugin-jsx-a11y(anchor-is-valid): Use of incorrect `href` for the 'a' element.
╭─[anchor_is_valid.tsx:1:2]
1<a href={'#'} />
· ─
╰────
help: Provide a correct href for the `a` element.
help: Provide a correct `href` for the `a` element.

eslint-plugin-jsx-a11y(anchor-is-valid): Use an incorrect href for the 'a' element.
eslint-plugin-jsx-a11y(anchor-is-valid): Use of incorrect `href` for the 'a' element.
╭─[anchor_is_valid.tsx:1:2]
1<a href={`#`} />
· ─
╰────
help: Provide a correct `href` for the `a` element.

eslint-plugin-jsx-a11y(anchor-is-valid): Use of incorrect `href` for the 'a' element.
╭─[anchor_is_valid.tsx:1:2]
1<a href='javascript:void(0)' />
· ─
╰────
help: Provide a correct href for the `a` element.
help: Provide a correct `href` for the `a` element.

eslint-plugin-jsx-a11y(anchor-is-valid): Use an incorrect href for the 'a' element.
eslint-plugin-jsx-a11y(anchor-is-valid): Use of incorrect `href` for the 'a' element.
╭─[anchor_is_valid.tsx:1:2]
1<a href={'javascript:void(0)'} />
· ─
╰────
help: Provide a correct href for the `a` element.
help: Provide a correct `href` for the `a` element.

eslint-plugin-jsx-a11y(anchor-is-valid): Missing `href` attribute for the `a` element.
╭─[anchor_is_valid.tsx:1:2]
1<a onClick={() => void 0} />
· ─
╰────
help: Provide an href for the `a` element.
help: Provide an `href` for the `a` element.

eslint-plugin-jsx-a11y(anchor-is-valid): The a element has `href` and `onClick`.
eslint-plugin-jsx-a11y(anchor-is-valid): The `a` element has `href` and `onClick`.
╭─[anchor_is_valid.tsx:1:2]
1<a href='#' onClick={() => void 0} />
· ─
╰────
help: Use a `button` element instead of an `a` element.

eslint-plugin-jsx-a11y(anchor-is-valid): The a element has `href` and `onClick`.
eslint-plugin-jsx-a11y(anchor-is-valid): The `a` element has `href` and `onClick`.
╭─[anchor_is_valid.tsx:1:2]
1<a href='javascript:void(0)' onClick={() => void 0} />
· ─
╰────
help: Use a `button` element instead of an `a` element.

eslint-plugin-jsx-a11y(anchor-is-valid): The a element has `href` and `onClick`.
eslint-plugin-jsx-a11y(anchor-is-valid): The `a` element has `href` and `onClick`.
╭─[anchor_is_valid.tsx:1:2]
1<a href={'javascript:void(0)'} onClick={() => void 0} />
· ─
╰────
help: Use a `button` element instead of an `a` element.

eslint-plugin-jsx-a11y(anchor-is-valid): The a element has `href` and `onClick`.
eslint-plugin-jsx-a11y(anchor-is-valid): The `a` element has `href` and `onClick`.
╭─[anchor_is_valid.tsx:1:2]
1<Link href='#' onClick={() => void 0} />
· ────
Expand Down

0 comments on commit d1d54a6

Please sign in to comment.