-
-
Notifications
You must be signed in to change notification settings - Fork 526
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(biome_css_analyze): implement noIrregularWhitespaceCss (#3428)
- Loading branch information
1 parent
74397eb
commit 3229b32
Showing
12 changed files
with
762 additions
and
92 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
98 changes: 98 additions & 0 deletions
98
crates/biome_css_analyze/src/lint/nursery/no_irregular_whitespace_css.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
use biome_analyze::{ | ||
context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, | ||
}; | ||
use biome_console::markup; | ||
use biome_css_syntax::{AnyCssRule, CssLanguage}; | ||
use biome_rowan::{AstNode, Direction, SyntaxToken, TextRange}; | ||
|
||
const IRREGULAR_WHITESPACES: &[char; 22] = &[ | ||
'\u{c}', '\u{b}', '\u{85}', '\u{feff}', '\u{a0}', '\u{1680}', '\u{180e}', '\u{2000}', | ||
'\u{2001}', '\u{2002}', '\u{2003}', '\u{2004}', '\u{2005}', '\u{2006}', '\u{2007}', '\u{2008}', | ||
'\u{2009}', '\u{200a}', '\u{200b}', '\u{202f}', '\u{205f}', '\u{3000}', | ||
]; | ||
|
||
declare_lint_rule! { | ||
/// Disallows the use of irregular whitespace. | ||
/// | ||
/// Using irregular whitespace would lead to the failure of selecting the correct target. | ||
/// | ||
/// ## Examples | ||
/// | ||
/// ### Invalid | ||
/// | ||
/// ```css,expect_diagnostic | ||
/// .firstClass.secondClass { | ||
/// color: red; | ||
/// } | ||
/// ``` | ||
/// | ||
/// ```css,expect_diagnostic | ||
/// .firstClass .secondClass { | ||
/// color:red; | ||
/// } | ||
/// ``` | ||
/// ### Valid | ||
/// | ||
/// ```css | ||
/// .firstClass .secondClass { | ||
/// color: red; | ||
/// } | ||
/// ``` | ||
/// | ||
pub NoIrregularWhitespaceCss { | ||
version: "next", | ||
name: "noIrregularWhitespaceCss", | ||
language: "css", | ||
recommended: false, | ||
sources: &[RuleSource::Stylelint("no-irregular-whitespace")], | ||
} | ||
} | ||
|
||
impl Rule for NoIrregularWhitespaceCss { | ||
type Query = Ast<AnyCssRule>; | ||
type State = TextRange; | ||
type Signals = Vec<Self::State>; | ||
type Options = (); | ||
|
||
fn run(ctx: &RuleContext<Self>) -> Self::Signals { | ||
let node = ctx.query(); | ||
get_irregular_whitespace(node) | ||
} | ||
|
||
fn diagnostic(_: &RuleContext<Self>, range: &Self::State) -> Option<RuleDiagnostic> { | ||
Some( | ||
RuleDiagnostic::new( | ||
rule_category!(), | ||
range, | ||
markup! { | ||
"Irregular whitespace found." | ||
}, | ||
) | ||
.note(markup! { | ||
"Replace the irregular whitespace with normal whitespaces." | ||
}), | ||
) | ||
} | ||
} | ||
|
||
fn get_irregular_whitespace(node: &AnyCssRule) -> Vec<TextRange> { | ||
let syntax = node.syntax(); | ||
let mut all_whitespaces_token: Vec<TextRange> = vec![]; | ||
let matches_irregular_whitespace = |token: &SyntaxToken<CssLanguage>| { | ||
!token.has_leading_comments() | ||
&& !token.has_trailing_comments() | ||
&& token.text().chars().any(|char| { | ||
IRREGULAR_WHITESPACES | ||
.iter() | ||
.any(|irregular_whitespace| &char == irregular_whitespace) | ||
}) | ||
}; | ||
|
||
for token in syntax.descendants_tokens(Direction::Next) { | ||
if matches_irregular_whitespace(&token) { | ||
all_whitespaces_token.push(token.text_range()); | ||
} | ||
} | ||
|
||
all_whitespaces_token | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
97 changes: 97 additions & 0 deletions
97
crates/biome_css_analyze/tests/specs/nursery/noIrregularWhitespaceCss/invalid.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
/* \u{b} */ | ||
@import 'a.css'; | ||
/* \u{c} */ | ||
@layermodule, state; | ||
/* \u{feff} */ | ||
.firstClass.secondClass { | ||
padding: 10px; | ||
} | ||
/* \u{a0} */ | ||
.firstClass .secondClass { | ||
padding: 10px; | ||
} | ||
/* \u{1680} */ | ||
.firstClass .secondClass { | ||
padding: 10px; | ||
} | ||
/* \u{2000} */ | ||
.firstClass .secondClass { | ||
padding: 10px; | ||
} | ||
/* \u{2001} */ | ||
.firstClass .secondClass { | ||
flex: 1 1 100px; | ||
} | ||
/* \u{2002} */ | ||
.firstClass.secondClass { | ||
padding: 10px; | ||
} | ||
/* \u{2003} */ | ||
.firstClass .secondClass { | ||
padding: 10px; | ||
} | ||
/* \u{2004} */ | ||
.firstClass .secondClass { | ||
padding: 10px; | ||
} | ||
/* \u{2005} */ | ||
.firstClass .secondClass { | ||
padding: 10px; | ||
} | ||
/* \u{2006} */ | ||
.firstClass .secondClass { | ||
padding: 10px; | ||
} | ||
/* \u{2007} */ | ||
.firstClass .secondClass { | ||
padding: 10px; | ||
} | ||
/* \u{2008} */ | ||
@view-transition { | ||
navigation: auto; | ||
} | ||
/* \u{2009} */ | ||
@layer state { | ||
body { | ||
padding: 10px; | ||
} | ||
} | ||
/* \u{200a} */ | ||
@layer state { | ||
body { | ||
padding: 10px; | ||
} | ||
} | ||
/* \u{200b} */ | ||
@keyframes slidein { | ||
from{ | ||
transform: translateX(0%); | ||
} | ||
|
||
to { | ||
transform: translateX(100%); | ||
} | ||
} | ||
/* \u{202f} */ | ||
@font-face { | ||
font-family: "Trickster"; | ||
src: | ||
local("Trickster"), | ||
url("trickster-COLRv1.otf") format("opentype") tech(color-COLRv1), | ||
url("trickster-outline.otf") format("opentype"), | ||
url("trickster-outline.woff") format("woff"); | ||
} | ||
/* \u{205f} */ | ||
@keyframes slidein { | ||
from { | ||
transform: translateX(0%); | ||
} | ||
|
||
to { | ||
transform: translateX(100%); | ||
} | ||
} | ||
/* \u{3000} */ | ||
@container (width < 15rem) { | ||
color: blue; | ||
} |
Oops, something went wrong.