Skip to content

Commit 52071f5

Browse files
denbezrukovautofix-ci[bot]ematipico
authored
fix(css_analyze): handle CSS Modules pseudo-class 'local' in NoUnknownPseudoClass rule (#6981) (#7685)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Emanuele Stoppa <[email protected]>
1 parent 5bc57d3 commit 52071f5

File tree

7 files changed

+63
-9
lines changed

7 files changed

+63
-9
lines changed

.changeset/big-pans-begin.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@biomejs/biome": patch
3+
---
4+
5+
Fixed [#6981](https://github.com/biomejs/biome/issues/6981): The [NoUnknownPseudoClass](https://biomejs.dev/linter/rules/no-unknown-pseudo-class/) rule no longer reports local pseudo-classes when CSS Modules are used.
6+

crates/biome_css_analyze/src/keywords.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -980,6 +980,11 @@ pub const OTHER_PSEUDO_CLASSES: [&str; 51] = [
980980
"visited",
981981
"window-inactive",
982982
];
983+
984+
// CSS Modules pseudo-classes
985+
// https://github.com/css-modules/css-modules
986+
pub const CSS_MODULE_PSEUDO_CLASSES: [&str; 2] = ["global", "local"];
987+
983988
// https://github.com/known-css/known-css-properties/blob/master/source/w3c.json
984989
pub const KNOWN_PROPERTIES: &[&str] = &[
985990
"-webkit-line-clamp",

crates/biome_css_analyze/src/lint/correctness/no_unknown_pseudo_class.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use crate::{
22
keywords::{WEBKIT_SCROLLBAR_PSEUDO_CLASSES, WEBKIT_SCROLLBAR_PSEUDO_ELEMENTS},
3-
utils::{is_custom_selector, is_known_pseudo_class, is_page_pseudo_class, vendor_prefixed},
3+
utils::{
4+
is_css_module_pseudo_class, is_custom_selector, is_known_pseudo_class,
5+
is_page_pseudo_class, vendor_prefixed,
6+
},
47
};
58
use biome_analyze::{
69
Ast, Rule, RuleDiagnostic, RuleSource, context::RuleContext, declare_lint_rule,
@@ -174,9 +177,7 @@ impl Rule for NoUnknownPseudoClass {
174177
}
175178
};
176179

177-
let is_valid_global = lower_name == "global" && is_css_modules;
178-
179-
if is_valid_class || is_valid_global {
180+
if is_valid_class || is_css_modules && is_css_module_pseudo_class(lower_name) {
180181
None
181182
} else {
182183
Some(NoUnknownPseudoClassSelectorState {

crates/biome_css_analyze/src/utils.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::keywords::{
22
A_NPLUS_BNOTATION_PSEUDO_CLASSES, A_NPLUS_BOF_SNOTATION_PSEUDO_CLASSES,
3-
AT_RULE_PAGE_PSEUDO_CLASSES, HTML_TAGS, KNOWN_CHROME_PROPERTIES, KNOWN_EDGE_PROPERTIES,
4-
KNOWN_EXPLORER_PROPERTIES, KNOWN_FIREFOX_PROPERTIES, KNOWN_PROPERTIES, KNOWN_SAFARI_PROPERTIES,
5-
KNOWN_SAMSUNG_INTERNET_PROPERTIES, KNOWN_US_BROWSER_PROPERTIES,
3+
AT_RULE_PAGE_PSEUDO_CLASSES, CSS_MODULE_PSEUDO_CLASSES, HTML_TAGS, KNOWN_CHROME_PROPERTIES,
4+
KNOWN_EDGE_PROPERTIES, KNOWN_EXPLORER_PROPERTIES, KNOWN_FIREFOX_PROPERTIES, KNOWN_PROPERTIES,
5+
KNOWN_SAFARI_PROPERTIES, KNOWN_SAMSUNG_INTERNET_PROPERTIES, KNOWN_US_BROWSER_PROPERTIES,
66
LEVEL_ONE_AND_TWO_PSEUDO_ELEMENTS, LINGUISTIC_PSEUDO_CLASSES,
77
LOGICAL_COMBINATIONS_PSEUDO_CLASSES, LONGHAND_SUB_PROPERTIES_OF_SHORTHAND_PROPERTIES,
88
MATH_ML_TAGS, MEDIA_FEATURE_NAMES, OTHER_PSEUDO_CLASSES, OTHER_PSEUDO_ELEMENTS,
@@ -141,3 +141,14 @@ pub fn is_known_type_selector(prop: &str) -> bool {
141141
|| MATH_ML_TAGS.binary_search(&input.as_ref()).is_ok()
142142
|| is_custom_element(prop)
143143
}
144+
145+
/// Check if the input string is a CSS module pseudo-class.
146+
///
147+
/// CSS modules support special pseudo-classes like `:global` and `:local` for
148+
/// scoping control.
149+
/// These are only valid when CSS modules are enabled.
150+
///
151+
/// See https://github.com/css-modules/css-modules for more details.
152+
pub fn is_css_module_pseudo_class(prop: &str) -> bool {
153+
CSS_MODULE_PSEUDO_CLASSES.binary_search(&prop).is_ok()
154+
}

crates/biome_css_analyze/tests/spec_tests.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,19 @@ pub(crate) fn analyze_and_snap(
115115
parser_options: CssParserOptions,
116116
plugins: AnalyzerPluginSlice,
117117
) {
118+
let mut diagnostics = Vec::new();
119+
let options = create_analyzer_options::<CssLanguage>(input_file, &mut diagnostics);
120+
121+
let parser_options = if options.css_modules() {
122+
parser_options.allow_css_modules()
123+
} else {
124+
parser_options
125+
};
126+
118127
let parsed = parse_css(input_code, parser_options);
119128
let root = parsed.tree();
120129

121-
let mut diagnostics = Vec::new();
122130
let mut code_fixes = Vec::new();
123-
let options = create_analyzer_options::<CssLanguage>(input_file, &mut diagnostics);
124131

125132
let (_, errors) = biome_css_analyze::analyze(&root, filter, &options, plugins, |event| {
126133
if let Some(mut diag) = event.diagnostic() {

crates/biome_css_analyze/tests/specs/correctness/noUnknownPseudoClass/validGlobal.css

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,15 @@
22
.meow :global(.global) {
33
color: blue;
44
}
5+
6+
.foo :local(.bar) {
7+
color: red;
8+
}
9+
10+
:local(.component) {
11+
margin: 0;
12+
}
13+
14+
.parent :local(.child):hover {
15+
background: blue;
16+
}

crates/biome_css_analyze/tests/specs/correctness/noUnknownPseudoClass/validGlobal.css.snap

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,16 @@ expression: validGlobal.css
99
color: blue;
1010
}
1111
12+
.foo :local(.bar) {
13+
color: red;
14+
}
15+
16+
:local(.component) {
17+
margin: 0;
18+
}
19+
20+
.parent :local(.child):hover {
21+
background: blue;
22+
}
23+
1224
```

0 commit comments

Comments
 (0)