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