Skip to content

Commit

Permalink
feat(biome_css_analyzer): noDuplicateCustomProperties
Browse files Browse the repository at this point in the history
  • Loading branch information
chansuke committed May 9, 2024
1 parent d74b584 commit eebb582
Show file tree
Hide file tree
Showing 11 changed files with 247 additions and 56 deletions.
131 changes: 75 additions & 56 deletions crates/biome_configuration/src/linter/rules.rs

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions crates/biome_css_analyze/src/lint/nursery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use biome_analyze::declare_group;
pub mod no_color_invalid_hex;
pub mod no_css_empty_block;
pub mod no_duplicate_at_import_rules;
pub mod no_duplicate_custom_properties;
pub mod no_duplicate_font_names;
pub mod no_duplicate_selectors_keyframe_block;
pub mod no_important_in_keyframe;
Expand All @@ -22,6 +23,7 @@ declare_group! {
self :: no_color_invalid_hex :: NoColorInvalidHex ,
self :: no_css_empty_block :: NoCssEmptyBlock ,
self :: no_duplicate_at_import_rules :: NoDuplicateAtImportRules ,
self :: no_duplicate_custom_properties :: NoDuplicateCustomProperties ,
self :: no_duplicate_font_names :: NoDuplicateFontNames ,
self :: no_duplicate_selectors_keyframe_block :: NoDuplicateSelectorsKeyframeBlock ,
self :: no_important_in_keyframe :: NoImportantInKeyframe ,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use biome_analyze::{context::RuleContext, declare_rule, Ast, Rule, RuleDiagnostic};
use biome_console::markup;
use biome_css_syntax::{CssDeclarationOrRuleList, CssLanguage};
use biome_rowan::{SyntaxToken, TextRange};

declare_rule! {
/// Disallow duplicate custom properties within declaration blocks.
///
/// This rule checks the declaration blocks for duplicate custom properties.
///
/// ## Examples
///
/// ### Invalid
///
/// ```css,expect_diagnostic
/// a { --custom-property: pink; --custom-property: orange; }
/// ```
///
/// ```css,expect_diagnostic
/// a { --custom-property: pink; background: orange; --custom-property: orange }
/// ```
///
/// ### Valid
///
/// ```css
/// a { --custom-property: pink; }
/// ```
///
/// ```css
/// a { --custom-property: pink; --cUstOm-prOpErtY: orange; }
/// ```
///
pub NoDuplicateCustomProperties {
version: "next",
name: "noDuplicateCustomProperties",
recommended: false,
}
}

impl Rule for NoDuplicateCustomProperties {
type Query = Ast<CssDeclarationOrRuleList>;
type State = TextRange;
type Signals = Option<Self::State>;
type Options = ();

fn run(ctx: &RuleContext<Self>) -> Option<Self::State> {
let node = ctx.query();
let mut custom_properties: Vec<SyntaxToken<CssLanguage>> = vec![];
for item in node {
if let Some(css_declaration) = item.as_css_declaration_with_semicolon() {
if let Ok(property) = css_declaration.declaration().ok()?.property() {
if let Some(property) = property.as_css_generic_property() {
if let Some(ident) = property.name().ok()?.as_css_identifier() {
let value_token = ident.value_token().ok()?;
custom_properties.push(value_token);
}
}
}
}
}
if let Some(duplicate) = check_duplicate_custom_properties(custom_properties) {
return Some(duplicate.text_trimmed_range());
}
None
}

fn diagnostic(_: &RuleContext<Self>, range: &Self::State) -> Option<RuleDiagnostic> {
Some(
RuleDiagnostic::new(
rule_category!(),
range,
markup! {
"Duplicate custom properties are not allowed."
},
)
.note(markup! {
"Consider removing the duplicate custom property."
}),
)
}
}

fn check_duplicate_custom_properties(
custom_properties: Vec<SyntaxToken<CssLanguage>>,
) -> Option<SyntaxToken<CssLanguage>> {
let mut seen = std::collections::HashSet::<&str>::new();
for value in custom_properties.iter() {
let trimmed_text = value.text_trimmed();
if !seen.insert(trimmed_text) {
return Some(value.clone());
}
}
None
}
1 change: 1 addition & 0 deletions crates/biome_css_analyze/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub type NoColorInvalidHex =
pub type NoCssEmptyBlock =
<lint::nursery::no_css_empty_block::NoCssEmptyBlock as biome_analyze::Rule>::Options;
pub type NoDuplicateAtImportRules = < lint :: nursery :: no_duplicate_at_import_rules :: NoDuplicateAtImportRules as biome_analyze :: Rule > :: Options ;
pub type NoDuplicateCustomProperties = < lint :: nursery :: no_duplicate_custom_properties :: NoDuplicateCustomProperties as biome_analyze :: Rule > :: Options ;
pub type NoDuplicateFontNames =
<lint::nursery::no_duplicate_font_names::NoDuplicateFontNames as biome_analyze::Rule>::Options;
pub type NoDuplicateSelectorsKeyframeBlock = < lint :: nursery :: no_duplicate_selectors_keyframe_block :: NoDuplicateSelectorsKeyframeBlock as biome_analyze :: Rule > :: Options ;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
a { --custom-property: pink; --custom-property: orange; }

a { --custom-property: pink; background: orange; --custom-property: orange }
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
source: crates/biome_css_analyze/tests/spec_tests.rs
expression: invalid.css
---
# Input
```css
a { --custom-property: pink; --custom-property: orange; }
a { --custom-property: pink; background: orange; --custom-property: orange }
```

# Diagnostics
```
invalid.css:1:30 lint/nursery/noDuplicateCustomProperties ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! Duplicate custom properties are not allowed.
> 1 │ a { --custom-property: pink; --custom-property: orange; }
│ ^^^^^^^^^^^^^^^^^
2 │
3 │ a { --custom-property: pink; background: orange; --custom-property: orange }
i Consider removing the duplicate custom property.
```

```
invalid.css:3:50 lint/nursery/noDuplicateCustomProperties ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! Duplicate custom properties are not allowed.
1 │ a { --custom-property: pink; --custom-property: orange; }
2 │
> 3 │ a { --custom-property: pink; background: orange; --custom-property: orange }
│ ^^^^^^^^^^^^^^^^^
4 │
i Consider removing the duplicate custom property.
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/* should not generate diagnostics */
a { --custom-property: pink; }

a { --custom-property: pink; --cUstOm-prOpErtY: orange; }
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
source: crates/biome_css_analyze/tests/spec_tests.rs
expression: valid.css
---
# Input
```css
/* should not generate diagnostics */
a { --custom-property: pink; }
a { --custom-property: pink; --cUstOm-prOpErtY: orange; }
```
1 change: 1 addition & 0 deletions crates/biome_diagnostics_categories/src/categories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ define_categories! {
"lint/nursery/noCssEmptyBlock": "https://biomejs.dev/linter/rules/no-css-empty-block",
"lint/nursery/noDoneCallback": "https://biomejs.dev/linter/rules/no-done-callback",
"lint/nursery/noDuplicateAtImportRules": "https://biomejs.dev/linter/rules/no-duplicate-at-import-rules",
"lint/nursery/noDuplicateCustomProperties": "https://biomejs.dev/linter/rules/no-duplicate-custom-properties",
"lint/nursery/noDuplicateElseIf": "https://biomejs.dev/linter/rules/no-duplicate-else-if",
"lint/nursery/noDuplicateFontNames": "https://biomejs.dev/linter/rules/no-font-family-duplicate-names",
"lint/nursery/noDuplicateJsonKeys": "https://biomejs.dev/linter/rules/no-duplicate-json-keys",
Expand Down
5 changes: 5 additions & 0 deletions packages/@biomejs/backend-jsonrpc/src/workspace.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions packages/@biomejs/biome/configuration_schema.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit eebb582

Please sign in to comment.