This is unreleased documentation for Yew Next version.
For up-to-date documentation, see the latest version on docs.rs.

yew_macro/html_tree/lint/
mod.rs

1//! Lints to catch possible misuse of the `html!` macro use. At the moment these are mostly focused
2//! on accessibility.
3
4use 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
11/// Lints HTML elements to check if they are well formed. If the element is not well-formed, then
12/// use `proc-macro-error` (and the `emit_warning!` macro) to produce a warning. At present, these
13/// are only emitted on nightly.
14pub trait Lint {
15    #[cfg_attr(not(yew_lints), allow(dead_code))]
16    fn lint(element: &HtmlElement);
17}
18
19/// Applies all the lints to the HTML tree.
20pub fn lint_all(tree: &HtmlTree) {
21    lint::<AHrefLint>(tree);
22    lint::<ImgAltLint>(tree);
23}
24
25/// Applies a specific lint to the HTML tree.
26pub 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
44/// Retrieves an attribute from an element and returns a reference valid for the lifetime of the
45/// element (if that attribute can be found on the prop).
46///
47/// Attribute names are lowercased before being compared (so pass "href" for `name` and not "HREF").
48fn 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
55/// Lints to check if anchor (`<a>`) tags have valid `href` attributes defined.
56pub 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
93/// Checks to make sure that images have `alt` attributes defined.
94pub 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}