From 3ee3123a4e2469e09b73fc8b8b2c04be94c9d45f Mon Sep 17 00:00:00 2001 From: Kazuhiro-Mimaki Date: Wed, 12 Jun 2024 23:01:43 +0900 Subject: [PATCH] feat: implement no_unknown_type_selector --- crates/biome_css_analyze/src/keywords.rs | 300 +++++++++++++++++- .../lint/nursery/no_unknown_type_selector.rs | 95 ++++++ crates/biome_css_analyze/src/utils.rs | 19 +- .../nursery/noUnknownTypeSelector/invalid.css | 25 ++ .../noUnknownTypeSelector/invalid.css.snap | 183 +++++++++++ .../nursery/noUnknownTypeSelector/valid.css | 21 ++ .../noUnknownTypeSelector/valid.css.snap | 29 ++ 7 files changed, 666 insertions(+), 6 deletions(-) create mode 100644 crates/biome_css_analyze/src/lint/nursery/no_unknown_type_selector.rs create mode 100644 crates/biome_css_analyze/tests/specs/nursery/noUnknownTypeSelector/invalid.css create mode 100644 crates/biome_css_analyze/tests/specs/nursery/noUnknownTypeSelector/invalid.css.snap create mode 100644 crates/biome_css_analyze/tests/specs/nursery/noUnknownTypeSelector/valid.css create mode 100644 crates/biome_css_analyze/tests/specs/nursery/noUnknownTypeSelector/valid.css.snap diff --git a/crates/biome_css_analyze/src/keywords.rs b/crates/biome_css_analyze/src/keywords.rs index 4b41b8dafdf6..68924f4beed4 100644 --- a/crates/biome_css_analyze/src/keywords.rs +++ b/crates/biome_css_analyze/src/keywords.rs @@ -5433,17 +5433,290 @@ pub const RESET_TO_INITIAL_PROPERTIES_BY_FONT: [&str; 13] = [ "font-variation-settings", ]; +// https://developer.mozilla.org/ja/docs/Web/HTML/Element +// https://github.com/sindresorhus/html-tags/blob/main/html-tags.json +pub const HTML_TAGS: [&str; 146] = [ + "a", + "abbr", + "acronym", + "address", + "applet", + "area", + "article", + "aside", + "audio", + "b", + "base", + "basefont", + "bdi", + "bdo", + "blink", + "blockquote", + "body", + "br", + "button", + "canvas", + "caption", + "center", + "cite", + "code", + "col", + "colgroup", + "content", + "data", + "datalist", + "dd", + "del", + "details", + "dfn", + "dir", + "div", + "dl", + "dt", + "em", + "embed", + "fencedframe", + "fieldset", + "figcaption", + "figure", + "font", + "footer", + "form", + "frame", + "frameset", + "h1", + "h2", + "h3", + "h4", + "h5", + "h6", + "head", + "header", + "hgroup", + "hr", + "html", + "i", + "iframe", + "img", + "input", + "ins", + "isindex", + "kbd", + "keygen", + "label", + "legend", + "li", + "link", + "listbox", + "listing", + "main", + "map", + "mark", + "marquee", + "math", + "menu", + "menuitem", + "meta", + "meter", + "model", + "multicol", + "nav", + "nextid", + "nobr", + "noembed", + "noframes", + "noscript", + "object", + "ol", + "optgroup", + "option", + "output", + "p", + "param", + "picture", + "plaintext", + "portal", + "pre", + "progress", + "q", + "rb", + "rp", + "rt", + "rtc", + "ruby", + "s", + "samp", + "script", + "search", + "section", + "select", + "selectlist", + "slot", + "small", + "source", + "spacer", + "span", + "strike", + "strong", + "style", + "sub", + "summary", + "sup", + "svg", + "table", + "tbody", + "td", + "template", + "textarea", + "tfoot", + "th", + "thead", + "time", + "title", + "tr", + "track", + "tt", + "u", + "ul", + "var", + "video", + "wbr", + "xmp", +]; + +// https://developer.mozilla.org/ja/docs/Web/SVG/Element +// https://github.com/element-io/svg-tags/blob/master/lib/svg-tags.json +pub const SVG_TAGS: [&str; 81] = [ + "a", + "altGlyph", + "altGlyphDef", + "altGlyphItem", + "animate", + "animateColor", + "animateMotion", + "animateTransform", + "circle", + "clipPath", + "color-profile", + "cursor", + "defs", + "desc", + "ellipse", + "feBlend", + "feColorMatrix", + "feComponentTransfer", + "feComposite", + "feConvolveMatrix", + "feDiffuseLighting", + "feDisplacementMap", + "feDistantLight", + "feDropShadow", + "feFlood", + "feFuncA", + "feFuncB", + "feFuncG", + "feFuncR", + "feGaussianBlur", + "feImage", + "feMerge", + "feMergeNode", + "feMorphology", + "feOffset", + "fePointLight", + "feSpecularLighting", + "feSpotLight", + "feTile", + "feTurbulence", + "filter", + "font", + "font-face", + "font-face-format", + "font-face-name", + "font-face-src", + "font-face-uri", + "foreignObject", + "g", + "glyph", + "glyphRef", + "hatchpath", + "hkern", + "image", + "line", + "linearGradient", + "marker", + "mask", + "metadata", + "missing-glyph", + "mpath", + "path", + "pattern", + "polygon", + "polyline", + "radialGradient", + "rect", + "script", + "set", + "stop", + "style", + "svg", + "switch", + "symbol", + "text", + "textPath", + "title", + "tspan", + "use", + "view", + "vkern", +]; + +// https://developer.mozilla.org/ja/docs/Web/MathML/Element +pub const MATH_ML_TAGS: [&str; 32] = [ + "annotation", + "annotation-xml", + "maction", + "math", + "menclose", + "merror", + "mfenced", + "mfrac", + "mi", + "mmultiscripts", + "mn", + "mo", + "mover", + "mpadded", + "mphantom", + "mprescripts", + "mroot", + "mrow", + "ms", + "mspace", + "msqrt", + "mstyle", + "msub", + "msubsup", + "msup", + "mtable", + "mtd", + "mtext", + "mtr", + "munder", + "munderover", + "semantics", +]; + #[cfg(test)] mod tests { use std::collections::HashSet; use super::{ - FUNCTION_KEYWORDS, KNOWN_EDGE_PROPERTIES, KNOWN_EXPLORER_PROPERTIES, + FUNCTION_KEYWORDS, HTML_TAGS, KNOWN_EDGE_PROPERTIES, KNOWN_EXPLORER_PROPERTIES, KNOWN_FIREFOX_PROPERTIES, KNOWN_PROPERTIES, KNOWN_SAFARI_PROPERTIES, KNOWN_SAMSUNG_INTERNET_PROPERTIES, KNOWN_US_BROWSER_PROPERTIES, - LONGHAND_SUB_PROPERTIES_OF_SHORTHAND_PROPERTIES, MEDIA_FEATURE_NAMES, + LONGHAND_SUB_PROPERTIES_OF_SHORTHAND_PROPERTIES, MATH_ML_TAGS, MEDIA_FEATURE_NAMES, RESET_TO_INITIAL_PROPERTIES_BY_BORDER, RESET_TO_INITIAL_PROPERTIES_BY_FONT, - SHORTHAND_PROPERTIES, + SHORTHAND_PROPERTIES, SVG_TAGS, }; #[test] @@ -5635,4 +5908,25 @@ mod tests { .any(|&x| !set.insert(x)); assert!(!has_duplicates); } + + #[test] + fn test_selector_types() { + for items in HTML_TAGS.windows(2) { + assert!(items[0] < items[1], "{} < {}", items[0], items[1]); + } + } + + #[test] + fn test_svg_tags() { + for items in SVG_TAGS.windows(2) { + assert!(items[0] < items[1], "{} < {}", items[0], items[1]); + } + } + + #[test] + fn test_math_ml_tags() { + for items in MATH_ML_TAGS.windows(2) { + assert!(items[0] < items[1], "{} < {}", items[0], items[1]); + } + } } diff --git a/crates/biome_css_analyze/src/lint/nursery/no_unknown_type_selector.rs b/crates/biome_css_analyze/src/lint/nursery/no_unknown_type_selector.rs new file mode 100644 index 000000000000..15dd5a247321 --- /dev/null +++ b/crates/biome_css_analyze/src/lint/nursery/no_unknown_type_selector.rs @@ -0,0 +1,95 @@ +use biome_analyze::{context::RuleContext, declare_rule, Ast, Rule, RuleDiagnostic, RuleSource}; +use biome_console::markup; +use biome_css_syntax::CssTypeSelector; +use biome_rowan::AstNode; + +use crate::utils::is_known_type_selector; + +declare_rule! { + /// Disallow unknown type selectors. + /// + /// This rule considers tags defined in the HTML, SVG, and MathML specifications to be known. + /// For details on known CSS type selectors, see the following links + /// - https://developer.mozilla.org/en-US/docs/Web/CSS/Type_selectors + /// - https://developer.mozilla.org/ja/docs/Web/HTML/Element + /// - https://developer.mozilla.org/ja/docs/Web/SVG/Element + /// - https://developer.mozilla.org/ja/docs/Web/MathML/Element + /// + /// This rule allows custom elements. + /// + /// ## Examples + /// + /// ### Invalid + /// + /// ```css,expect_diagnostic + /// unknown {} + /// ``` + /// + /// ```css,expect_diagnostic + /// unknown > ul {} + /// ``` + /// + /// ```css,expect_diagnostic + /// x-Foo {} + /// ``` + /// + /// ### Valid + /// + /// ```css + /// input {} + /// ``` + /// + /// ```css + /// ul > li {} + /// ``` + /// + /// ```css + /// x-foo {} + /// ``` + /// + pub NoUnknownTypeSelector { + version: "next", + name: "noUnknownTypeSelector", + language: "css", + recommended: true, + sources: &[RuleSource::Stylelint("selector-type-no-unknown")], + } +} + +impl Rule for NoUnknownTypeSelector { + type Query = Ast; + type State = CssTypeSelector; + type Signals = Option; + type Options = (); + + fn run(ctx: &RuleContext) -> Option { + let css_type_selector = ctx.query(); + let type_selector = css_type_selector + .ident() + .ok()? + .value_token() + .ok()? + .token_text_trimmed(); + if !is_known_type_selector(&type_selector) { + return Some(css_type_selector.clone()); + } + None + } + + fn diagnostic(_: &RuleContext, node: &Self::State) -> Option { + let span = node.range(); + Some( + RuleDiagnostic::new( + rule_category!(), + span, + markup! { + "Unknown type selector is not allowed." + }, + ) + .note(markup! { + "See ""MDN web docs"" for more details." + }).note(markup! { + "Consider replacing this issue, replace the unknown type selector with valid one."}) + ) + } +} diff --git a/crates/biome_css_analyze/src/utils.rs b/crates/biome_css_analyze/src/utils.rs index df274dc12ba9..d70c7ebaa99e 100644 --- a/crates/biome_css_analyze/src/utils.rs +++ b/crates/biome_css_analyze/src/utils.rs @@ -2,15 +2,15 @@ use crate::keywords::{ AT_RULE_PAGE_PSEUDO_CLASSES, A_NPLUS_BNOTATION_PSEUDO_CLASSES, A_NPLUS_BOF_SNOTATION_PSEUDO_CLASSES, BASIC_KEYWORDS, FONT_FAMILY_KEYWORDS, FONT_SIZE_KEYWORDS, FONT_STRETCH_KEYWORDS, FONT_STYLE_KEYWORDS, FONT_VARIANTS_KEYWORDS, - FONT_WEIGHT_ABSOLUTE_KEYWORDS, FONT_WEIGHT_NUMERIC_KEYWORDS, FUNCTION_KEYWORDS, + FONT_WEIGHT_ABSOLUTE_KEYWORDS, FONT_WEIGHT_NUMERIC_KEYWORDS, FUNCTION_KEYWORDS, HTML_TAGS, KNOWN_CHROME_PROPERTIES, KNOWN_EDGE_PROPERTIES, KNOWN_EXPLORER_PROPERTIES, KNOWN_FIREFOX_PROPERTIES, KNOWN_PROPERTIES, KNOWN_SAFARI_PROPERTIES, KNOWN_SAMSUNG_INTERNET_PROPERTIES, KNOWN_US_BROWSER_PROPERTIES, LEVEL_ONE_AND_TWO_PSEUDO_ELEMENTS, LINE_HEIGHT_KEYWORDS, LINGUISTIC_PSEUDO_CLASSES, LOGICAL_COMBINATIONS_PSEUDO_CLASSES, LONGHAND_SUB_PROPERTIES_OF_SHORTHAND_PROPERTIES, - MEDIA_FEATURE_NAMES, OTHER_PSEUDO_CLASSES, OTHER_PSEUDO_ELEMENTS, + MATH_ML_TAGS, MEDIA_FEATURE_NAMES, OTHER_PSEUDO_CLASSES, OTHER_PSEUDO_ELEMENTS, RESET_TO_INITIAL_PROPERTIES_BY_BORDER, RESET_TO_INITIAL_PROPERTIES_BY_FONT, - RESOURCE_STATE_PSEUDO_CLASSES, SHADOW_TREE_PSEUDO_ELEMENTS, SHORTHAND_PROPERTIES, + RESOURCE_STATE_PSEUDO_CLASSES, SHADOW_TREE_PSEUDO_ELEMENTS, SHORTHAND_PROPERTIES, SVG_TAGS, SYSTEM_FAMILY_NAME_KEYWORDS, VENDOR_PREFIXES, VENDOR_SPECIFIC_PSEUDO_ELEMENTS, }; use biome_css_syntax::{AnyCssGenericComponentValue, AnyCssValue, CssGenericComponentValueList}; @@ -215,3 +215,16 @@ pub fn get_reset_to_initial_properties(shorthand_property: &str) -> &'static [&' _ => &[], } } + +fn is_custom_element(prop: &str) -> bool { + prop.contains('-') && prop.eq(prop.to_lowercase().as_str()) +} + +/// Check if the input string is a known type selector. +pub fn is_known_type_selector(prop: &str) -> bool { + let input = prop.to_lowercase(); + HTML_TAGS.binary_search(&input.as_str()).is_ok() + || SVG_TAGS.binary_search(&prop).is_ok() + || MATH_ML_TAGS.binary_search(&input.as_str()).is_ok() + || is_custom_element(prop) +} diff --git a/crates/biome_css_analyze/tests/specs/nursery/noUnknownTypeSelector/invalid.css b/crates/biome_css_analyze/tests/specs/nursery/noUnknownTypeSelector/invalid.css new file mode 100644 index 000000000000..2fe3d8798769 --- /dev/null +++ b/crates/biome_css_analyze/tests/specs/nursery/noUnknownTypeSelector/invalid.css @@ -0,0 +1,25 @@ +unknown { +} + +ul unknown { +} + +unknown ul { +} + +li > hoge { +} + +fuga > li { +} + +table, +unknown { +} + +unknown, +article { +} + +x-Foo { +} diff --git a/crates/biome_css_analyze/tests/specs/nursery/noUnknownTypeSelector/invalid.css.snap b/crates/biome_css_analyze/tests/specs/nursery/noUnknownTypeSelector/invalid.css.snap new file mode 100644 index 000000000000..a32a4fe8992b --- /dev/null +++ b/crates/biome_css_analyze/tests/specs/nursery/noUnknownTypeSelector/invalid.css.snap @@ -0,0 +1,183 @@ +--- +source: crates/biome_css_analyze/tests/spec_tests.rs +expression: invalid.css +--- +# Input +```css +unknown { +} + +ul unknown { +} + +unknown ul { +} + +li > hoge { +} + +fuga > li { +} + +table, +unknown { +} + +unknown, +article { +} + +x-Foo { +} + +``` + +# Diagnostics +``` +invalid.css:1:1 lint/nursery/noUnknownTypeSelector ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Unknown type selector is not allowed. + + > 1 │ unknown { + │ ^^^^^^^ + 2 │ } + 3 │ + + i See MDN web docs for more details. + + i Consider replacing this issue, replace the unknown type selector with valid one. + + +``` + +``` +invalid.css:4:4 lint/nursery/noUnknownTypeSelector ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Unknown type selector is not allowed. + + 2 │ } + 3 │ + > 4 │ ul unknown { + │ ^^^^^^^ + 5 │ } + 6 │ + + i See MDN web docs for more details. + + i Consider replacing this issue, replace the unknown type selector with valid one. + + +``` + +``` +invalid.css:7:1 lint/nursery/noUnknownTypeSelector ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Unknown type selector is not allowed. + + 5 │ } + 6 │ + > 7 │ unknown ul { + │ ^^^^^^^ + 8 │ } + 9 │ + + i See MDN web docs for more details. + + i Consider replacing this issue, replace the unknown type selector with valid one. + + +``` + +``` +invalid.css:10:6 lint/nursery/noUnknownTypeSelector ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Unknown type selector is not allowed. + + 8 │ } + 9 │ + > 10 │ li > hoge { + │ ^^^^ + 11 │ } + 12 │ + + i See MDN web docs for more details. + + i Consider replacing this issue, replace the unknown type selector with valid one. + + +``` + +``` +invalid.css:13:1 lint/nursery/noUnknownTypeSelector ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Unknown type selector is not allowed. + + 11 │ } + 12 │ + > 13 │ fuga > li { + │ ^^^^ + 14 │ } + 15 │ + + i See MDN web docs for more details. + + i Consider replacing this issue, replace the unknown type selector with valid one. + + +``` + +``` +invalid.css:17:1 lint/nursery/noUnknownTypeSelector ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Unknown type selector is not allowed. + + 16 │ table, + > 17 │ unknown { + │ ^^^^^^^ + 18 │ } + 19 │ + + i See MDN web docs for more details. + + i Consider replacing this issue, replace the unknown type selector with valid one. + + +``` + +``` +invalid.css:20:1 lint/nursery/noUnknownTypeSelector ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Unknown type selector is not allowed. + + 18 │ } + 19 │ + > 20 │ unknown, + │ ^^^^^^^ + 21 │ article { + 22 │ } + + i See MDN web docs for more details. + + i Consider replacing this issue, replace the unknown type selector with valid one. + + +``` + +``` +invalid.css:24:1 lint/nursery/noUnknownTypeSelector ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Unknown type selector is not allowed. + + 22 │ } + 23 │ + > 24 │ x-Foo { + │ ^^^^^ + 25 │ } + 26 │ + + i See MDN web docs for more details. + + i Consider replacing this issue, replace the unknown type selector with valid one. + + +``` diff --git a/crates/biome_css_analyze/tests/specs/nursery/noUnknownTypeSelector/valid.css b/crates/biome_css_analyze/tests/specs/nursery/noUnknownTypeSelector/valid.css new file mode 100644 index 000000000000..cbe72617a5c2 --- /dev/null +++ b/crates/biome_css_analyze/tests/specs/nursery/noUnknownTypeSelector/valid.css @@ -0,0 +1,21 @@ +input { +} + +ul li { +} + +li > a { +} + +table, +tr { +} + +x-foo { +} + +g { +} + +mfrac { +} diff --git a/crates/biome_css_analyze/tests/specs/nursery/noUnknownTypeSelector/valid.css.snap b/crates/biome_css_analyze/tests/specs/nursery/noUnknownTypeSelector/valid.css.snap new file mode 100644 index 000000000000..fde7a93556c3 --- /dev/null +++ b/crates/biome_css_analyze/tests/specs/nursery/noUnknownTypeSelector/valid.css.snap @@ -0,0 +1,29 @@ +--- +source: crates/biome_css_analyze/tests/spec_tests.rs +expression: valid.css +--- +# Input +```css +input { +} + +ul li { +} + +li > a { +} + +table, +tr { +} + +x-foo { +} + +g { +} + +mfrac { +} + +```