yew_macro/html_tree/lint/
mod.rs1use proc_macro_error::emit_warning;
5use syn::spanned::Spanned;
6
7use super::html_element::{HtmlElement, TagName};
8use super::HtmlTree;
9use crate::props::{ElementProps, Prop};
10
11pub trait Lint {
15 #[cfg_attr(not(yew_lints), allow(dead_code))]
16 fn lint(element: &HtmlElement);
17}
18
19pub fn lint_all(tree: &HtmlTree) {
21 lint::<AHrefLint>(tree);
22 lint::<ImgAltLint>(tree);
23}
24
25pub fn lint<L>(tree: &HtmlTree)
27where
28 L: Lint,
29{
30 let _ = L::lint;
31 #[cfg(not(yew_lints))]
32 let _ = tree;
33 #[cfg(yew_lints)]
34 match tree {
35 HtmlTree::List(list) => {
36 for child in &list.children.0 {
37 lint::<L>(child)
38 }
39 }
40 HtmlTree::Element(el) => L::lint(el),
41 _ => {}
42 }
43}
44
45fn get_attribute<'a>(props: &'a ElementProps, name: &str) -> Option<&'a Prop> {
50 props
51 .attributes
52 .iter()
53 .find(|item| item.label.eq_ignore_ascii_case(name))
54}
55
56pub struct AHrefLint;
58
59impl Lint for AHrefLint {
60 fn lint(element: &HtmlElement) {
61 if let TagName::Lit(ref tag_name) = element.name {
62 if !tag_name.eq_ignore_ascii_case("a") {
63 return;
64 };
65 if let Some(prop) = get_attribute(&element.props, "href") {
66 if let syn::Expr::Lit(lit) = &prop.value {
67 if let syn::Lit::Str(href) = &lit.lit {
68 let href_value = href.value();
69 match href_value.as_ref() {
70 "#" | "javascript:void(0)" => emit_warning!(
71 lit.span(),
72 format!("'{href_value}' is not a suitable value for the `href` attribute. \
73 Without a meaningful attribute assistive technologies \
74 will struggle to understand your webpage. \
75 https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML#onclick_events"
76 )),
77 _ => {}
78
79 }
80 }
81 };
82 } else {
83 emit_warning!(
84 quote::quote! {#tag_name}.span(),
85 "All `<a>` elements should have a `href` attribute. This makes it possible \
86 for assistive technologies to correctly interpret what your links point to. \
87 https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML#more_on_links"
88 )
89 }
90 }
91 }
92}
93
94pub struct ImgAltLint;
96
97impl Lint for ImgAltLint {
98 fn lint(element: &HtmlElement) {
99 if let super::html_element::TagName::Lit(ref tag_name) = element.name {
100 if !tag_name.eq_ignore_ascii_case("img") {
101 return;
102 };
103 if get_attribute(&element.props, "alt").is_none() {
104 emit_warning!(
105 quote::quote! {#tag_name}.span(),
106 "All `<img>` tags should have an `alt` attribute which provides a \
107 human-readable description "
108 )
109 }
110 }
111 }
112}