From 2e81a05d2b0e946dca95f2f99bda5dc191748938 Mon Sep 17 00:00:00 2001 From: Timon Jurschitsch Date: Tue, 30 Apr 2024 13:40:11 +0200 Subject: [PATCH 1/4] feat(css): implement noDuplicateAtImportRules --- .../biome_configuration/src/linter/rules.rs | 109 +++++++++------- crates/biome_css_analyze/src/lint/nursery.rs | 2 + .../nursery/no_duplicate_at_import_rules.rs | 122 ++++++++++++++++++ crates/biome_css_analyze/src/options.rs | 1 + .../noDuplicateAtImportRules/invalid.css | 3 + .../noDuplicateAtImportRules/invalid.css.snap | 28 ++++ .../noDuplicateAtImportRules/valid.css | 9 ++ .../noDuplicateAtImportRules/valid.css.snap | 17 +++ .../src/categories.rs | 7 +- .../@biomejs/backend-jsonrpc/src/workspace.ts | 11 +- .../@biomejs/biome/configuration_schema.json | 7 + 11 files changed, 265 insertions(+), 51 deletions(-) create mode 100644 crates/biome_css_analyze/src/lint/nursery/no_duplicate_at_import_rules.rs create mode 100644 crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalid.css create mode 100644 crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalid.css.snap create mode 100644 crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/valid.css create mode 100644 crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/valid.css.snap diff --git a/crates/biome_configuration/src/linter/rules.rs b/crates/biome_configuration/src/linter/rules.rs index d89f9fe318c4..3e8e265d5b18 100644 --- a/crates/biome_configuration/src/linter/rules.rs +++ b/crates/biome_configuration/src/linter/rules.rs @@ -2662,6 +2662,9 @@ pub struct Nursery { #[doc = "Disallow using a callback in asynchronous tests and hooks."] #[serde(skip_serializing_if = "Option::is_none")] pub no_done_callback: Option>, + #[doc = "Succinct description of the rule."] + #[serde(skip_serializing_if = "Option::is_none")] + pub no_duplicate_at_import_rules: Option>, #[doc = "Disallow duplicate conditions in if-else-if chains"] #[serde(skip_serializing_if = "Option::is_none")] pub no_duplicate_else_if: Option>, @@ -2750,6 +2753,7 @@ impl Nursery { "noConstantMathMinMaxClamp", "noCssEmptyBlock", "noDoneCallback", + "noDuplicateAtImportRules", "noDuplicateElseIf", "noDuplicateFontNames", "noDuplicateJsonKeys", @@ -2789,16 +2793,16 @@ impl Nursery { const RECOMMENDED_RULES_AS_FILTERS: &'static [RuleFilter<'static>] = &[ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[3]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[4]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[5]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[6]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[7]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[8]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[9]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[10]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24]), ]; const ALL_RULES_AS_FILTERS: &'static [RuleFilter<'static>] = &[ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[0]), @@ -2827,6 +2831,7 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26]), ]; #[doc = r" Retrieves the recommended rules"] pub(crate) fn is_recommended_true(&self) -> bool { @@ -2868,111 +2873,116 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[4])); } } - if let Some(rule) = self.no_duplicate_else_if.as_ref() { + if let Some(rule) = self.no_duplicate_at_import_rules.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[5])); } } - if let Some(rule) = self.no_duplicate_font_names.as_ref() { + if let Some(rule) = self.no_duplicate_else_if.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[6])); } } - if let Some(rule) = self.no_duplicate_json_keys.as_ref() { + if let Some(rule) = self.no_duplicate_font_names.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[7])); } } - if let Some(rule) = self.no_duplicate_selectors_keyframe_block.as_ref() { + if let Some(rule) = self.no_duplicate_json_keys.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[8])); } } - if let Some(rule) = self.no_evolving_any.as_ref() { + if let Some(rule) = self.no_duplicate_selectors_keyframe_block.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[9])); } } - if let Some(rule) = self.no_flat_map_identity.as_ref() { + if let Some(rule) = self.no_evolving_any.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[10])); } } - if let Some(rule) = self.no_important_in_keyframe.as_ref() { + if let Some(rule) = self.no_flat_map_identity.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11])); } } - if let Some(rule) = self.no_misplaced_assertion.as_ref() { + if let Some(rule) = self.no_important_in_keyframe.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12])); } } - if let Some(rule) = self.no_nodejs_modules.as_ref() { + if let Some(rule) = self.no_misplaced_assertion.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13])); } } - if let Some(rule) = self.no_react_specific_props.as_ref() { + if let Some(rule) = self.no_nodejs_modules.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14])); } } - if let Some(rule) = self.no_restricted_imports.as_ref() { + if let Some(rule) = self.no_react_specific_props.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15])); } } - if let Some(rule) = self.no_undeclared_dependencies.as_ref() { + if let Some(rule) = self.no_restricted_imports.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16])); } } - if let Some(rule) = self.no_unknown_function.as_ref() { + if let Some(rule) = self.no_undeclared_dependencies.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17])); } } - if let Some(rule) = self.no_unknown_unit.as_ref() { + if let Some(rule) = self.no_unknown_function.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18])); } } - if let Some(rule) = self.no_useless_undefined_initialization.as_ref() { + if let Some(rule) = self.no_unknown_unit.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19])); } } - if let Some(rule) = self.use_array_literals.as_ref() { + if let Some(rule) = self.no_useless_undefined_initialization.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20])); } } - if let Some(rule) = self.use_consistent_new_builtin.as_ref() { + if let Some(rule) = self.use_array_literals.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[21])); } } - if let Some(rule) = self.use_default_switch_clause.as_ref() { + if let Some(rule) = self.use_consistent_new_builtin.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22])); } } - if let Some(rule) = self.use_generic_font_names.as_ref() { + if let Some(rule) = self.use_default_switch_clause.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23])); } } - if let Some(rule) = self.use_import_restrictions.as_ref() { + if let Some(rule) = self.use_generic_font_names.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24])); } } - if let Some(rule) = self.use_sorted_classes.as_ref() { + if let Some(rule) = self.use_import_restrictions.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25])); } } + if let Some(rule) = self.use_sorted_classes.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26])); + } + } index_set } pub(crate) fn get_disabled_rules(&self) -> IndexSet { @@ -3002,111 +3012,116 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[4])); } } - if let Some(rule) = self.no_duplicate_else_if.as_ref() { + if let Some(rule) = self.no_duplicate_at_import_rules.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[5])); } } - if let Some(rule) = self.no_duplicate_font_names.as_ref() { + if let Some(rule) = self.no_duplicate_else_if.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[6])); } } - if let Some(rule) = self.no_duplicate_json_keys.as_ref() { + if let Some(rule) = self.no_duplicate_font_names.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[7])); } } - if let Some(rule) = self.no_duplicate_selectors_keyframe_block.as_ref() { + if let Some(rule) = self.no_duplicate_json_keys.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[8])); } } - if let Some(rule) = self.no_evolving_any.as_ref() { + if let Some(rule) = self.no_duplicate_selectors_keyframe_block.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[9])); } } - if let Some(rule) = self.no_flat_map_identity.as_ref() { + if let Some(rule) = self.no_evolving_any.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[10])); } } - if let Some(rule) = self.no_important_in_keyframe.as_ref() { + if let Some(rule) = self.no_flat_map_identity.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11])); } } - if let Some(rule) = self.no_misplaced_assertion.as_ref() { + if let Some(rule) = self.no_important_in_keyframe.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12])); } } - if let Some(rule) = self.no_nodejs_modules.as_ref() { + if let Some(rule) = self.no_misplaced_assertion.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13])); } } - if let Some(rule) = self.no_react_specific_props.as_ref() { + if let Some(rule) = self.no_nodejs_modules.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14])); } } - if let Some(rule) = self.no_restricted_imports.as_ref() { + if let Some(rule) = self.no_react_specific_props.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15])); } } - if let Some(rule) = self.no_undeclared_dependencies.as_ref() { + if let Some(rule) = self.no_restricted_imports.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16])); } } - if let Some(rule) = self.no_unknown_function.as_ref() { + if let Some(rule) = self.no_undeclared_dependencies.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17])); } } - if let Some(rule) = self.no_unknown_unit.as_ref() { + if let Some(rule) = self.no_unknown_function.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18])); } } - if let Some(rule) = self.no_useless_undefined_initialization.as_ref() { + if let Some(rule) = self.no_unknown_unit.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19])); } } - if let Some(rule) = self.use_array_literals.as_ref() { + if let Some(rule) = self.no_useless_undefined_initialization.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20])); } } - if let Some(rule) = self.use_consistent_new_builtin.as_ref() { + if let Some(rule) = self.use_array_literals.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[21])); } } - if let Some(rule) = self.use_default_switch_clause.as_ref() { + if let Some(rule) = self.use_consistent_new_builtin.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22])); } } - if let Some(rule) = self.use_generic_font_names.as_ref() { + if let Some(rule) = self.use_default_switch_clause.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23])); } } - if let Some(rule) = self.use_import_restrictions.as_ref() { + if let Some(rule) = self.use_generic_font_names.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24])); } } - if let Some(rule) = self.use_sorted_classes.as_ref() { + if let Some(rule) = self.use_import_restrictions.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25])); } } + if let Some(rule) = self.use_sorted_classes.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26])); + } + } index_set } #[doc = r" Checks if, given a rule name, matches one of the rules contained in this category"] @@ -3163,6 +3178,10 @@ impl Nursery { .no_done_callback .as_ref() .map(|conf| (conf.level(), conf.get_options())), + "noDuplicateAtImportRules" => self + .no_duplicate_at_import_rules + .as_ref() + .map(|conf| (conf.level(), conf.get_options())), "noDuplicateElseIf" => self .no_duplicate_else_if .as_ref() diff --git a/crates/biome_css_analyze/src/lint/nursery.rs b/crates/biome_css_analyze/src/lint/nursery.rs index 8e1e51340d73..5455fea8eba7 100644 --- a/crates/biome_css_analyze/src/lint/nursery.rs +++ b/crates/biome_css_analyze/src/lint/nursery.rs @@ -4,6 +4,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_font_names; pub mod no_duplicate_selectors_keyframe_block; pub mod no_important_in_keyframe; @@ -17,6 +18,7 @@ declare_group! { rules : [ self :: no_color_invalid_hex :: NoColorInvalidHex , self :: no_css_empty_block :: NoCssEmptyBlock , + self :: no_duplicate_at_import_rules :: NoDuplicateAtImportRules , self :: no_duplicate_font_names :: NoDuplicateFontNames , self :: no_duplicate_selectors_keyframe_block :: NoDuplicateSelectorsKeyframeBlock , self :: no_important_in_keyframe :: NoImportantInKeyframe , diff --git a/crates/biome_css_analyze/src/lint/nursery/no_duplicate_at_import_rules.rs b/crates/biome_css_analyze/src/lint/nursery/no_duplicate_at_import_rules.rs new file mode 100644 index 000000000000..f32188c69730 --- /dev/null +++ b/crates/biome_css_analyze/src/lint/nursery/no_duplicate_at_import_rules.rs @@ -0,0 +1,122 @@ +use std::collections::{HashMap, HashSet}; + +use biome_analyze::{context::RuleContext, declare_rule, Ast, Rule, RuleDiagnostic, RuleSource}; +use biome_console::markup; +use biome_css_syntax::{AnyCssAtRule, AnyCssRule, CssImportAtRule, CssRuleList}; +use biome_rowan::AstNode; + +declare_rule! { + /// Disallow duplicate `@import` rules. + /// + /// This rule checks if the file urls of the @import rules are duplicates. + /// + /// This rule also checks the imported media queries and alerts of duplicates. + /// + /// ## Examples + /// + /// ### Invalid + /// + /// ```css,expect_diagnostic + /// @import 'a.css'; + /// @import 'a.css'; + /// ``` + /// + /// ```css,expect_diagnostic + /// @import "a.css"; + /// @import 'a.css'; + /// ``` + /// + /// ```css,expect_diagnostic + /// @import url('a.css'); + /// @import url('a.css'); + /// ``` + /// + /// ### Valid + /// + /// ```css + /// @import 'a.css'; + /// @import 'b.css'; + /// ``` + /// + /// ```css + /// @import url('a.css') tv; + /// @import url('a.css') projection; + /// ``` + /// + pub NoDuplicateAtImportRules { + version: "next", + name: "noDuplicateAtImportRules", + recommended: false, + sources: &[RuleSource::Stylelint("no-duplicate-at-import-rules")], + } +} + +impl Rule for NoDuplicateAtImportRules { + type Query = Ast; + type State = CssImportAtRule; + type Signals = Option; + type Options = (); + + fn run(ctx: &RuleContext) -> Option { + let node = ctx.query(); + // let mut import_url_list = HashSet::new(); + let mut import_url_map: HashMap> = HashMap::new(); + for rule in node { + match rule { + AnyCssRule::CssAtRule(item) => match item.rule().ok()? { + AnyCssAtRule::CssImportAtRule(import_rule) => { + let import_url = import_rule.url().ok()?.text().to_lowercase(); + if let Some(media_query_set) = import_url_map.get_mut(&import_url) { + // if the current import_rule has no media queries or there are no queries safed in the + // media_query_set, this is always a duplicate + if import_rule.media().text().is_empty() || media_query_set.is_empty() { + return Some(import_rule); + } + + for media in import_rule.media() { + match media { + Ok(media) => { + if !media_query_set.insert(media.text().to_lowercase()) { + return Some(import_rule); + } + } + _ => return None, + } + } + } else { + let mut media_set: HashSet = HashSet::new(); + for media in import_rule.media() { + match media { + Ok(media) => { + media_set.insert(media.text().to_lowercase()); + } + _ => return None, + } + } + import_url_map.insert(import_url, media_set); + } + } + _ => return None, + }, + _ => return None, + } + } + None + } + + fn diagnostic(_: &RuleContext, node: &Self::State) -> Option { + let span = node.range(); + Some( + RuleDiagnostic::new( + rule_category!(), + span, + markup! { + "Duplicate @import rule." + }, + ) + .note(markup! { + "Looks like you imported the same file twice. Consider removing one to get rid of this error!" + }), + ) + } +} diff --git a/crates/biome_css_analyze/src/options.rs b/crates/biome_css_analyze/src/options.rs index a56a05c517f2..cacfd2fec994 100644 --- a/crates/biome_css_analyze/src/options.rs +++ b/crates/biome_css_analyze/src/options.rs @@ -6,6 +6,7 @@ pub type NoColorInvalidHex = ::Options; pub type NoCssEmptyBlock = ::Options; +pub type NoDuplicateAtImportRules = < lint :: nursery :: no_duplicate_at_import_rules :: NoDuplicateAtImportRules as biome_analyze :: Rule > :: Options ; pub type NoDuplicateFontNames = ::Options; pub type NoDuplicateSelectorsKeyframeBlock = < lint :: nursery :: no_duplicate_selectors_keyframe_block :: NoDuplicateSelectorsKeyframeBlock as biome_analyze :: Rule > :: Options ; diff --git a/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalid.css b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalid.css new file mode 100644 index 000000000000..73943ae070b0 --- /dev/null +++ b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalid.css @@ -0,0 +1,3 @@ +@import "a.css"; +@import "b.css"; +@import "a.css"; diff --git a/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalid.css.snap b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalid.css.snap new file mode 100644 index 000000000000..436b142367f1 --- /dev/null +++ b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalid.css.snap @@ -0,0 +1,28 @@ +--- +source: crates/biome_css_analyze/tests/spec_tests.rs +expression: invalid.css +--- +# Input +```css +@import "a.css"; +@import "b.css"; +@import "a.css"; + +``` + +# Diagnostics +``` +invalid.css:3:2 lint/nursery/noDuplicateAtImportRules ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Duplicate @import rule. + + 1 │ @import "a.css"; + 2 │ @import "b.css"; + > 3 │ @import "a.css"; + │ ^^^^^^^^^^^^^^^ + 4 │ + + i Looks like you imported the same file twice. Consider removing one to get rid of this error! + + +``` diff --git a/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/valid.css b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/valid.css new file mode 100644 index 000000000000..c1b89db6e4c9 --- /dev/null +++ b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/valid.css @@ -0,0 +1,9 @@ +/* should not generate diagnostics */ +@import "a.css"; +@import "b.css"; + +@import url("c.css"); +@import url("d.css"); + +@import url("e.css") tv; +@import url("e.css") projection; diff --git a/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/valid.css.snap b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/valid.css.snap new file mode 100644 index 000000000000..46ad9f4898c5 --- /dev/null +++ b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/valid.css.snap @@ -0,0 +1,17 @@ +--- +source: crates/biome_css_analyze/tests/spec_tests.rs +expression: valid.css +--- +# Input +```css +/* should not generate diagnostics */ +@import "a.css"; +@import "b.css"; + +@import url("c.css"); +@import url("d.css"); + +@import url("e.css") tv; +@import url("e.css") projection; + +``` diff --git a/crates/biome_diagnostics_categories/src/categories.rs b/crates/biome_diagnostics_categories/src/categories.rs index 0c517cef9c81..cf54df80db91 100644 --- a/crates/biome_diagnostics_categories/src/categories.rs +++ b/crates/biome_diagnostics_categories/src/categories.rs @@ -110,12 +110,12 @@ define_categories! { "lint/correctness/useValidForDirection": "https://biomejs.dev/linter/rules/use-valid-for-direction", "lint/correctness/useYield": "https://biomejs.dev/linter/rules/use-yield", "lint/nursery/colorNoInvalidHex": "https://biomejs.dev/linter/rules/color-no-invalid-hex", - "lint/nursery/useArrayLiterals": "https://biomejs.dev/linter/rules/use-array-literals", "lint/nursery/noColorInvalidHex": "https://biomejs.dev/linter/rules/no-color-invalid-hex", "lint/nursery/noConsole": "https://biomejs.dev/linter/rules/no-console", "lint/nursery/noConstantMathMinMaxClamp": "https://biomejs.dev/linter/rules/no-constant-math-min-max-clamp", "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/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", @@ -131,12 +131,13 @@ define_categories! { "lint/nursery/noTypeOnlyImportAttributes": "https://biomejs.dev/linter/rules/no-type-only-import-attributes", "lint/nursery/noUndeclaredDependencies": "https://biomejs.dev/linter/rules/no-undeclared-dependencies", "lint/nursery/noUnknownFunction": "https://biomejs.dev/linter/rules/no-unknown-function", - "lint/nursery/noUselessUndefinedInitialization": "https://biomejs.dev/linter/rules/no-useless-undefined-initialization", "lint/nursery/noUnknownUnit": "https://biomejs.dev/linter/rules/no-unknown-unit", + "lint/nursery/noUselessUndefinedInitialization": "https://biomejs.dev/linter/rules/no-useless-undefined-initialization", + "lint/nursery/useArrayLiterals": "https://biomejs.dev/linter/rules/use-array-literals", "lint/nursery/useBiomeSuppressionComment": "https://biomejs.dev/linter/rules/use-biome-suppression-comment", "lint/nursery/useConsistentNewBuiltin": "https://biomejs.dev/linter/rules/use-consistent-new-builtin", - "lint/nursery/useGenericFontNames": "https://biomejs.dev/linter/rules/use-generic-font-names", "lint/nursery/useDefaultSwitchClause": "https://biomejs.dev/linter/rules/use-default-switch-clause", + "lint/nursery/useGenericFontNames": "https://biomejs.dev/linter/rules/use-generic-font-names", "lint/nursery/useImportRestrictions": "https://biomejs.dev/linter/rules/use-import-restrictions", "lint/nursery/useSortedClasses": "https://biomejs.dev/linter/rules/use-sorted-classes", "lint/performance/noAccumulatingSpread": "https://biomejs.dev/linter/rules/no-accumulating-spread", diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index b02d3ed64249..5c6a12831d10 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -928,6 +928,10 @@ export interface Nursery { * Disallow using a callback in asynchronous tests and hooks. */ noDoneCallback?: RuleConfiguration_for_Null; + /** + * Succinct description of the rule. + */ + noDuplicateAtImportRules?: RuleConfiguration_for_Null; /** * Disallow duplicate conditions in if-else-if chains */ @@ -1981,12 +1985,12 @@ export type Category = | "lint/correctness/useValidForDirection" | "lint/correctness/useYield" | "lint/nursery/colorNoInvalidHex" - | "lint/nursery/useArrayLiterals" | "lint/nursery/noColorInvalidHex" | "lint/nursery/noConsole" | "lint/nursery/noConstantMathMinMaxClamp" | "lint/nursery/noCssEmptyBlock" | "lint/nursery/noDoneCallback" + | "lint/nursery/noDuplicateAtImportRules" | "lint/nursery/noDuplicateElseIf" | "lint/nursery/noDuplicateFontNames" | "lint/nursery/noDuplicateJsonKeys" @@ -2002,12 +2006,13 @@ export type Category = | "lint/nursery/noTypeOnlyImportAttributes" | "lint/nursery/noUndeclaredDependencies" | "lint/nursery/noUnknownFunction" - | "lint/nursery/noUselessUndefinedInitialization" | "lint/nursery/noUnknownUnit" + | "lint/nursery/noUselessUndefinedInitialization" + | "lint/nursery/useArrayLiterals" | "lint/nursery/useBiomeSuppressionComment" | "lint/nursery/useConsistentNewBuiltin" - | "lint/nursery/useGenericFontNames" | "lint/nursery/useDefaultSwitchClause" + | "lint/nursery/useGenericFontNames" | "lint/nursery/useImportRestrictions" | "lint/nursery/useSortedClasses" | "lint/performance/noAccumulatingSpread" diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index fb7bc6b7b6b3..3e0bf1aca554 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -1468,6 +1468,13 @@ { "type": "null" } ] }, + "noDuplicateAtImportRules": { + "description": "Succinct description of the rule.", + "anyOf": [ + { "$ref": "#/definitions/RuleConfiguration" }, + { "type": "null" } + ] + }, "noDuplicateElseIf": { "description": "Disallow duplicate conditions in if-else-if chains", "anyOf": [ From cdfcb12362c25fa0d1d3325c1dff617093895355 Mon Sep 17 00:00:00 2001 From: Timon Jurschitsch Date: Tue, 30 Apr 2024 13:50:07 +0200 Subject: [PATCH 2/4] changes after just ready --- crates/biome_configuration/src/linter/rules.rs | 2 +- packages/@biomejs/backend-jsonrpc/src/workspace.ts | 2 +- packages/@biomejs/biome/configuration_schema.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/biome_configuration/src/linter/rules.rs b/crates/biome_configuration/src/linter/rules.rs index 3e8e265d5b18..79e1eaa9922c 100644 --- a/crates/biome_configuration/src/linter/rules.rs +++ b/crates/biome_configuration/src/linter/rules.rs @@ -2662,7 +2662,7 @@ pub struct Nursery { #[doc = "Disallow using a callback in asynchronous tests and hooks."] #[serde(skip_serializing_if = "Option::is_none")] pub no_done_callback: Option>, - #[doc = "Succinct description of the rule."] + #[doc = "Disallow duplicate @import rules."] #[serde(skip_serializing_if = "Option::is_none")] pub no_duplicate_at_import_rules: Option>, #[doc = "Disallow duplicate conditions in if-else-if chains"] diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index 5c6a12831d10..de07221da890 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -929,7 +929,7 @@ export interface Nursery { */ noDoneCallback?: RuleConfiguration_for_Null; /** - * Succinct description of the rule. + * Disallow duplicate @import rules. */ noDuplicateAtImportRules?: RuleConfiguration_for_Null; /** diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index 3e0bf1aca554..b5fe7aa00e87 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -1469,7 +1469,7 @@ ] }, "noDuplicateAtImportRules": { - "description": "Succinct description of the rule.", + "description": "Disallow duplicate @import rules.", "anyOf": [ { "$ref": "#/definitions/RuleConfiguration" }, { "type": "null" } From f19c365348b16ea9dee19d40f166a35831e5bc04 Mon Sep 17 00:00:00 2001 From: Timon Jurschitsch Date: Tue, 30 Apr 2024 17:48:36 +0200 Subject: [PATCH 3/4] implement suggested changes, add more tests, fix small bug --- .../biome_configuration/src/linter/rules.rs | 2 ++ .../nursery/no_duplicate_at_import_rules.rs | 15 ++++++---- .../noDuplicateAtImportRules/invalid.css.snap | 2 +- .../noDuplicateAtImportRules/invalidMedia.css | 3 ++ .../invalidMedia.css.snap | 28 +++++++++++++++++++ .../invalidMultipleMedia.css | 3 ++ .../invalidMultipleMedia.css.snap | 28 +++++++++++++++++++ .../noDuplicateAtImportRules/invalidUrls.css | 2 ++ .../invalidUrls.css.snap | 26 +++++++++++++++++ 9 files changed, 103 insertions(+), 6 deletions(-) create mode 100644 crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidMedia.css create mode 100644 crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidMedia.css.snap create mode 100644 crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidMultipleMedia.css create mode 100644 crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidMultipleMedia.css.snap create mode 100644 crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidUrls.css create mode 100644 crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidUrls.css.snap diff --git a/crates/biome_configuration/src/linter/rules.rs b/crates/biome_configuration/src/linter/rules.rs index 79e1eaa9922c..21ec698eb31b 100644 --- a/crates/biome_configuration/src/linter/rules.rs +++ b/crates/biome_configuration/src/linter/rules.rs @@ -2779,6 +2779,7 @@ impl Nursery { const RECOMMENDED_RULES: &'static [&'static str] = &[ "noCssEmptyBlock", "noDoneCallback", + "noDuplicateAtImportRules", "noDuplicateElseIf", "noDuplicateFontNames", "noDuplicateJsonKeys", @@ -2793,6 +2794,7 @@ impl Nursery { const RECOMMENDED_RULES_AS_FILTERS: &'static [RuleFilter<'static>] = &[ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[3]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[4]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[5]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[6]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[7]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[8]), diff --git a/crates/biome_css_analyze/src/lint/nursery/no_duplicate_at_import_rules.rs b/crates/biome_css_analyze/src/lint/nursery/no_duplicate_at_import_rules.rs index f32188c69730..c47ec5220385 100644 --- a/crates/biome_css_analyze/src/lint/nursery/no_duplicate_at_import_rules.rs +++ b/crates/biome_css_analyze/src/lint/nursery/no_duplicate_at_import_rules.rs @@ -46,7 +46,7 @@ declare_rule! { pub NoDuplicateAtImportRules { version: "next", name: "noDuplicateAtImportRules", - recommended: false, + recommended: true, sources: &[RuleSource::Stylelint("no-duplicate-at-import-rules")], } } @@ -59,15 +59,20 @@ impl Rule for NoDuplicateAtImportRules { fn run(ctx: &RuleContext) -> Option { let node = ctx.query(); - // let mut import_url_list = HashSet::new(); let mut import_url_map: HashMap> = HashMap::new(); for rule in node { match rule { AnyCssRule::CssAtRule(item) => match item.rule().ok()? { AnyCssAtRule::CssImportAtRule(import_rule) => { - let import_url = import_rule.url().ok()?.text().to_lowercase(); + let import_url = import_rule + .url() + .ok()? + .text() + .to_lowercase() + .replace("url(", "") + .replace(')', ""); if let Some(media_query_set) = import_url_map.get_mut(&import_url) { - // if the current import_rule has no media queries or there are no queries safed in the + // if the current import_rule has no media queries or there are no queries saved in the // media_query_set, this is always a duplicate if import_rule.media().text().is_empty() || media_query_set.is_empty() { return Some(import_rule); @@ -115,7 +120,7 @@ impl Rule for NoDuplicateAtImportRules { }, ) .note(markup! { - "Looks like you imported the same file twice. Consider removing one to get rid of this error!" + "To fix this issue, remove one of the duplicated imports." }), ) } diff --git a/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalid.css.snap b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalid.css.snap index 436b142367f1..5af5c3015ef2 100644 --- a/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalid.css.snap +++ b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalid.css.snap @@ -22,7 +22,7 @@ invalid.css:3:2 lint/nursery/noDuplicateAtImportRules ━━━━━━━━ │ ^^^^^^^^^^^^^^^ 4 │ - i Looks like you imported the same file twice. Consider removing one to get rid of this error! + i To fix this issue, remove one of the duplicated imports. ``` diff --git a/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidMedia.css b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidMedia.css new file mode 100644 index 000000000000..b5df7f354a2a --- /dev/null +++ b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidMedia.css @@ -0,0 +1,3 @@ +@import url("a.css") tv; +@import url("a.css") projection; +@import "a.css"; diff --git a/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidMedia.css.snap b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidMedia.css.snap new file mode 100644 index 000000000000..f1d4416f1ae8 --- /dev/null +++ b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidMedia.css.snap @@ -0,0 +1,28 @@ +--- +source: crates/biome_css_analyze/tests/spec_tests.rs +expression: invalidMedia.css +--- +# Input +```css +@import url("a.css") tv; +@import url("a.css") projection; +@import "a.css"; + +``` + +# Diagnostics +``` +invalidMedia.css:3:2 lint/nursery/noDuplicateAtImportRules ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Duplicate @import rule. + + 1 │ @import url("a.css") tv; + 2 │ @import url("a.css") projection; + > 3 │ @import "a.css"; + │ ^^^^^^^^^^^^^^^ + 4 │ + + i To fix this issue, remove one of the duplicated imports. + + +``` diff --git a/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidMultipleMedia.css b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidMultipleMedia.css new file mode 100644 index 000000000000..83c5d2d721ce --- /dev/null +++ b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidMultipleMedia.css @@ -0,0 +1,3 @@ +@import url("a.css") tv, projection; +@import url("a.css") mobile; +@import url("a.css") tv; diff --git a/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidMultipleMedia.css.snap b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidMultipleMedia.css.snap new file mode 100644 index 000000000000..2adbaec8ba59 --- /dev/null +++ b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidMultipleMedia.css.snap @@ -0,0 +1,28 @@ +--- +source: crates/biome_css_analyze/tests/spec_tests.rs +expression: invalidMultipleMedia.css +--- +# Input +```css +@import url("a.css") tv, projection; +@import url("a.css") mobile; +@import url("a.css") tv; + +``` + +# Diagnostics +``` +invalidMultipleMedia.css:3:2 lint/nursery/noDuplicateAtImportRules ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Duplicate @import rule. + + 1 │ @import url("a.css") tv, projection; + 2 │ @import url("a.css") mobile; + > 3 │ @import url("a.css") tv; + │ ^^^^^^^^^^^^^^^^^^^^^^^ + 4 │ + + i To fix this issue, remove one of the duplicated imports. + + +``` diff --git a/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidUrls.css b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidUrls.css new file mode 100644 index 000000000000..0b00168d4cdf --- /dev/null +++ b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidUrls.css @@ -0,0 +1,2 @@ +@import url("c.css"); +@import url("c.css"); diff --git a/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidUrls.css.snap b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidUrls.css.snap new file mode 100644 index 000000000000..961d906e107d --- /dev/null +++ b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidUrls.css.snap @@ -0,0 +1,26 @@ +--- +source: crates/biome_css_analyze/tests/spec_tests.rs +expression: invalidUrls.css +--- +# Input +```css +@import url("c.css"); +@import url("c.css"); + +``` + +# Diagnostics +``` +invalidUrls.css:2:2 lint/nursery/noDuplicateAtImportRules ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Duplicate @import rule. + + 1 │ @import url("c.css"); + > 2 │ @import url("c.css"); + │ ^^^^^^^^^^^^^^^^^^^^ + 3 │ + + i To fix this issue, remove one of the duplicated imports. + + +``` From ea2f9dc9f075d9872079dd7522cca649e56cc4ba Mon Sep 17 00:00:00 2001 From: Timon Jurschitsch Date: Wed, 1 May 2024 16:32:40 +0200 Subject: [PATCH 4/4] implement suggestions, update snapshots --- .../src/lint/nursery/no_duplicate_at_import_rules.rs | 4 ++-- .../specs/nursery/noDuplicateAtImportRules/invalid.css.snap | 4 ++-- .../nursery/noDuplicateAtImportRules/invalidMedia.css.snap | 4 ++-- .../noDuplicateAtImportRules/invalidMultipleMedia.css.snap | 4 ++-- .../nursery/noDuplicateAtImportRules/invalidUrls.css.snap | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/biome_css_analyze/src/lint/nursery/no_duplicate_at_import_rules.rs b/crates/biome_css_analyze/src/lint/nursery/no_duplicate_at_import_rules.rs index c47ec5220385..6440e77351b7 100644 --- a/crates/biome_css_analyze/src/lint/nursery/no_duplicate_at_import_rules.rs +++ b/crates/biome_css_analyze/src/lint/nursery/no_duplicate_at_import_rules.rs @@ -116,11 +116,11 @@ impl Rule for NoDuplicateAtImportRules { rule_category!(), span, markup! { - "Duplicate @import rule." + "Each ""@import"" should be unique unless differing by media queries." }, ) .note(markup! { - "To fix this issue, remove one of the duplicated imports." + "Consider removing one of the duplicated imports." }), ) } diff --git a/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalid.css.snap b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalid.css.snap index 5af5c3015ef2..dd5d39c3353e 100644 --- a/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalid.css.snap +++ b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalid.css.snap @@ -14,7 +14,7 @@ expression: invalid.css ``` invalid.css:3:2 lint/nursery/noDuplicateAtImportRules ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Duplicate @import rule. + ! Each @import should be unique unless differing by media queries. 1 │ @import "a.css"; 2 │ @import "b.css"; @@ -22,7 +22,7 @@ invalid.css:3:2 lint/nursery/noDuplicateAtImportRules ━━━━━━━━ │ ^^^^^^^^^^^^^^^ 4 │ - i To fix this issue, remove one of the duplicated imports. + i Consider removing one of the duplicated imports. ``` diff --git a/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidMedia.css.snap b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidMedia.css.snap index f1d4416f1ae8..cbb4170fd51d 100644 --- a/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidMedia.css.snap +++ b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidMedia.css.snap @@ -14,7 +14,7 @@ expression: invalidMedia.css ``` invalidMedia.css:3:2 lint/nursery/noDuplicateAtImportRules ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Duplicate @import rule. + ! Each @import should be unique unless differing by media queries. 1 │ @import url("a.css") tv; 2 │ @import url("a.css") projection; @@ -22,7 +22,7 @@ invalidMedia.css:3:2 lint/nursery/noDuplicateAtImportRules ━━━━━━━ │ ^^^^^^^^^^^^^^^ 4 │ - i To fix this issue, remove one of the duplicated imports. + i Consider removing one of the duplicated imports. ``` diff --git a/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidMultipleMedia.css.snap b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidMultipleMedia.css.snap index 2adbaec8ba59..5a95a2b76d96 100644 --- a/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidMultipleMedia.css.snap +++ b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidMultipleMedia.css.snap @@ -14,7 +14,7 @@ expression: invalidMultipleMedia.css ``` invalidMultipleMedia.css:3:2 lint/nursery/noDuplicateAtImportRules ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Duplicate @import rule. + ! Each @import should be unique unless differing by media queries. 1 │ @import url("a.css") tv, projection; 2 │ @import url("a.css") mobile; @@ -22,7 +22,7 @@ invalidMultipleMedia.css:3:2 lint/nursery/noDuplicateAtImportRules ━━━━ │ ^^^^^^^^^^^^^^^^^^^^^^^ 4 │ - i To fix this issue, remove one of the duplicated imports. + i Consider removing one of the duplicated imports. ``` diff --git a/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidUrls.css.snap b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidUrls.css.snap index 961d906e107d..b74f5e25a41b 100644 --- a/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidUrls.css.snap +++ b/crates/biome_css_analyze/tests/specs/nursery/noDuplicateAtImportRules/invalidUrls.css.snap @@ -13,14 +13,14 @@ expression: invalidUrls.css ``` invalidUrls.css:2:2 lint/nursery/noDuplicateAtImportRules ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Duplicate @import rule. + ! Each @import should be unique unless differing by media queries. 1 │ @import url("c.css"); > 2 │ @import url("c.css"); │ ^^^^^^^^^^^^^^^^^^^^ 3 │ - i To fix this issue, remove one of the duplicated imports. + i Consider removing one of the duplicated imports. ```