diff --git a/Cargo.lock b/Cargo.lock index 13f2bc89cd1c..29aca451159b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -295,12 +295,16 @@ dependencies = [ name = "biome_css_parser" version = "0.5.7" dependencies = [ + "biome_configuration", "biome_console", "biome_css_factory", "biome_css_syntax", + "biome_deserialize", "biome_diagnostics", + "biome_fs", "biome_parser", "biome_rowan", + "biome_service", "biome_test_utils", "biome_unicode_table", "insta", diff --git a/crates/biome_configuration/src/css.rs b/crates/biome_configuration/src/css.rs index 60b21897a599..bfb790119e4d 100644 --- a/crates/biome_configuration/src/css.rs +++ b/crates/biome_configuration/src/css.rs @@ -32,6 +32,10 @@ pub struct CssParser { /// Allow comments to appear on incorrect lines in `.css` files #[partial(bpaf(hide))] pub allow_wrong_line_comments: bool, + + /// Enables parsing of CSS Modules specific features. + #[partial(bpaf(hide))] + pub css_modules: bool, } /// Options that changes how the CSS formatter behaves diff --git a/crates/biome_css_factory/src/generated/node_factory.rs b/crates/biome_css_factory/src/generated/node_factory.rs index 3dbb23cad7e0..aa7f8ca05bc0 100644 --- a/crates/biome_css_factory/src/generated/node_factory.rs +++ b/crates/biome_css_factory/src/generated/node_factory.rs @@ -2030,6 +2030,76 @@ pub fn css_url_value_raw(value_token: SyntaxToken) -> CssUrlValueRaw { [Some(SyntaxElement::Token(value_token))], )) } +pub fn css_value_at_rule( + value_token: SyntaxToken, + clause: AnyCssValueAtRuleClause, + semicolon_token: SyntaxToken, +) -> CssValueAtRule { + CssValueAtRule::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::CSS_VALUE_AT_RULE, + [ + Some(SyntaxElement::Token(value_token)), + Some(SyntaxElement::Node(clause.into_syntax())), + Some(SyntaxElement::Token(semicolon_token)), + ], + )) +} +pub fn css_value_at_rule_declaration_clause( + properties: CssValueAtRulePropertyList, +) -> CssValueAtRuleDeclarationClause { + CssValueAtRuleDeclarationClause::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::CSS_VALUE_AT_RULE_DECLARATION_CLAUSE, + [Some(SyntaxElement::Node(properties.into_syntax()))], + )) +} +pub fn css_value_at_rule_generic_property( + name: AnyCssDeclarationName, + colon_token: SyntaxToken, + value: CssValueAtRuleGenericValue, +) -> CssValueAtRuleGenericProperty { + CssValueAtRuleGenericProperty::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::CSS_VALUE_AT_RULE_GENERIC_PROPERTY, + [ + Some(SyntaxElement::Node(name.into_syntax())), + Some(SyntaxElement::Token(colon_token)), + Some(SyntaxElement::Node(value.into_syntax())), + ], + )) +} +pub fn css_value_at_rule_import_clause( + specifiers: CssValueAtRuleImportSpecifierList, + from_token: SyntaxToken, + source: AnyCssValueAtRuleImportSource, +) -> CssValueAtRuleImportClause { + CssValueAtRuleImportClause::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::CSS_VALUE_AT_RULE_IMPORT_CLAUSE, + [ + Some(SyntaxElement::Node(specifiers.into_syntax())), + Some(SyntaxElement::Token(from_token)), + Some(SyntaxElement::Node(source.into_syntax())), + ], + )) +} +pub fn css_value_at_rule_import_specifier(name: CssIdentifier) -> CssValueAtRuleImportSpecifier { + CssValueAtRuleImportSpecifier::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::CSS_VALUE_AT_RULE_IMPORT_SPECIFIER, + [Some(SyntaxElement::Node(name.into_syntax()))], + )) +} +pub fn css_value_at_rule_named_import_specifier( + name: CssIdentifier, + as_token: SyntaxToken, + local_name: CssIdentifier, +) -> CssValueAtRuleNamedImportSpecifier { + CssValueAtRuleNamedImportSpecifier::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::CSS_VALUE_AT_RULE_NAMED_IMPORT_SPECIFIER, + [ + Some(SyntaxElement::Node(name.into_syntax())), + Some(SyntaxElement::Token(as_token)), + Some(SyntaxElement::Node(local_name.into_syntax())), + ], + )) +} pub fn css_component_value_list(items: I) -> CssComponentValueList where I: IntoIterator, @@ -2438,6 +2508,51 @@ where .map(|item| Some(item.into_syntax().into())), )) } +pub fn css_value_at_rule_import_specifier_list( + items: I, + separators: S, +) -> CssValueAtRuleImportSpecifierList +where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, + S: IntoIterator, + S::IntoIter: ExactSizeIterator, +{ + let mut items = items.into_iter(); + let mut separators = separators.into_iter(); + let length = items.len() + separators.len(); + CssValueAtRuleImportSpecifierList::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::CSS_VALUE_AT_RULE_IMPORT_SPECIFIER_LIST, + (0..length).map(|index| { + if index % 2 == 0 { + Some(items.next()?.into_syntax().into()) + } else { + Some(separators.next()?.into()) + } + }), + )) +} +pub fn css_value_at_rule_property_list(items: I, separators: S) -> CssValueAtRulePropertyList +where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, + S: IntoIterator, + S::IntoIter: ExactSizeIterator, +{ + let mut items = items.into_iter(); + let mut separators = separators.into_iter(); + let length = items.len() + separators.len(); + CssValueAtRulePropertyList::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::CSS_VALUE_AT_RULE_PROPERTY_LIST, + (0..length).map(|index| { + if index % 2 == 0 { + Some(items.next()?.into_syntax().into()) + } else { + Some(separators.next()?.into()) + } + }), + )) +} pub fn css_bogus(slots: I) -> CssBogus where I: IntoIterator>, @@ -2655,3 +2770,13 @@ where slots, )) } +pub fn css_value_at_rule_generic_value(slots: I) -> CssValueAtRuleGenericValue +where + I: IntoIterator>, + I::IntoIter: ExactSizeIterator, +{ + CssValueAtRuleGenericValue::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::CSS_VALUE_AT_RULE_GENERIC_VALUE, + slots, + )) +} diff --git a/crates/biome_css_factory/src/generated/syntax_factory.rs b/crates/biome_css_factory/src/generated/syntax_factory.rs index 70c670a5315c..bbea4d978952 100644 --- a/crates/biome_css_factory/src/generated/syntax_factory.rs +++ b/crates/biome_css_factory/src/generated/syntax_factory.rs @@ -35,7 +35,10 @@ impl SyntaxFactory for CssSyntaxFactory { | CSS_BOGUS_SCOPE_RANGE | CSS_BOGUS_SELECTOR | CSS_BOGUS_SUB_SELECTOR - | CSS_BOGUS_URL_MODIFIER => RawSyntaxNode::new(kind, children.into_iter().map(Some)), + | CSS_BOGUS_URL_MODIFIER + | CSS_VALUE_AT_RULE_GENERIC_VALUE => { + RawSyntaxNode::new(kind, children.into_iter().map(Some)) + } CSS_AT_RULE => { let mut elements = (&children).into_iter(); let mut slots: RawNodeSlots<2usize> = RawNodeSlots::default(); @@ -4099,6 +4102,176 @@ impl SyntaxFactory for CssSyntaxFactory { } slots.into_node(CSS_URL_VALUE_RAW, children) } + CSS_VALUE_AT_RULE => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<3usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if element.kind() == T![value] { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if let Some(element) = ¤t_element { + if AnyCssValueAtRuleClause::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if let Some(element) = ¤t_element { + if element.kind() == T ! [;] { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + CSS_VALUE_AT_RULE.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(CSS_VALUE_AT_RULE, children) + } + CSS_VALUE_AT_RULE_DECLARATION_CLAUSE => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if CssValueAtRulePropertyList::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + CSS_VALUE_AT_RULE_DECLARATION_CLAUSE.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(CSS_VALUE_AT_RULE_DECLARATION_CLAUSE, children) + } + CSS_VALUE_AT_RULE_GENERIC_PROPERTY => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<3usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if AnyCssDeclarationName::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if let Some(element) = ¤t_element { + if element.kind() == T ! [:] { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if let Some(element) = ¤t_element { + if CssValueAtRuleGenericValue::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + CSS_VALUE_AT_RULE_GENERIC_PROPERTY.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(CSS_VALUE_AT_RULE_GENERIC_PROPERTY, children) + } + CSS_VALUE_AT_RULE_IMPORT_CLAUSE => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<3usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if CssValueAtRuleImportSpecifierList::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if let Some(element) = ¤t_element { + if element.kind() == T![from] { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if let Some(element) = ¤t_element { + if AnyCssValueAtRuleImportSource::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + CSS_VALUE_AT_RULE_IMPORT_CLAUSE.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(CSS_VALUE_AT_RULE_IMPORT_CLAUSE, children) + } + CSS_VALUE_AT_RULE_IMPORT_SPECIFIER => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if CssIdentifier::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + CSS_VALUE_AT_RULE_IMPORT_SPECIFIER.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(CSS_VALUE_AT_RULE_IMPORT_SPECIFIER, children) + } + CSS_VALUE_AT_RULE_NAMED_IMPORT_SPECIFIER => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<3usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if CssIdentifier::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if let Some(element) = ¤t_element { + if element.kind() == T![as] { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if let Some(element) = ¤t_element { + if CssIdentifier::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + CSS_VALUE_AT_RULE_NAMED_IMPORT_SPECIFIER.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(CSS_VALUE_AT_RULE_NAMED_IMPORT_SPECIFIER, children) + } CSS_COMPONENT_VALUE_LIST => { Self::make_node_list_syntax(kind, children, AnyCssValue::can_cast) } @@ -4220,6 +4393,20 @@ impl SyntaxFactory for CssSyntaxFactory { CSS_URL_MODIFIER_LIST => { Self::make_node_list_syntax(kind, children, AnyCssUrlModifier::can_cast) } + CSS_VALUE_AT_RULE_IMPORT_SPECIFIER_LIST => Self::make_separated_list_syntax( + kind, + children, + AnyCssValueAtRuleImportSpecifier::can_cast, + T ! [,], + false, + ), + CSS_VALUE_AT_RULE_PROPERTY_LIST => Self::make_separated_list_syntax( + kind, + children, + AnyCssValueAtRuleProperty::can_cast, + T ! [,], + false, + ), _ => unreachable!("Is {:?} a token?", kind), } } diff --git a/crates/biome_css_formatter/src/css/any/at_rule.rs b/crates/biome_css_formatter/src/css/any/at_rule.rs index b05cc19933cb..bdc64c0e781d 100644 --- a/crates/biome_css_formatter/src/css/any/at_rule.rs +++ b/crates/biome_css_formatter/src/css/any/at_rule.rs @@ -27,6 +27,7 @@ impl FormatRule for FormatAnyCssAtRule { AnyCssAtRule::CssScopeAtRule(node) => node.format().fmt(f), AnyCssAtRule::CssStartingStyleAtRule(node) => node.format().fmt(f), AnyCssAtRule::CssSupportsAtRule(node) => node.format().fmt(f), + AnyCssAtRule::CssValueAtRule(node) => node.format().fmt(f), } } } diff --git a/crates/biome_css_formatter/src/css/any/mod.rs b/crates/biome_css_formatter/src/css/any/mod.rs index 74731f20c4ef..24d4db6fb108 100644 --- a/crates/biome_css_formatter/src/css/any/mod.rs +++ b/crates/biome_css_formatter/src/css/any/mod.rs @@ -70,3 +70,7 @@ pub(crate) mod supports_or_combinable_condition; pub(crate) mod url_modifier; pub(crate) mod url_value; pub(crate) mod value; +pub(crate) mod value_at_rule_clause; +pub(crate) mod value_at_rule_import_source; +pub(crate) mod value_at_rule_import_specifier; +pub(crate) mod value_at_rule_property; diff --git a/crates/biome_css_formatter/src/css/any/value_at_rule_clause.rs b/crates/biome_css_formatter/src/css/any/value_at_rule_clause.rs new file mode 100644 index 000000000000..7d3271c5d3c0 --- /dev/null +++ b/crates/biome_css_formatter/src/css/any/value_at_rule_clause.rs @@ -0,0 +1,15 @@ +//! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file. + +use crate::prelude::*; +use biome_css_syntax::AnyCssValueAtRuleClause; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatAnyCssValueAtRuleClause; +impl FormatRule for FormatAnyCssValueAtRuleClause { + type Context = CssFormatContext; + fn fmt(&self, node: &AnyCssValueAtRuleClause, f: &mut CssFormatter) -> FormatResult<()> { + match node { + AnyCssValueAtRuleClause::CssValueAtRuleDeclarationClause(node) => node.format().fmt(f), + AnyCssValueAtRuleClause::CssValueAtRuleImportClause(node) => node.format().fmt(f), + } + } +} diff --git a/crates/biome_css_formatter/src/css/any/value_at_rule_import_source.rs b/crates/biome_css_formatter/src/css/any/value_at_rule_import_source.rs new file mode 100644 index 000000000000..0dba8232dfb9 --- /dev/null +++ b/crates/biome_css_formatter/src/css/any/value_at_rule_import_source.rs @@ -0,0 +1,15 @@ +//! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file. + +use crate::prelude::*; +use biome_css_syntax::AnyCssValueAtRuleImportSource; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatAnyCssValueAtRuleImportSource; +impl FormatRule for FormatAnyCssValueAtRuleImportSource { + type Context = CssFormatContext; + fn fmt(&self, node: &AnyCssValueAtRuleImportSource, f: &mut CssFormatter) -> FormatResult<()> { + match node { + AnyCssValueAtRuleImportSource::CssIdentifier(node) => node.format().fmt(f), + AnyCssValueAtRuleImportSource::CssString(node) => node.format().fmt(f), + } + } +} diff --git a/crates/biome_css_formatter/src/css/any/value_at_rule_import_specifier.rs b/crates/biome_css_formatter/src/css/any/value_at_rule_import_specifier.rs new file mode 100644 index 000000000000..97f07aed70ba --- /dev/null +++ b/crates/biome_css_formatter/src/css/any/value_at_rule_import_specifier.rs @@ -0,0 +1,23 @@ +//! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file. + +use crate::prelude::*; +use biome_css_syntax::AnyCssValueAtRuleImportSpecifier; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatAnyCssValueAtRuleImportSpecifier; +impl FormatRule for FormatAnyCssValueAtRuleImportSpecifier { + type Context = CssFormatContext; + fn fmt( + &self, + node: &AnyCssValueAtRuleImportSpecifier, + f: &mut CssFormatter, + ) -> FormatResult<()> { + match node { + AnyCssValueAtRuleImportSpecifier::CssValueAtRuleImportSpecifier(node) => { + node.format().fmt(f) + } + AnyCssValueAtRuleImportSpecifier::CssValueAtRuleNamedImportSpecifier(node) => { + node.format().fmt(f) + } + } + } +} diff --git a/crates/biome_css_formatter/src/css/any/value_at_rule_property.rs b/crates/biome_css_formatter/src/css/any/value_at_rule_property.rs new file mode 100644 index 000000000000..8246c1746037 --- /dev/null +++ b/crates/biome_css_formatter/src/css/any/value_at_rule_property.rs @@ -0,0 +1,15 @@ +//! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file. + +use crate::prelude::*; +use biome_css_syntax::AnyCssValueAtRuleProperty; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatAnyCssValueAtRuleProperty; +impl FormatRule for FormatAnyCssValueAtRuleProperty { + type Context = CssFormatContext; + fn fmt(&self, node: &AnyCssValueAtRuleProperty, f: &mut CssFormatter) -> FormatResult<()> { + match node { + AnyCssValueAtRuleProperty::CssBogusProperty(node) => node.format().fmt(f), + AnyCssValueAtRuleProperty::CssValueAtRuleGenericProperty(node) => node.format().fmt(f), + } + } +} diff --git a/crates/biome_css_formatter/src/css/auxiliary/mod.rs b/crates/biome_css_formatter/src/css/auxiliary/mod.rs index 5e29882972ea..36f80a09f892 100644 --- a/crates/biome_css_formatter/src/css/auxiliary/mod.rs +++ b/crates/biome_css_formatter/src/css/auxiliary/mod.rs @@ -70,3 +70,7 @@ pub(crate) mod supports_not_condition; pub(crate) mod supports_or_condition; pub(crate) mod universal_namespace_prefix; pub(crate) mod url_function; +pub(crate) mod value_at_rule_declaration_clause; +pub(crate) mod value_at_rule_import_clause; +pub(crate) mod value_at_rule_import_specifier; +pub(crate) mod value_at_rule_named_import_specifier; diff --git a/crates/biome_css_formatter/src/css/auxiliary/value_at_rule_declaration_clause.rs b/crates/biome_css_formatter/src/css/auxiliary/value_at_rule_declaration_clause.rs new file mode 100644 index 000000000000..8827f37a2f3e --- /dev/null +++ b/crates/biome_css_formatter/src/css/auxiliary/value_at_rule_declaration_clause.rs @@ -0,0 +1,14 @@ +use crate::prelude::*; +use biome_css_syntax::CssValueAtRuleDeclarationClause; +use biome_rowan::AstNode; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatCssValueAtRuleDeclarationClause; +impl FormatNodeRule for FormatCssValueAtRuleDeclarationClause { + fn fmt_fields( + &self, + node: &CssValueAtRuleDeclarationClause, + f: &mut CssFormatter, + ) -> FormatResult<()> { + format_verbatim_node(node.syntax()).fmt(f) + } +} diff --git a/crates/biome_css_formatter/src/css/auxiliary/value_at_rule_import_clause.rs b/crates/biome_css_formatter/src/css/auxiliary/value_at_rule_import_clause.rs new file mode 100644 index 000000000000..abcf0ba3f724 --- /dev/null +++ b/crates/biome_css_formatter/src/css/auxiliary/value_at_rule_import_clause.rs @@ -0,0 +1,14 @@ +use crate::prelude::*; +use biome_css_syntax::CssValueAtRuleImportClause; +use biome_rowan::AstNode; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatCssValueAtRuleImportClause; +impl FormatNodeRule for FormatCssValueAtRuleImportClause { + fn fmt_fields( + &self, + node: &CssValueAtRuleImportClause, + f: &mut CssFormatter, + ) -> FormatResult<()> { + format_verbatim_node(node.syntax()).fmt(f) + } +} diff --git a/crates/biome_css_formatter/src/css/auxiliary/value_at_rule_import_specifier.rs b/crates/biome_css_formatter/src/css/auxiliary/value_at_rule_import_specifier.rs new file mode 100644 index 000000000000..0c425afba1a3 --- /dev/null +++ b/crates/biome_css_formatter/src/css/auxiliary/value_at_rule_import_specifier.rs @@ -0,0 +1,14 @@ +use crate::prelude::*; +use biome_css_syntax::CssValueAtRuleImportSpecifier; +use biome_rowan::AstNode; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatCssValueAtRuleImportSpecifier; +impl FormatNodeRule for FormatCssValueAtRuleImportSpecifier { + fn fmt_fields( + &self, + node: &CssValueAtRuleImportSpecifier, + f: &mut CssFormatter, + ) -> FormatResult<()> { + format_verbatim_node(node.syntax()).fmt(f) + } +} diff --git a/crates/biome_css_formatter/src/css/auxiliary/value_at_rule_named_import_specifier.rs b/crates/biome_css_formatter/src/css/auxiliary/value_at_rule_named_import_specifier.rs new file mode 100644 index 000000000000..e03805c9f016 --- /dev/null +++ b/crates/biome_css_formatter/src/css/auxiliary/value_at_rule_named_import_specifier.rs @@ -0,0 +1,16 @@ +use crate::prelude::*; +use biome_css_syntax::CssValueAtRuleNamedImportSpecifier; +use biome_rowan::AstNode; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatCssValueAtRuleNamedImportSpecifier; +impl FormatNodeRule + for FormatCssValueAtRuleNamedImportSpecifier +{ + fn fmt_fields( + &self, + node: &CssValueAtRuleNamedImportSpecifier, + f: &mut CssFormatter, + ) -> FormatResult<()> { + format_verbatim_node(node.syntax()).fmt(f) + } +} diff --git a/crates/biome_css_formatter/src/css/bogus/mod.rs b/crates/biome_css_formatter/src/css/bogus/mod.rs index 47c7fb78c4d1..a4c981b9d3e1 100644 --- a/crates/biome_css_formatter/src/css/bogus/mod.rs +++ b/crates/biome_css_formatter/src/css/bogus/mod.rs @@ -23,3 +23,4 @@ pub(crate) mod bogus_scope_range; pub(crate) mod bogus_selector; pub(crate) mod bogus_sub_selector; pub(crate) mod bogus_url_modifier; +pub(crate) mod value_at_rule_generic_value; diff --git a/crates/biome_css_formatter/src/css/bogus/value_at_rule_generic_value.rs b/crates/biome_css_formatter/src/css/bogus/value_at_rule_generic_value.rs new file mode 100644 index 000000000000..2beb6c63d1e8 --- /dev/null +++ b/crates/biome_css_formatter/src/css/bogus/value_at_rule_generic_value.rs @@ -0,0 +1,5 @@ +use crate::FormatBogusNodeRule; +use biome_css_syntax::CssValueAtRuleGenericValue; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatCssValueAtRuleGenericValue; +impl FormatBogusNodeRule for FormatCssValueAtRuleGenericValue {} diff --git a/crates/biome_css_formatter/src/css/lists/mod.rs b/crates/biome_css_formatter/src/css/lists/mod.rs index 74962b19f636..b16c1c0b4cc4 100644 --- a/crates/biome_css_formatter/src/css/lists/mod.rs +++ b/crates/biome_css_formatter/src/css/lists/mod.rs @@ -25,3 +25,5 @@ pub(crate) mod rule_list; pub(crate) mod selector_list; pub(crate) mod sub_selector_list; pub(crate) mod url_modifier_list; +pub(crate) mod value_at_rule_import_specifier_list; +pub(crate) mod value_at_rule_property_list; diff --git a/crates/biome_css_formatter/src/css/lists/value_at_rule_import_specifier_list.rs b/crates/biome_css_formatter/src/css/lists/value_at_rule_import_specifier_list.rs new file mode 100644 index 000000000000..0d3b0b27f6f7 --- /dev/null +++ b/crates/biome_css_formatter/src/css/lists/value_at_rule_import_specifier_list.rs @@ -0,0 +1,14 @@ +use crate::prelude::*; +use biome_css_syntax::CssValueAtRuleImportSpecifierList; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatCssValueAtRuleImportSpecifierList; +impl FormatRule for FormatCssValueAtRuleImportSpecifierList { + type Context = CssFormatContext; + fn fmt( + &self, + node: &CssValueAtRuleImportSpecifierList, + f: &mut CssFormatter, + ) -> FormatResult<()> { + format_verbatim_node(node.syntax()).fmt(f) + } +} diff --git a/crates/biome_css_formatter/src/css/lists/value_at_rule_property_list.rs b/crates/biome_css_formatter/src/css/lists/value_at_rule_property_list.rs new file mode 100644 index 000000000000..b757313207ab --- /dev/null +++ b/crates/biome_css_formatter/src/css/lists/value_at_rule_property_list.rs @@ -0,0 +1,10 @@ +use crate::prelude::*; +use biome_css_syntax::CssValueAtRulePropertyList; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatCssValueAtRulePropertyList; +impl FormatRule for FormatCssValueAtRulePropertyList { + type Context = CssFormatContext; + fn fmt(&self, node: &CssValueAtRulePropertyList, f: &mut CssFormatter) -> FormatResult<()> { + f.join().entries(node.iter().formatted()).finish() + } +} diff --git a/crates/biome_css_formatter/src/css/properties/mod.rs b/crates/biome_css_formatter/src/css/properties/mod.rs index cdad64da9ef9..391baed69ad3 100644 --- a/crates/biome_css_formatter/src/css/properties/mod.rs +++ b/crates/biome_css_formatter/src/css/properties/mod.rs @@ -1,3 +1,4 @@ //! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file. pub(crate) mod generic_property; +pub(crate) mod value_at_rule_generic_property; diff --git a/crates/biome_css_formatter/src/css/properties/value_at_rule_generic_property.rs b/crates/biome_css_formatter/src/css/properties/value_at_rule_generic_property.rs new file mode 100644 index 000000000000..628547051d14 --- /dev/null +++ b/crates/biome_css_formatter/src/css/properties/value_at_rule_generic_property.rs @@ -0,0 +1,22 @@ +use crate::prelude::*; +use biome_css_syntax::{CssValueAtRuleGenericProperty, CssValueAtRuleGenericPropertyFields}; +use biome_formatter::write; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatCssValueAtRuleGenericProperty; +impl FormatNodeRule for FormatCssValueAtRuleGenericProperty { + fn fmt_fields( + &self, + node: &CssValueAtRuleGenericProperty, + f: &mut CssFormatter, + ) -> FormatResult<()> { + let CssValueAtRuleGenericPropertyFields { + name, + colon_token, + value, + } = node.as_fields(); + write!( + f, + [name.format(), colon_token.format(), space(), value.format()] + ) + } +} diff --git a/crates/biome_css_formatter/src/css/statements/mod.rs b/crates/biome_css_formatter/src/css/statements/mod.rs index 9ac3872e4621..4236073418a2 100644 --- a/crates/biome_css_formatter/src/css/statements/mod.rs +++ b/crates/biome_css_formatter/src/css/statements/mod.rs @@ -20,3 +20,4 @@ pub(crate) mod property_at_rule; pub(crate) mod scope_at_rule; pub(crate) mod starting_style_at_rule; pub(crate) mod supports_at_rule; +pub(crate) mod value_at_rule; diff --git a/crates/biome_css_formatter/src/css/statements/value_at_rule.rs b/crates/biome_css_formatter/src/css/statements/value_at_rule.rs new file mode 100644 index 000000000000..e0a698b0c64c --- /dev/null +++ b/crates/biome_css_formatter/src/css/statements/value_at_rule.rs @@ -0,0 +1,10 @@ +use crate::prelude::*; +use biome_css_syntax::CssValueAtRule; +use biome_rowan::AstNode; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatCssValueAtRule; +impl FormatNodeRule for FormatCssValueAtRule { + fn fmt_fields(&self, node: &CssValueAtRule, f: &mut CssFormatter) -> FormatResult<()> { + format_verbatim_node(node.syntax()).fmt(f) + } +} diff --git a/crates/biome_css_formatter/src/generated.rs b/crates/biome_css_formatter/src/generated.rs index b9b341129aeb..9d5c79bb74d2 100644 --- a/crates/biome_css_formatter/src/generated.rs +++ b/crates/biome_css_formatter/src/generated.rs @@ -4868,6 +4868,178 @@ impl IntoFormat for biome_css_syntax::CssUrlValueRaw { ) } } +impl FormatRule + for crate::css::statements::value_at_rule::FormatCssValueAtRule +{ + type Context = CssFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_css_syntax::CssValueAtRule, + f: &mut CssFormatter, + ) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_css_syntax::CssValueAtRule { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::CssValueAtRule, + crate::css::statements::value_at_rule::FormatCssValueAtRule, + >; + fn format(&self) -> Self::Format<'_> { + #![allow(clippy::default_constructed_unit_structs)] + FormatRefWithRule::new( + self, + crate::css::statements::value_at_rule::FormatCssValueAtRule::default(), + ) + } +} +impl IntoFormat for biome_css_syntax::CssValueAtRule { + type Format = FormatOwnedWithRule< + biome_css_syntax::CssValueAtRule, + crate::css::statements::value_at_rule::FormatCssValueAtRule, + >; + fn into_format(self) -> Self::Format { + #![allow(clippy::default_constructed_unit_structs)] + FormatOwnedWithRule::new( + self, + crate::css::statements::value_at_rule::FormatCssValueAtRule::default(), + ) + } +} +impl FormatRule < biome_css_syntax :: CssValueAtRuleDeclarationClause > for crate :: css :: auxiliary :: value_at_rule_declaration_clause :: FormatCssValueAtRuleDeclarationClause { type Context = CssFormatContext ; # [inline (always)] fn fmt (& self , node : & biome_css_syntax :: CssValueAtRuleDeclarationClause , f : & mut CssFormatter) -> FormatResult < () > { FormatNodeRule :: < biome_css_syntax :: CssValueAtRuleDeclarationClause > :: fmt (self , node , f) } } +impl AsFormat for biome_css_syntax::CssValueAtRuleDeclarationClause { + type Format < 'a > = FormatRefWithRule < 'a , biome_css_syntax :: CssValueAtRuleDeclarationClause , crate :: css :: auxiliary :: value_at_rule_declaration_clause :: FormatCssValueAtRuleDeclarationClause > ; + fn format(&self) -> Self::Format<'_> { + #![allow(clippy::default_constructed_unit_structs)] + FormatRefWithRule :: new (self , crate :: css :: auxiliary :: value_at_rule_declaration_clause :: FormatCssValueAtRuleDeclarationClause :: default ()) + } +} +impl IntoFormat for biome_css_syntax::CssValueAtRuleDeclarationClause { + type Format = FormatOwnedWithRule < biome_css_syntax :: CssValueAtRuleDeclarationClause , crate :: css :: auxiliary :: value_at_rule_declaration_clause :: FormatCssValueAtRuleDeclarationClause > ; + fn into_format(self) -> Self::Format { + #![allow(clippy::default_constructed_unit_structs)] + FormatOwnedWithRule :: new (self , crate :: css :: auxiliary :: value_at_rule_declaration_clause :: FormatCssValueAtRuleDeclarationClause :: default ()) + } +} +impl FormatRule + for crate::css::properties::value_at_rule_generic_property::FormatCssValueAtRuleGenericProperty +{ + type Context = CssFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_css_syntax::CssValueAtRuleGenericProperty, + f: &mut CssFormatter, + ) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_css_syntax::CssValueAtRuleGenericProperty { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::CssValueAtRuleGenericProperty, + crate::css::properties::value_at_rule_generic_property::FormatCssValueAtRuleGenericProperty, + >; + fn format(&self) -> Self::Format<'_> { + #![allow(clippy::default_constructed_unit_structs)] + FormatRefWithRule :: new (self , crate :: css :: properties :: value_at_rule_generic_property :: FormatCssValueAtRuleGenericProperty :: default ()) + } +} +impl IntoFormat for biome_css_syntax::CssValueAtRuleGenericProperty { + type Format = FormatOwnedWithRule< + biome_css_syntax::CssValueAtRuleGenericProperty, + crate::css::properties::value_at_rule_generic_property::FormatCssValueAtRuleGenericProperty, + >; + fn into_format(self) -> Self::Format { + #![allow(clippy::default_constructed_unit_structs)] + FormatOwnedWithRule :: new (self , crate :: css :: properties :: value_at_rule_generic_property :: FormatCssValueAtRuleGenericProperty :: default ()) + } +} +impl FormatRule + for crate::css::auxiliary::value_at_rule_import_clause::FormatCssValueAtRuleImportClause +{ + type Context = CssFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_css_syntax::CssValueAtRuleImportClause, + f: &mut CssFormatter, + ) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_css_syntax::CssValueAtRuleImportClause { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::CssValueAtRuleImportClause, + crate::css::auxiliary::value_at_rule_import_clause::FormatCssValueAtRuleImportClause, + >; + fn format(&self) -> Self::Format<'_> { + #![allow(clippy::default_constructed_unit_structs)] + FormatRefWithRule :: new (self , crate :: css :: auxiliary :: value_at_rule_import_clause :: FormatCssValueAtRuleImportClause :: default ()) + } +} +impl IntoFormat for biome_css_syntax::CssValueAtRuleImportClause { + type Format = FormatOwnedWithRule< + biome_css_syntax::CssValueAtRuleImportClause, + crate::css::auxiliary::value_at_rule_import_clause::FormatCssValueAtRuleImportClause, + >; + fn into_format(self) -> Self::Format { + #![allow(clippy::default_constructed_unit_structs)] + FormatOwnedWithRule :: new (self , crate :: css :: auxiliary :: value_at_rule_import_clause :: FormatCssValueAtRuleImportClause :: default ()) + } +} +impl FormatRule + for crate::css::auxiliary::value_at_rule_import_specifier::FormatCssValueAtRuleImportSpecifier +{ + type Context = CssFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_css_syntax::CssValueAtRuleImportSpecifier, + f: &mut CssFormatter, + ) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_css_syntax::CssValueAtRuleImportSpecifier { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::CssValueAtRuleImportSpecifier, + crate::css::auxiliary::value_at_rule_import_specifier::FormatCssValueAtRuleImportSpecifier, + >; + fn format(&self) -> Self::Format<'_> { + #![allow(clippy::default_constructed_unit_structs)] + FormatRefWithRule :: new (self , crate :: css :: auxiliary :: value_at_rule_import_specifier :: FormatCssValueAtRuleImportSpecifier :: default ()) + } +} +impl IntoFormat for biome_css_syntax::CssValueAtRuleImportSpecifier { + type Format = FormatOwnedWithRule< + biome_css_syntax::CssValueAtRuleImportSpecifier, + crate::css::auxiliary::value_at_rule_import_specifier::FormatCssValueAtRuleImportSpecifier, + >; + fn into_format(self) -> Self::Format { + #![allow(clippy::default_constructed_unit_structs)] + FormatOwnedWithRule :: new (self , crate :: css :: auxiliary :: value_at_rule_import_specifier :: FormatCssValueAtRuleImportSpecifier :: default ()) + } +} +impl FormatRule < biome_css_syntax :: CssValueAtRuleNamedImportSpecifier > for crate :: css :: auxiliary :: value_at_rule_named_import_specifier :: FormatCssValueAtRuleNamedImportSpecifier { type Context = CssFormatContext ; # [inline (always)] fn fmt (& self , node : & biome_css_syntax :: CssValueAtRuleNamedImportSpecifier , f : & mut CssFormatter) -> FormatResult < () > { FormatNodeRule :: < biome_css_syntax :: CssValueAtRuleNamedImportSpecifier > :: fmt (self , node , f) } } +impl AsFormat for biome_css_syntax::CssValueAtRuleNamedImportSpecifier { + type Format < 'a > = FormatRefWithRule < 'a , biome_css_syntax :: CssValueAtRuleNamedImportSpecifier , crate :: css :: auxiliary :: value_at_rule_named_import_specifier :: FormatCssValueAtRuleNamedImportSpecifier > ; + fn format(&self) -> Self::Format<'_> { + #![allow(clippy::default_constructed_unit_structs)] + FormatRefWithRule :: new (self , crate :: css :: auxiliary :: value_at_rule_named_import_specifier :: FormatCssValueAtRuleNamedImportSpecifier :: default ()) + } +} +impl IntoFormat for biome_css_syntax::CssValueAtRuleNamedImportSpecifier { + type Format = FormatOwnedWithRule < biome_css_syntax :: CssValueAtRuleNamedImportSpecifier , crate :: css :: auxiliary :: value_at_rule_named_import_specifier :: FormatCssValueAtRuleNamedImportSpecifier > ; + fn into_format(self) -> Self::Format { + #![allow(clippy::default_constructed_unit_structs)] + FormatOwnedWithRule :: new (self , crate :: css :: auxiliary :: value_at_rule_named_import_specifier :: FormatCssValueAtRuleNamedImportSpecifier :: default ()) + } +} impl AsFormat for biome_css_syntax::CssComponentValueList { type Format<'a> = FormatRefWithRule< 'a, @@ -5527,6 +5699,41 @@ impl IntoFormat for biome_css_syntax::CssUrlModifierList { ) } } +impl AsFormat for biome_css_syntax::CssValueAtRuleImportSpecifierList { + type Format < 'a > = FormatRefWithRule < 'a , biome_css_syntax :: CssValueAtRuleImportSpecifierList , crate :: css :: lists :: value_at_rule_import_specifier_list :: FormatCssValueAtRuleImportSpecifierList > ; + fn format(&self) -> Self::Format<'_> { + #![allow(clippy::default_constructed_unit_structs)] + FormatRefWithRule :: new (self , crate :: css :: lists :: value_at_rule_import_specifier_list :: FormatCssValueAtRuleImportSpecifierList :: default ()) + } +} +impl IntoFormat for biome_css_syntax::CssValueAtRuleImportSpecifierList { + type Format = FormatOwnedWithRule < biome_css_syntax :: CssValueAtRuleImportSpecifierList , crate :: css :: lists :: value_at_rule_import_specifier_list :: FormatCssValueAtRuleImportSpecifierList > ; + fn into_format(self) -> Self::Format { + #![allow(clippy::default_constructed_unit_structs)] + FormatOwnedWithRule :: new (self , crate :: css :: lists :: value_at_rule_import_specifier_list :: FormatCssValueAtRuleImportSpecifierList :: default ()) + } +} +impl AsFormat for biome_css_syntax::CssValueAtRulePropertyList { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::CssValueAtRulePropertyList, + crate::css::lists::value_at_rule_property_list::FormatCssValueAtRulePropertyList, + >; + fn format(&self) -> Self::Format<'_> { + #![allow(clippy::default_constructed_unit_structs)] + FormatRefWithRule :: new (self , crate :: css :: lists :: value_at_rule_property_list :: FormatCssValueAtRulePropertyList :: default ()) + } +} +impl IntoFormat for biome_css_syntax::CssValueAtRulePropertyList { + type Format = FormatOwnedWithRule< + biome_css_syntax::CssValueAtRulePropertyList, + crate::css::lists::value_at_rule_property_list::FormatCssValueAtRulePropertyList, + >; + fn into_format(self) -> Self::Format { + #![allow(clippy::default_constructed_unit_structs)] + FormatOwnedWithRule :: new (self , crate :: css :: lists :: value_at_rule_property_list :: FormatCssValueAtRulePropertyList :: default ()) + } +} impl FormatRule for crate::css::bogus::bogus::FormatCssBogus { type Context = CssFormatContext; #[inline(always)] @@ -6374,6 +6581,40 @@ impl IntoFormat for biome_css_syntax::CssBogusUrlModifier { ) } } +impl FormatRule + for crate::css::bogus::value_at_rule_generic_value::FormatCssValueAtRuleGenericValue +{ + type Context = CssFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_css_syntax::CssValueAtRuleGenericValue, + f: &mut CssFormatter, + ) -> FormatResult<()> { + FormatBogusNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_css_syntax::CssValueAtRuleGenericValue { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::CssValueAtRuleGenericValue, + crate::css::bogus::value_at_rule_generic_value::FormatCssValueAtRuleGenericValue, + >; + fn format(&self) -> Self::Format<'_> { + #![allow(clippy::default_constructed_unit_structs)] + FormatRefWithRule :: new (self , crate :: css :: bogus :: value_at_rule_generic_value :: FormatCssValueAtRuleGenericValue :: default ()) + } +} +impl IntoFormat for biome_css_syntax::CssValueAtRuleGenericValue { + type Format = FormatOwnedWithRule< + biome_css_syntax::CssValueAtRuleGenericValue, + crate::css::bogus::value_at_rule_generic_value::FormatCssValueAtRuleGenericValue, + >; + fn into_format(self) -> Self::Format { + #![allow(clippy::default_constructed_unit_structs)] + FormatOwnedWithRule :: new (self , crate :: css :: bogus :: value_at_rule_generic_value :: FormatCssValueAtRuleGenericValue :: default ()) + } +} impl AsFormat for biome_css_syntax::AnyCssAtRule { type Format<'a> = FormatRefWithRule< 'a, @@ -8166,3 +8407,99 @@ impl IntoFormat for biome_css_syntax::AnyCssValue { FormatOwnedWithRule::new(self, crate::css::any::value::FormatAnyCssValue::default()) } } +impl AsFormat for biome_css_syntax::AnyCssValueAtRuleClause { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::AnyCssValueAtRuleClause, + crate::css::any::value_at_rule_clause::FormatAnyCssValueAtRuleClause, + >; + fn format(&self) -> Self::Format<'_> { + #![allow(clippy::default_constructed_unit_structs)] + FormatRefWithRule::new( + self, + crate::css::any::value_at_rule_clause::FormatAnyCssValueAtRuleClause::default(), + ) + } +} +impl IntoFormat for biome_css_syntax::AnyCssValueAtRuleClause { + type Format = FormatOwnedWithRule< + biome_css_syntax::AnyCssValueAtRuleClause, + crate::css::any::value_at_rule_clause::FormatAnyCssValueAtRuleClause, + >; + fn into_format(self) -> Self::Format { + #![allow(clippy::default_constructed_unit_structs)] + FormatOwnedWithRule::new( + self, + crate::css::any::value_at_rule_clause::FormatAnyCssValueAtRuleClause::default(), + ) + } +} +impl AsFormat for biome_css_syntax::AnyCssValueAtRuleImportSource { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::AnyCssValueAtRuleImportSource, + crate::css::any::value_at_rule_import_source::FormatAnyCssValueAtRuleImportSource, + >; + fn format(&self) -> Self::Format<'_> { + #![allow(clippy::default_constructed_unit_structs)] + FormatRefWithRule :: new (self , crate :: css :: any :: value_at_rule_import_source :: FormatAnyCssValueAtRuleImportSource :: default ()) + } +} +impl IntoFormat for biome_css_syntax::AnyCssValueAtRuleImportSource { + type Format = FormatOwnedWithRule< + biome_css_syntax::AnyCssValueAtRuleImportSource, + crate::css::any::value_at_rule_import_source::FormatAnyCssValueAtRuleImportSource, + >; + fn into_format(self) -> Self::Format { + #![allow(clippy::default_constructed_unit_structs)] + FormatOwnedWithRule :: new (self , crate :: css :: any :: value_at_rule_import_source :: FormatAnyCssValueAtRuleImportSource :: default ()) + } +} +impl AsFormat for biome_css_syntax::AnyCssValueAtRuleImportSpecifier { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::AnyCssValueAtRuleImportSpecifier, + crate::css::any::value_at_rule_import_specifier::FormatAnyCssValueAtRuleImportSpecifier, + >; + fn format(&self) -> Self::Format<'_> { + #![allow(clippy::default_constructed_unit_structs)] + FormatRefWithRule :: new (self , crate :: css :: any :: value_at_rule_import_specifier :: FormatAnyCssValueAtRuleImportSpecifier :: default ()) + } +} +impl IntoFormat for biome_css_syntax::AnyCssValueAtRuleImportSpecifier { + type Format = FormatOwnedWithRule< + biome_css_syntax::AnyCssValueAtRuleImportSpecifier, + crate::css::any::value_at_rule_import_specifier::FormatAnyCssValueAtRuleImportSpecifier, + >; + fn into_format(self) -> Self::Format { + #![allow(clippy::default_constructed_unit_structs)] + FormatOwnedWithRule :: new (self , crate :: css :: any :: value_at_rule_import_specifier :: FormatAnyCssValueAtRuleImportSpecifier :: default ()) + } +} +impl AsFormat for biome_css_syntax::AnyCssValueAtRuleProperty { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::AnyCssValueAtRuleProperty, + crate::css::any::value_at_rule_property::FormatAnyCssValueAtRuleProperty, + >; + fn format(&self) -> Self::Format<'_> { + #![allow(clippy::default_constructed_unit_structs)] + FormatRefWithRule::new( + self, + crate::css::any::value_at_rule_property::FormatAnyCssValueAtRuleProperty::default(), + ) + } +} +impl IntoFormat for biome_css_syntax::AnyCssValueAtRuleProperty { + type Format = FormatOwnedWithRule< + biome_css_syntax::AnyCssValueAtRuleProperty, + crate::css::any::value_at_rule_property::FormatAnyCssValueAtRuleProperty, + >; + fn into_format(self) -> Self::Format { + #![allow(clippy::default_constructed_unit_structs)] + FormatOwnedWithRule::new( + self, + crate::css::any::value_at_rule_property::FormatAnyCssValueAtRuleProperty::default(), + ) + } +} diff --git a/crates/biome_css_formatter/tests/language.rs b/crates/biome_css_formatter/tests/language.rs index 4a03098df42f..0dfa81319a6b 100644 --- a/crates/biome_css_formatter/tests/language.rs +++ b/crates/biome_css_formatter/tests/language.rs @@ -19,7 +19,12 @@ impl TestFormatLanguage for CssTestFormatLanguage { type FormatLanguage = CssFormatLanguage; fn parse(&self, text: &str) -> AnyParse { - let parse = parse_css(text, CssParserOptions::default()); + let parse = parse_css( + text, + CssParserOptions::default() + .allow_wrong_line_comments() + .allow_css_modules(), + ); AnyParse::new(parse.syntax().as_send().unwrap(), parse.into_diagnostics()) } diff --git a/crates/biome_css_formatter/tests/specs/prettier/css/modules/modules.css.snap b/crates/biome_css_formatter/tests/specs/prettier/css/modules/modules.css.snap index 6c5314481f69..25d089092d29 100644 --- a/crates/biome_css_formatter/tests/specs/prettier/css/modules/modules.css.snap +++ b/crates/biome_css_formatter/tests/specs/prettier/css/modules/modules.css.snap @@ -69,17 +69,8 @@ info: css/modules/modules.css ```diff --- Prettier +++ Biome -@@ -1,11 +1,18 @@ --@value colors: './colors.css'; --@value first, second, third from colors; -+@ -+value colors: './colors.css' -+; -+@ -+value first, -+second, -+third from colors -+; +@@ -2,10 +2,11 @@ + @value first, second, third from colors; .title { - @nest :global(h1)& { @@ -92,7 +83,7 @@ info: css/modules/modules.css .className { color: green; -@@ -13,16 +20,16 @@ +@@ -13,16 +14,16 @@ } .otherClassName { @@ -112,7 +103,7 @@ info: css/modules/modules.css } :global { -@@ -42,7 +49,7 @@ +@@ -42,7 +43,7 @@ localAlias: keyFromDep; } :export { @@ -126,14 +117,8 @@ info: css/modules/modules.css # Output ```css -@ -value colors: './colors.css' -; -@ -value first, -second, -third from colors -; +@value colors: './colors.css'; +@value first, second, third from colors; .title { @ @@ -192,147 +177,6 @@ third from colors # Errors ``` -modules.css:1:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - × Unexpected value or character. - - > 1 │ @value colors: './colors.css'; - │ ^^^^^ - 2 │ @value first, second, third from colors; - 3 │ - - i Expected one of: - - - charset - - color-profile - - container - - counter-style - - document - - font-face - - font-feature-values - - font-palette-values - - import - - keyframes - - layer - - media - - namespace - - page - - property - - supports - - viewport - - scope - -modules.css:1:16 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - × Unexpected value or character. - - > 1 │ @value colors: './colors.css'; - │ ^^^^^^^^^^^^^^ - 2 │ @value first, second, third from colors; - 3 │ - - i Expected one of: - - - hover - - focus - - active - - first-child - - last-child - - nth-child - - nth-last-child - - first-of-type - - last-of-type - - nth-of-type - - nth-last-of-type - - only-child - - only-of-type - - checked - - disabled - - enabled - - required - - optional - - valid - - invalid - - in-range - - out-of-range - - read-only - - read-write - - placeholder-shown - - default - - checked - - indeterminate - - blank - - empty - - root - - target - - lang - - not - - is - - where - - fullscreen - - link - - visited - - any-link - - local-link - - scope - - current - - past - - future - -modules.css:1:30 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - × expected `,` but instead found `;` - - > 1 │ @value colors: './colors.css'; - │ ^ - 2 │ @value first, second, third from colors; - 3 │ - - i Remove ; - -modules.css:2:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - × Unexpected value or character. - - 1 │ @value colors: './colors.css'; - > 2 │ @value first, second, third from colors; - │ ^^^^^ - 3 │ - 4 │ .title { - - i Expected one of: - - - charset - - color-profile - - container - - counter-style - - document - - font-face - - font-feature-values - - font-palette-values - - import - - keyframes - - layer - - media - - namespace - - page - - property - - supports - - viewport - - scope - -modules.css:2:40 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - × expected `,` but instead found `;` - - 1 │ @value colors: './colors.css'; - > 2 │ @value first, second, third from colors; - │ ^ - 3 │ - 4 │ .title { - - i Remove ; - modules.css:5:4 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Unexpected value or character. diff --git a/crates/biome_css_formatter/tests/specs/prettier/css/units/values.css.snap b/crates/biome_css_formatter/tests/specs/prettier/css/units/values.css.snap index c882f3169263..007cefb3ea3a 100644 --- a/crates/biome_css_formatter/tests/specs/prettier/css/units/values.css.snap +++ b/crates/biome_css_formatter/tests/specs/prettier/css/units/values.css.snap @@ -2,7 +2,6 @@ source: crates/biome_formatter_test/src/snapshot_builder.rs info: css/units/values.css --- - # Input ```css @@ -62,10 +61,11 @@ info: css/units/values.css ```diff --- Prettier +++ Biome -@@ -1,7 +1,9 @@ +@@ -1,7 +1,10 @@ -@value 4XLarge 28/36px; -+@ -+value 4XLarge 28/36px ++@value ++4 ++XLarge 28/36px +; .postCssLowerCasingValueName { @@ -74,7 +74,7 @@ info: css/units/values.css } .cssUnits { -@@ -25,7 +27,7 @@ +@@ -25,7 +28,7 @@ a: 5vmax; a: 5cm; a: 5mm; @@ -83,7 +83,7 @@ info: css/units/values.css a: 5in; a: 5pt; a: 5pc; -@@ -35,13 +37,13 @@ +@@ -35,13 +38,13 @@ a: 5rad; a: 5s; a: 5ms; @@ -106,8 +106,9 @@ info: css/units/values.css # Output ```css -@ -value 4XLarge 28/36px +@value +4 +XLarge 28/36px ; .postCssLowerCasingValueName { @@ -159,62 +160,19 @@ value 4XLarge 28/36px # Errors ``` -values.css:1:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - × Unexpected value or character. - - > 1 │ @value 4XLarge 28/36px; - │ ^^^^^ - 2 │ - 3 │ .postCssLowerCasingValueName { - - i Expected one of: - - - charset - - color-profile - - container - - counter-style - - document - - font-face - - font-feature-values - - font-palette-values - - import - - keyframes - - layer - - media - - namespace - - page - - property - - supports - - viewport - - scope - values.css:1:8 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - × Expected a compound selector but instead found '4'. - - > 1 │ @value 4XLarge 28/36px; - │ ^ - 2 │ - 3 │ .postCssLowerCasingValueName { - - i Expected a compound selector here. + × Unexpected value or character. > 1 │ @value 4XLarge 28/36px; │ ^ 2 │ 3 │ .postCssLowerCasingValueName { -values.css:1:9 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - × expected `,` but instead found `XLarge` - - > 1 │ @value 4XLarge 28/36px; - │ ^^^^^^ - 2 │ - 3 │ .postCssLowerCasingValueName { + i Expected one of: - i Remove XLarge + - declaration at rule clause + - import at rule clause values.css:1:16 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -256,5 +214,3 @@ values.css:1:23 parse ━━━━━━━━━━━━━━━━━━━ ``` - - diff --git a/crates/biome_css_parser/Cargo.toml b/crates/biome_css_parser/Cargo.toml index 5499aebcd3a5..bdc68dbd8b60 100644 --- a/crates/biome_css_parser/Cargo.toml +++ b/crates/biome_css_parser/Cargo.toml @@ -21,11 +21,15 @@ biome_unicode_table = { workspace = true } tracing = { workspace = true } [dev-dependencies] -biome_test_utils = { path = "../biome_test_utils" } -insta = { workspace = true } -quickcheck = { workspace = true } -quickcheck_macros = { workspace = true } -tests_macros = { path = "../tests_macros" } +biome_configuration = { workspace = true } +biome_deserialize = { workspace = true } +biome_fs = { workspace = true } +biome_service = { workspace = true } +biome_test_utils = { path = "../biome_test_utils" } +insta = { workspace = true } +quickcheck = { workspace = true } +quickcheck_macros = { workspace = true } +tests_macros = { path = "../tests_macros" } [lints] workspace = true diff --git a/crates/biome_css_parser/src/lexer/mod.rs b/crates/biome_css_parser/src/lexer/mod.rs index 91ab39e55041..2fbc878083a4 100644 --- a/crates/biome_css_parser/src/lexer/mod.rs +++ b/crates/biome_css_parser/src/lexer/mod.rs @@ -77,7 +77,7 @@ pub(crate) struct CssLexer<'src> { diagnostics: Vec, - config: CssParserOptions, + options: CssParserOptions, } impl<'src> Lexer<'src> for CssLexer<'src> { @@ -197,12 +197,12 @@ impl<'src> CssLexer<'src> { current_flags: TokenFlags::empty(), position: 0, diagnostics: vec![], - config: CssParserOptions::default(), + options: CssParserOptions::default(), } } - pub(crate) fn with_config(self, config: CssParserOptions) -> Self { - Self { config, ..self } + pub(crate) fn with_options(self, options: CssParserOptions) -> Self { + Self { options, ..self } } /// Bumps the current byte and creates a lexed token of the passed in kind @@ -843,6 +843,8 @@ impl<'src> CssLexer<'src> { b"domain" => DOMAIN_KW, b"media-document" => MEDIA_DOCUMENT_KW, b"regexp" => REGEXP_KW, + b"value" => VALUE_KW, + b"as" => AS_KW, _ => IDENT, } } @@ -1030,7 +1032,7 @@ impl<'src> CssLexer<'src> { COMMENT } } - Some(b'/') if self.config.allow_wrong_line_comments => { + Some(b'/') if self.options.allow_wrong_line_comments => { self.advance(2); while let Some(chr) = self.current_byte() { diff --git a/crates/biome_css_parser/src/lexer/tests.rs b/crates/biome_css_parser/src/lexer/tests.rs index eda53c0c2465..375a4ab93d2b 100644 --- a/crates/biome_css_parser/src/lexer/tests.rs +++ b/crates/biome_css_parser/src/lexer/tests.rs @@ -15,8 +15,8 @@ use std::time::Duration; // and make sure the tokens yielded are fully lossless and the source can be reconstructed from only the tokens macro_rules! assert_lex { ($src:expr, $($kind:ident:$len:expr $(,)?)*) => {{ - let config = CssParserOptions::default().allow_wrong_line_comments(); - let mut lexer = CssLexer::from_str($src).with_config(config); + let options = CssParserOptions::default().allow_wrong_line_comments().allow_css_modules(); + let mut lexer = CssLexer::from_str($src).with_options(options); let mut idx = 0; let mut tok_idx = TextSize::default(); diff --git a/crates/biome_css_parser/src/lib.rs b/crates/biome_css_parser/src/lib.rs index 6d820fe6b1f6..937d5164936f 100644 --- a/crates/biome_css_parser/src/lib.rs +++ b/crates/biome_css_parser/src/lib.rs @@ -29,10 +29,10 @@ pub fn parse_css(source: &str, options: CssParserOptions) -> CssParse { pub fn parse_css_with_cache( source: &str, cache: &mut NodeCache, - config: CssParserOptions, + options: CssParserOptions, ) -> CssParse { tracing::debug_span!("Parsing phase").in_scope(move || { - let mut parser = CssParser::new(source, config); + let mut parser = CssParser::new(source, options); parse_root(&mut parser); diff --git a/crates/biome_css_parser/src/parser.rs b/crates/biome_css_parser/src/parser.rs index 4daf37df363a..ba36514c8596 100644 --- a/crates/biome_css_parser/src/parser.rs +++ b/crates/biome_css_parser/src/parser.rs @@ -12,29 +12,53 @@ pub(crate) struct CssParser<'source> { context: ParserContext, source: CssTokenSource<'source>, state: CssParserState, + options: CssParserOptions, } #[derive(Default, Debug, Clone, Copy)] pub struct CssParserOptions { + /// If this is `true`, **wrong** comments starting with `//` will be treated + /// as a comment. + /// + /// This option exists because there are so many css-in-js tools and people + /// use `//` as a comment because it's javascript file. + /// + /// Defaults to `false`. pub allow_wrong_line_comments: bool, + + /// Enables parsing of CSS Modules specific features. + /// Defaults to `false`. + pub css_modules: bool, } impl CssParserOptions { + /// Allows the parser to parse wrong line comments. pub fn allow_wrong_line_comments(mut self) -> Self { self.allow_wrong_line_comments = true; self } + + /// Enables parsing of css modules selectors. + pub fn allow_css_modules(mut self) -> Self { + self.css_modules = true; + self + } } impl<'source> CssParser<'source> { - pub fn new(source: &'source str, config: CssParserOptions) -> Self { + pub fn new(source: &'source str, options: CssParserOptions) -> Self { Self { context: ParserContext::default(), - source: CssTokenSource::from_str(source, config), + source: CssTokenSource::from_str(source, options), state: CssParserState::new(), + options, } } + pub fn options(&self) -> &CssParserOptions { + &self.options + } + /// Re-lexes the current token in the specified context. Returns the kind /// of the re-lexed token (can be the same as before if the context doesn't make a difference for the current token) #[allow(dead_code)] //TODO remote this once we actually don't use it diff --git a/crates/biome_css_parser/src/syntax/at_rule/mod.rs b/crates/biome_css_parser/src/syntax/at_rule/mod.rs index d49fcca1cf83..441f6f850e36 100644 --- a/crates/biome_css_parser/src/syntax/at_rule/mod.rs +++ b/crates/biome_css_parser/src/syntax/at_rule/mod.rs @@ -18,43 +18,34 @@ mod property; mod scope; mod starting_style; mod supports; +mod value; use crate::parser::CssParser; -use crate::syntax::at_rule::charset::{is_at_charset_at_rule, parse_charset_at_rule}; -use crate::syntax::at_rule::color_profile::{ - is_at_color_profile_at_rule, parse_color_profile_at_rule, -}; -use crate::syntax::at_rule::container::{is_at_container_at_rule, parse_container_at_rule}; -use crate::syntax::at_rule::counter_style::{ - is_at_counter_style_at_rule, parse_counter_style_at_rule, -}; -use crate::syntax::at_rule::document::{is_at_document_at_rule, parse_document_at_rule}; -use crate::syntax::at_rule::font_face::{is_at_font_face_at_rule, parse_font_face_at_rule}; -use crate::syntax::at_rule::font_feature_values::{ - is_at_font_feature_values_at_rule, parse_font_feature_values_at_rule, -}; -use crate::syntax::at_rule::font_palette_values::{ - is_at_font_palette_values_at_rule, parse_font_palette_values_at_rule, -}; -use crate::syntax::at_rule::import::{is_at_import_at_rule, parse_import_at_rule}; -use crate::syntax::at_rule::keyframes::{is_at_keyframes_at_rule, parse_keyframes_at_rule}; -use crate::syntax::at_rule::layer::{is_at_layer_at_rule, parse_layer_at_rule}; -use crate::syntax::at_rule::media::{is_at_media_at_rule, parse_media_at_rule}; -use crate::syntax::at_rule::namespace::{is_at_namespace_at_rule, parse_namespace_at_rule}; -use crate::syntax::at_rule::page::{is_at_page_at_rule, parse_page_at_rule}; -use crate::syntax::at_rule::scope::{is_at_scope_at_rule, parse_scope_at_rule}; -use crate::syntax::at_rule::starting_style::{ - is_at_starting_style_at_rule, parse_starting_style_at_rule, -}; -use crate::syntax::at_rule::supports::{is_at_supports_at_rule, parse_supports_at_rule}; +use crate::syntax::at_rule::charset::parse_charset_at_rule; +use crate::syntax::at_rule::color_profile::parse_color_profile_at_rule; +use crate::syntax::at_rule::container::parse_container_at_rule; +use crate::syntax::at_rule::counter_style::parse_counter_style_at_rule; +use crate::syntax::at_rule::document::parse_document_at_rule; +use crate::syntax::at_rule::font_face::parse_font_face_at_rule; +use crate::syntax::at_rule::font_feature_values::parse_font_feature_values_at_rule; +use crate::syntax::at_rule::font_palette_values::parse_font_palette_values_at_rule; +use crate::syntax::at_rule::import::parse_import_at_rule; +use crate::syntax::at_rule::keyframes::parse_keyframes_at_rule; +use crate::syntax::at_rule::layer::parse_layer_at_rule; +use crate::syntax::at_rule::media::parse_media_at_rule; +use crate::syntax::at_rule::namespace::parse_namespace_at_rule; +use crate::syntax::at_rule::page::parse_page_at_rule; +use crate::syntax::at_rule::property::parse_property_at_rule; +use crate::syntax::at_rule::scope::parse_scope_at_rule; +use crate::syntax::at_rule::starting_style::parse_starting_style_at_rule; +use crate::syntax::at_rule::supports::parse_supports_at_rule; +use crate::syntax::at_rule::value::parse_value_at_rule; use crate::syntax::parse_error::expected_any_at_rule; use biome_css_syntax::CssSyntaxKind::*; use biome_css_syntax::T; use biome_parser::prelude::ParsedSyntax::{Absent, Present}; use biome_parser::prelude::*; -use self::property::{is_at_property_at_rule, parse_property_at_rule}; - #[inline] pub(crate) fn is_at_at_rule(p: &mut CssParser) -> bool { p.at(T![@]) @@ -70,13 +61,15 @@ pub(crate) fn parse_at_rule(p: &mut CssParser) -> ParsedSyntax { p.bump(T![@]); - let kind = if parse_any_at_rule(p) - .or_add_diagnostic(p, expected_any_at_rule) - .is_some() - { - CSS_AT_RULE - } else { - CSS_BOGUS_RULE + // Show the error under the token next to the '@' + let range = p.cur_range(); + + let kind = match parse_any_at_rule(p) { + Present(_) => CSS_AT_RULE, + Absent => { + p.error(expected_any_at_rule(p, range)); + CSS_BOGUS_RULE + } }; Present(m.complete(p, kind)) @@ -84,43 +77,26 @@ pub(crate) fn parse_at_rule(p: &mut CssParser) -> ParsedSyntax { #[inline] pub(crate) fn parse_any_at_rule(p: &mut CssParser) -> ParsedSyntax { - if is_at_charset_at_rule(p) { - parse_charset_at_rule(p) - } else if is_at_color_profile_at_rule(p) { - parse_color_profile_at_rule(p) - } else if is_at_counter_style_at_rule(p) { - parse_counter_style_at_rule(p) - } else if is_at_container_at_rule(p) { - parse_container_at_rule(p) - } else if is_at_font_face_at_rule(p) { - parse_font_face_at_rule(p) - } else if is_at_font_feature_values_at_rule(p) { - parse_font_feature_values_at_rule(p) - } else if is_at_font_palette_values_at_rule(p) { - parse_font_palette_values_at_rule(p) - } else if is_at_media_at_rule(p) { - parse_media_at_rule(p) - } else if is_at_keyframes_at_rule(p) { - parse_keyframes_at_rule(p) - } else if is_at_page_at_rule(p) { - parse_page_at_rule(p) - } else if is_at_layer_at_rule(p) { - parse_layer_at_rule(p) - } else if is_at_scope_at_rule(p) { - parse_scope_at_rule(p) - } else if is_at_supports_at_rule(p) { - parse_supports_at_rule(p) - } else if is_at_import_at_rule(p) { - parse_import_at_rule(p) - } else if is_at_namespace_at_rule(p) { - parse_namespace_at_rule(p) - } else if is_at_starting_style_at_rule(p) { - parse_starting_style_at_rule(p) - } else if is_at_document_at_rule(p) { - parse_document_at_rule(p) - } else if is_at_property_at_rule(p) { - parse_property_at_rule(p) - } else { - Absent + match p.cur() { + T![charset] => parse_charset_at_rule(p), + T![color_profile] => parse_color_profile_at_rule(p), + T![counter_style] => parse_counter_style_at_rule(p), + T![container] => parse_container_at_rule(p), + T![font_face] => parse_font_face_at_rule(p), + T![font_feature_values] => parse_font_feature_values_at_rule(p), + T![font_palette_values] => parse_font_palette_values_at_rule(p), + T![media] => parse_media_at_rule(p), + T![keyframes] => parse_keyframes_at_rule(p), + T![page] => parse_page_at_rule(p), + T![layer] => parse_layer_at_rule(p), + T![scope] => parse_scope_at_rule(p), + T![supports] => parse_supports_at_rule(p), + T![import] => parse_import_at_rule(p), + T![namespace] => parse_namespace_at_rule(p), + T![starting_style] => parse_starting_style_at_rule(p), + T![document] => parse_document_at_rule(p), + T![property] => parse_property_at_rule(p), + T![value] => parse_value_at_rule(p), + _ => Absent, } } diff --git a/crates/biome_css_parser/src/syntax/at_rule/value.rs b/crates/biome_css_parser/src/syntax/at_rule/value.rs new file mode 100644 index 000000000000..80061861eee9 --- /dev/null +++ b/crates/biome_css_parser/src/syntax/at_rule/value.rs @@ -0,0 +1,319 @@ +use biome_css_syntax::CssSyntaxKind::*; +use biome_css_syntax::{CssSyntaxKind, TextRange, T}; +use biome_parser::diagnostic::{expect_one_of, ParseDiagnostic}; +use biome_parser::parse_lists::ParseSeparatedList; +use biome_parser::parse_recovery::{ParseRecovery, RecoveryResult}; +use biome_parser::parsed_syntax::ParsedSyntax::Present; +use biome_parser::prelude::ParsedSyntax::Absent; +use biome_parser::prelude::ToDiagnostic; +use biome_parser::{parsed_syntax::ParsedSyntax, token_set, Parser}; + +use crate::parser::CssParser; +use crate::syntax::parse_error::{expected_component_value, expected_identifier}; +use crate::syntax::{ + is_at_identifier, is_at_string, is_nth_at_identifier, parse_regular_identifier, parse_string, +}; + +/// Checks if the current token in the parser is a `@value` at-rule. +/// +/// This function verifies if the current token matches the `@value` rule. +#[inline] +pub(crate) fn is_at_value_at_rule(p: &mut CssParser) -> bool { + p.at(T![value]) +} + +/// Parses a `@value` at-rule in a CSS stylesheet. +/// For details, see [CSS Modules Values](https://github.com/css-modules/postcss-modules-values). +/// ```css +/// @value my-color #f00; +/// @value my-size 10px; +/// @value primary, secondary from colors; +/// @value small as bp-small, medium, large as bp-large from "./breakpoints.css"; +/// ``` +/// This function identifies and parses these `@value` rules within CSS stylesheets. +#[inline] +pub(crate) fn parse_value_at_rule(p: &mut CssParser) -> ParsedSyntax { + if !is_at_value_at_rule(p) { + return Absent; + } + + if !p.options().css_modules { + // @value at-rule is not a standard CSS feature. + // Provide a hint on how to enable parsing of @value at-rules. + p.error(value_at_rule_not_allowed(p, p.cur_range())); + + // Skip the entire rule to avoid parsing errors. + // Skip until the next semicolon. + while !p.eat(T![;]) { + p.bump_any(); + } + + return Absent; + } + + let m = p.start(); + + p.bump(T![value]); + + if is_at_value_at_rule_declaration_clause(p) { + parse_value_at_rule_declaration_clause(p).ok(); + } else if is_at_value_at_rule_import_clause(p) { + parse_value_at_rule_import_clause(p).ok(); + } else { + p.error(expected_at_rule_declaration_clause(p, p.cur_range())); + } + + p.expect(T![;]); + + Present(m.complete(p, CSS_VALUE_AT_RULE)) +} + +/// Checks if the current parser position is at a value at-rule import clause. +fn is_at_value_at_rule_import_clause(p: &mut CssParser) -> bool { + is_at_identifier(p) +} + +/// Parses a value at-rule import clause from the CSS parser. +/// ```css +/// @value red, green from "./colors.css" ; +/// @value my-size 10px; +/// @value primary, secondary from colors; +/// @value small as bp-small, medium, large as bp-large from "./breakpoints.css"; +/// ``` +fn parse_value_at_rule_import_clause(p: &mut CssParser) -> ParsedSyntax { + if !is_at_value_at_rule_import_clause(p) { + return Absent; + } + + let m = p.start(); + + ValueAtRuleImportSpecifierList.parse_list(p); + p.expect(T![from]); + parse_value_at_rule_import_source(p).or_add_diagnostic(p, expected_import_source); + + Present(m.complete(p, CSS_VALUE_AT_RULE_IMPORT_CLAUSE)) +} + +struct ValueAtRuleImportSpecifierList; + +impl ParseSeparatedList for ValueAtRuleImportSpecifierList { + type Kind = CssSyntaxKind; + type Parser<'source> = CssParser<'source>; + const LIST_KIND: Self::Kind = CSS_VALUE_AT_RULE_IMPORT_SPECIFIER_LIST; + + fn parse_element(&mut self, p: &mut Self::Parser<'_>) -> ParsedSyntax { + parse_value_at_rule_import_specifier(p) + } + + fn is_at_list_end(&self, p: &mut Self::Parser<'_>) -> bool { + p.at(T![from]) + } + + fn recover( + &mut self, + p: &mut Self::Parser<'_>, + parsed_element: ParsedSyntax, + ) -> RecoveryResult { + parsed_element.or_recover( + p, + &ValueAtRuleImportSpecifierListParseRecovery, + expected_import_specifier, + ) + } + + fn separating_element_kind(&mut self) -> Self::Kind { + T![,] + } +} + +struct ValueAtRuleImportSpecifierListParseRecovery; + +impl ParseRecovery for ValueAtRuleImportSpecifierListParseRecovery { + type Kind = CssSyntaxKind; + type Parser<'source> = CssParser<'source>; + const RECOVERED_KIND: Self::Kind = CSS_BOGUS; + + fn is_at_recovered(&self, p: &mut Self::Parser<'_>) -> bool { + // , is a separator, from is an end of a list + p.at_ts(token_set![T![,], T![from]]) + } +} + +/// Parses a value at-rule import specifier from the CSS parser. +fn parse_value_at_rule_import_specifier(p: &mut CssParser) -> ParsedSyntax { + if !is_at_identifier(p) { + return Absent; + } + + let m = p.start(); + + parse_regular_identifier(p).ok(); + + let kind = if p.eat(T![as]) { + parse_regular_identifier(p).or_add_diagnostic(p, expected_identifier); + CSS_VALUE_AT_RULE_NAMED_IMPORT_SPECIFIER + } else { + CSS_VALUE_AT_RULE_IMPORT_SPECIFIER + }; + + Present(m.complete(p, kind)) +} + +/// Checks if the current parser position is at a value at-rule import source. +fn is_at_value_at_rule_import_source(p: &mut CssParser) -> bool { + is_at_identifier(p) || is_at_string(p) +} + +/// Parses a value at-rule import source from the CSS parser. +fn parse_value_at_rule_import_source(p: &mut CssParser) -> ParsedSyntax { + if !is_at_value_at_rule_import_source(p) { + return Absent; + } + + if is_at_identifier(p) { + parse_regular_identifier(p) + } else { + parse_string(p) + } +} + +/// Checks if the current parser position is at a value at-rule declaration clause. +fn is_at_value_at_rule_declaration_clause(p: &mut CssParser) -> bool { + is_nth_at_value_at_rule_generic_property(p, 0) +} + +/// Parses a value at-rule declaration clause from the CSS parser. +fn parse_value_at_rule_declaration_clause(p: &mut CssParser) -> ParsedSyntax { + if !is_at_value_at_rule_declaration_clause(p) { + return Absent; + } + + let m = p.start(); + ValueAtRulePropertyList.parse_list(p); + Present(m.complete(p, CSS_VALUE_AT_RULE_DECLARATION_CLAUSE)) +} + +pub(crate) struct ValueAtRulePropertyList; + +impl ParseSeparatedList for ValueAtRulePropertyList { + type Kind = CssSyntaxKind; + type Parser<'source> = CssParser<'source>; + const LIST_KIND: Self::Kind = CSS_VALUE_AT_RULE_PROPERTY_LIST; + + fn parse_element(&mut self, p: &mut Self::Parser<'_>) -> ParsedSyntax { + parse_value_at_rule_generic_property(p) + } + + fn is_at_list_end(&self, p: &mut Self::Parser<'_>) -> bool { + p.at(T![;]) + } + + fn recover( + &mut self, + p: &mut Self::Parser<'_>, + parsed_element: ParsedSyntax, + ) -> RecoveryResult { + parsed_element.or_recover( + p, + &ValueAtRulePropertyListParseRecovery, + expected_component_value, + ) + } + + fn separating_element_kind(&mut self) -> Self::Kind { + T![,] + } +} + +/// Checks if the parser is at a generic property of a value at-rule at the nth position. +fn is_nth_at_value_at_rule_generic_property(p: &mut CssParser, n: usize) -> bool { + is_nth_at_identifier(p, n) && p.nth_at(n + 1, T![:]) +} + +/// Parses a generic property of a value at-rule from the CSS parser. +/// A generic property can be any sequence of tokens, which is why we parse it as an unformed tree. +/// This approach allows for flexibility, as the exact structure of generic properties may not be well-defined. +/// By parsing the property in this manner, we ensure that we can handle a wide variety of cases without +/// requiring specific grammar rules for each possible property structure. +/// +/// If we want to extend the parsing with a specific grammar in the future, we can implement speculative parsing. +/// Speculative parsing would attempt to parse the property according to the new grammar rules. +/// If an error occurs during this process, we can fall back to the current implementation, which treats +/// the property as an unformed tree. This approach ensures backward compatibility and allows for incremental +/// improvements to the parser without breaking existing functionality. +fn parse_value_at_rule_generic_property(p: &mut CssParser) -> ParsedSyntax { + if !is_nth_at_value_at_rule_generic_property(p, 0) { + return Absent; + } + + let m = p.start(); + parse_regular_identifier(p).ok(); + + p.expect(T![:]); + + { + let m = p.start(); + + // Skip all tokens until the end of the property value or the next property. + // `;` indicates the end of the list. + // `,` is the separator before the next property. + while !(ValueAtRulePropertyList.is_at_list_end(p) + || p.at(T![,]) && is_nth_at_value_at_rule_generic_property(p, 1)) + { + p.bump_any(); + } + m.complete(p, CSS_VALUE_AT_RULE_GENERIC_VALUE); + } + + Present(m.complete(p, CSS_VALUE_AT_RULE_GENERIC_PROPERTY)) +} + +/// Structure for recovering from parsing errors in a value at-rule property list. +struct ValueAtRulePropertyListParseRecovery; + +impl ParseRecovery for ValueAtRulePropertyListParseRecovery { + type Kind = CssSyntaxKind; + type Parser<'source> = CssParser<'source>; + const RECOVERED_KIND: Self::Kind = CSS_BOGUS_PROPERTY; + + fn is_at_recovered(&self, p: &mut Self::Parser<'_>) -> bool { + p.at(T![;]) || is_at_identifier(p) || p.has_nth_preceding_line_break(0) + } +} + +/// Creates a diagnostic for an expected import source. +fn expected_import_source(p: &CssParser, range: TextRange) -> ParseDiagnostic { + expect_one_of(&["identifier", "string"], range).into_diagnostic(p) +} + +/// Creates a diagnostic for an expected import specifier. +fn expected_import_specifier(p: &CssParser, range: TextRange) -> ParseDiagnostic { + expect_one_of(&["identifier", " as "], range).into_diagnostic(p) +} + +/// Generates a parse diagnostic for an expected at-rule declaration clause. +/// This function returns a diagnostic error indicating that an at-rule declaration clause +/// or import clause was expected at the given range in the CSS parser. +fn expected_at_rule_declaration_clause(p: &CssParser, range: TextRange) -> ParseDiagnostic { + expect_one_of( + &["declaration at rule clause", "import at rule clause"], + range, + ) + .into_diagnostic(p) +} + +/// Generates a parse diagnostic for when the @value at-rule is not allowed. +/// +/// This function returns an error diagnostic indicating that the @value at-rule +/// is not a standard CSS feature. It also provides a hint on how to enable +/// parsing of @value at-rules by setting the `css_modules` option to `true` +/// in the configuration file. +pub(crate) fn value_at_rule_not_allowed(p: &CssParser, range: TextRange) -> ParseDiagnostic { + p.err_builder( + "@value at-rule is not a standard CSS feature.", + range, + ) + .with_hint( + "You can enable @value at-rule parsing by setting the `css_modules` option to `true` in your configuration file.", + ) +} diff --git a/crates/biome_css_parser/src/token_source.rs b/crates/biome_css_parser/src/token_source.rs index 2d1b1fd03f68..05dd5e80cb45 100644 --- a/crates/biome_css_parser/src/token_source.rs +++ b/crates/biome_css_parser/src/token_source.rs @@ -28,8 +28,8 @@ impl<'src> CssTokenSource<'src> { } /// Creates a new token source for the given string - pub fn from_str(source: &'src str, config: CssParserOptions) -> Self { - let lexer = CssLexer::from_str(source).with_config(config); + pub fn from_str(source: &'src str, options: CssParserOptions) -> Self { + let lexer = CssLexer::from_str(source).with_options(options); let buffered = BufferedLexer::new(lexer); let mut source = CssTokenSource::new(buffered); diff --git a/crates/biome_css_parser/tests/css_test_suite/error/at_rule/value/at_rule_value_disabled.css b/crates/biome_css_parser/tests/css_test_suite/error/at_rule/value/at_rule_value_disabled.css new file mode 100644 index 000000000000..2c8fdf9e8ff9 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/at_rule/value/at_rule_value_disabled.css @@ -0,0 +1,2 @@ +@value colors: "./colors.css"; +@value common-gradient: transparent 75%, var(--ring-line-color) 75%, currentColor 79%; diff --git a/crates/biome_css_parser/tests/css_test_suite/error/at_rule/value/at_rule_value_disabled.css.snap b/crates/biome_css_parser/tests/css_test_suite/error/at_rule/value/at_rule_value_disabled.css.snap new file mode 100644 index 000000000000..edc90b38e95c --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/at_rule/value/at_rule_value_disabled.css.snap @@ -0,0 +1,120 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +expression: snapshot +--- +## Input + +```css +@value colors: "./colors.css"; +@value common-gradient: transparent 75%, var(--ring-line-color) 75%, currentColor 79%; + +``` + + +## AST + +``` +CssRoot { + bom_token: missing (optional), + rules: CssRuleList [ + CssBogusRule { + items: [ + AT@0..1 "@" [] [], + VALUE_KW@1..7 "value" [] [Whitespace(" ")], + IDENT@7..13 "colors" [] [], + COLON@13..15 ":" [] [Whitespace(" ")], + CSS_STRING_LITERAL@15..29 "\"./colors.css\"" [] [], + SEMICOLON@29..30 ";" [] [], + ], + }, + CssBogusRule { + items: [ + AT@30..32 "@" [Newline("\n")] [], + VALUE_KW@32..38 "value" [] [Whitespace(" ")], + IDENT@38..53 "common-gradient" [] [], + COLON@53..55 ":" [] [Whitespace(" ")], + IDENT@55..67 "transparent" [] [Whitespace(" ")], + CSS_PERCENTAGE_VALUE@67..69 "75" [] [], + PERCENT@69..70 "%" [] [], + COMMA@70..72 "," [] [Whitespace(" ")], + VAR_KW@72..75 "var" [] [], + L_PAREN@75..76 "(" [] [], + IDENT@76..93 "--ring-line-color" [] [], + R_PAREN@93..95 ")" [] [Whitespace(" ")], + CSS_PERCENTAGE_VALUE@95..97 "75" [] [], + PERCENT@97..98 "%" [] [], + COMMA@98..100 "," [] [Whitespace(" ")], + IDENT@100..113 "currentColor" [] [Whitespace(" ")], + CSS_PERCENTAGE_VALUE@113..115 "79" [] [], + PERCENT@115..116 "%" [] [], + SEMICOLON@116..117 ";" [] [], + ], + }, + ], + eof_token: EOF@117..118 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..118 + 0: (empty) + 1: CSS_RULE_LIST@0..117 + 0: CSS_BOGUS_RULE@0..30 + 0: AT@0..1 "@" [] [] + 1: VALUE_KW@1..7 "value" [] [Whitespace(" ")] + 2: IDENT@7..13 "colors" [] [] + 3: COLON@13..15 ":" [] [Whitespace(" ")] + 4: CSS_STRING_LITERAL@15..29 "\"./colors.css\"" [] [] + 5: SEMICOLON@29..30 ";" [] [] + 1: CSS_BOGUS_RULE@30..117 + 0: AT@30..32 "@" [Newline("\n")] [] + 1: VALUE_KW@32..38 "value" [] [Whitespace(" ")] + 2: IDENT@38..53 "common-gradient" [] [] + 3: COLON@53..55 ":" [] [Whitespace(" ")] + 4: IDENT@55..67 "transparent" [] [Whitespace(" ")] + 5: CSS_PERCENTAGE_VALUE@67..69 "75" [] [] + 6: PERCENT@69..70 "%" [] [] + 7: COMMA@70..72 "," [] [Whitespace(" ")] + 8: VAR_KW@72..75 "var" [] [] + 9: L_PAREN@75..76 "(" [] [] + 10: IDENT@76..93 "--ring-line-color" [] [] + 11: R_PAREN@93..95 ")" [] [Whitespace(" ")] + 12: CSS_PERCENTAGE_VALUE@95..97 "75" [] [] + 13: PERCENT@97..98 "%" [] [] + 14: COMMA@98..100 "," [] [Whitespace(" ")] + 15: IDENT@100..113 "currentColor" [] [Whitespace(" ")] + 16: CSS_PERCENTAGE_VALUE@113..115 "79" [] [] + 17: PERCENT@115..116 "%" [] [] + 18: SEMICOLON@116..117 ";" [] [] + 2: EOF@117..118 "" [Newline("\n")] [] + +``` + +## Diagnostics + +``` +at_rule_value_disabled.css:1:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × @value at-rule is not a standard CSS feature. + + > 1 │ @value colors: "./colors.css"; + │ ^^^^^ + 2 │ @value common-gradient: transparent 75%, var(--ring-line-color) 75%, currentColor 79%; + 3 │ + + i You can enable @value at-rule parsing by setting the `css_modules` option to `true` in your configuration file. + +at_rule_value_disabled.css:2:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × @value at-rule is not a standard CSS feature. + + 1 │ @value colors: "./colors.css"; + > 2 │ @value common-gradient: transparent 75%, var(--ring-line-color) 75%, currentColor 79%; + │ ^^^^^ + 3 │ + + i You can enable @value at-rule parsing by setting the `css_modules` option to `true` in your configuration file. + +``` diff --git a/crates/biome_css_parser/tests/css_test_suite/error/at_rule/value/enabled/at_rule_value_error_enabled.css b/crates/biome_css_parser/tests/css_test_suite/error/at_rule/value/enabled/at_rule_value_error_enabled.css new file mode 100644 index 000000000000..24c83168d141 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/at_rule/value/enabled/at_rule_value_error_enabled.css @@ -0,0 +1,2 @@ +@value ; +@value f from ; diff --git a/crates/biome_css_parser/tests/css_test_suite/error/at_rule/value/enabled/at_rule_value_error_enabled.css.snap b/crates/biome_css_parser/tests/css_test_suite/error/at_rule/value/enabled/at_rule_value_error_enabled.css.snap new file mode 100644 index 000000000000..d943033c0d51 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/at_rule/value/enabled/at_rule_value_error_enabled.css.snap @@ -0,0 +1,110 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +expression: snapshot +--- +## Input + +```css +@value ; +@value f from ; + +``` + + +## AST + +``` +CssRoot { + bom_token: missing (optional), + rules: CssRuleList [ + CssAtRule { + at_token: AT@0..1 "@" [] [], + rule: CssValueAtRule { + value_token: VALUE_KW@1..7 "value" [] [Whitespace(" ")], + clause: missing (required), + semicolon_token: SEMICOLON@7..8 ";" [] [], + }, + }, + CssAtRule { + at_token: AT@8..10 "@" [Newline("\n")] [], + rule: CssValueAtRule { + value_token: VALUE_KW@10..16 "value" [] [Whitespace(" ")], + clause: CssValueAtRuleImportClause { + specifiers: CssValueAtRuleImportSpecifierList [ + CssValueAtRuleImportSpecifier { + name: CssIdentifier { + value_token: IDENT@16..18 "f" [] [Whitespace(" ")], + }, + }, + ], + from_token: FROM_KW@18..23 "from" [] [Whitespace(" ")], + source: missing (required), + }, + semicolon_token: SEMICOLON@23..24 ";" [] [], + }, + }, + ], + eof_token: EOF@24..25 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..25 + 0: (empty) + 1: CSS_RULE_LIST@0..24 + 0: CSS_AT_RULE@0..8 + 0: AT@0..1 "@" [] [] + 1: CSS_VALUE_AT_RULE@1..8 + 0: VALUE_KW@1..7 "value" [] [Whitespace(" ")] + 1: (empty) + 2: SEMICOLON@7..8 ";" [] [] + 1: CSS_AT_RULE@8..24 + 0: AT@8..10 "@" [Newline("\n")] [] + 1: CSS_VALUE_AT_RULE@10..24 + 0: VALUE_KW@10..16 "value" [] [Whitespace(" ")] + 1: CSS_VALUE_AT_RULE_IMPORT_CLAUSE@16..23 + 0: CSS_VALUE_AT_RULE_IMPORT_SPECIFIER_LIST@16..18 + 0: CSS_VALUE_AT_RULE_IMPORT_SPECIFIER@16..18 + 0: CSS_IDENTIFIER@16..18 + 0: IDENT@16..18 "f" [] [Whitespace(" ")] + 1: FROM_KW@18..23 "from" [] [Whitespace(" ")] + 2: (empty) + 2: SEMICOLON@23..24 ";" [] [] + 2: EOF@24..25 "" [Newline("\n")] [] + +``` + +## Diagnostics + +``` +at_rule_value_error_enabled.css:1:8 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Unexpected value or character. + + > 1 │ @value ; + │ ^ + 2 │ @value f from ; + 3 │ + + i Expected one of: + + - declaration at rule clause + - import at rule clause + +at_rule_value_error_enabled.css:2:15 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Unexpected value or character. + + 1 │ @value ; + > 2 │ @value f from ; + │ ^ + 3 │ + + i Expected one of: + + - identifier + - string + +``` diff --git a/crates/biome_css_parser/tests/css_test_suite/error/at_rule/value/enabled/options.json b/crates/biome_css_parser/tests/css_test_suite/error/at_rule/value/enabled/options.json new file mode 100644 index 000000000000..bb9ca9442bd6 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/at_rule/value/enabled/options.json @@ -0,0 +1,8 @@ +{ + "$schema": "../../../../../../../../packages/@biomejs/biome/configuration_schema.json", + "css": { + "parser": { + "cssModules": true + } + } +} diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/at_rule/value/at_rule_value.css b/crates/biome_css_parser/tests/css_test_suite/ok/at_rule/value/at_rule_value.css new file mode 100644 index 000000000000..67286deeb3c0 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/at_rule/value/at_rule_value.css @@ -0,0 +1,15 @@ +/* my-component.css */ +/* alias paths for other values or composition */ +@value colors: "./colors.css"; +/* import multiple from a single file */ +@value primary, secondary from colors; +/* make local aliases to imported values */ +@value small as bp-small, medium, large as bp-large from "./breakpoints.css"; +/* value as selector name */ +@value selectorValue: secondary-color; +@value small: (max-width: 599px); +@value medium: (min-width: 600px) and (max-width: 959px); +@value large: (min-width: 960px); +@value primary: #BF4040; +@value secondary: #1F4F7F; +@value common-gradient: transparent 75%, var(--ring-line-color) 75%, currentColor 79%; diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/at_rule/value/at_rule_value.css.snap b/crates/biome_css_parser/tests/css_test_suite/ok/at_rule/value/at_rule_value.css.snap new file mode 100644 index 000000000000..fdc079ab3ae4 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/at_rule/value/at_rule_value.css.snap @@ -0,0 +1,512 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +expression: snapshot +--- +## Input + +```css +/* my-component.css */ +/* alias paths for other values or composition */ +@value colors: "./colors.css"; +/* import multiple from a single file */ +@value primary, secondary from colors; +/* make local aliases to imported values */ +@value small as bp-small, medium, large as bp-large from "./breakpoints.css"; +/* value as selector name */ +@value selectorValue: secondary-color; +@value small: (max-width: 599px); +@value medium: (min-width: 600px) and (max-width: 959px); +@value large: (min-width: 960px); +@value primary: #BF4040; +@value secondary: #1F4F7F; +@value common-gradient: transparent 75%, var(--ring-line-color) 75%, currentColor 79%; + +``` + + +## AST + +``` +CssRoot { + bom_token: missing (optional), + rules: CssRuleList [ + CssAtRule { + at_token: AT@0..74 "@" [Comments("/* my-component.css */"), Newline("\n"), Comments("/* alias paths for ot ..."), Newline("\n")] [], + rule: CssValueAtRule { + value_token: VALUE_KW@74..80 "value" [] [Whitespace(" ")], + clause: CssValueAtRuleDeclarationClause { + properties: CssValueAtRulePropertyList [ + CssValueAtRuleGenericProperty { + name: CssIdentifier { + value_token: IDENT@80..86 "colors" [] [], + }, + colon_token: COLON@86..88 ":" [] [Whitespace(" ")], + value: CssValueAtRuleGenericValue { + items: [ + CSS_STRING_LITERAL@88..102 "\"./colors.css\"" [] [], + ], + }, + }, + ], + }, + semicolon_token: SEMICOLON@102..103 ";" [] [], + }, + }, + CssAtRule { + at_token: AT@103..146 "@" [Newline("\n"), Comments("/* import multiple fr ..."), Newline("\n")] [], + rule: CssValueAtRule { + value_token: VALUE_KW@146..152 "value" [] [Whitespace(" ")], + clause: CssValueAtRuleImportClause { + specifiers: CssValueAtRuleImportSpecifierList [ + CssValueAtRuleImportSpecifier { + name: CssIdentifier { + value_token: IDENT@152..159 "primary" [] [], + }, + }, + COMMA@159..161 "," [] [Whitespace(" ")], + CssValueAtRuleImportSpecifier { + name: CssIdentifier { + value_token: IDENT@161..171 "secondary" [] [Whitespace(" ")], + }, + }, + ], + from_token: FROM_KW@171..176 "from" [] [Whitespace(" ")], + source: CssIdentifier { + value_token: IDENT@176..182 "colors" [] [], + }, + }, + semicolon_token: SEMICOLON@182..183 ";" [] [], + }, + }, + CssAtRule { + at_token: AT@183..229 "@" [Newline("\n"), Comments("/* make local aliases ..."), Newline("\n")] [], + rule: CssValueAtRule { + value_token: VALUE_KW@229..235 "value" [] [Whitespace(" ")], + clause: CssValueAtRuleImportClause { + specifiers: CssValueAtRuleImportSpecifierList [ + CssValueAtRuleNamedImportSpecifier { + name: CssIdentifier { + value_token: IDENT@235..241 "small" [] [Whitespace(" ")], + }, + as_token: AS_KW@241..244 "as" [] [Whitespace(" ")], + local_name: CssIdentifier { + value_token: IDENT@244..252 "bp-small" [] [], + }, + }, + COMMA@252..254 "," [] [Whitespace(" ")], + CssValueAtRuleImportSpecifier { + name: CssIdentifier { + value_token: IDENT@254..260 "medium" [] [], + }, + }, + COMMA@260..262 "," [] [Whitespace(" ")], + CssValueAtRuleNamedImportSpecifier { + name: CssIdentifier { + value_token: IDENT@262..268 "large" [] [Whitespace(" ")], + }, + as_token: AS_KW@268..271 "as" [] [Whitespace(" ")], + local_name: CssIdentifier { + value_token: IDENT@271..280 "bp-large" [] [Whitespace(" ")], + }, + }, + ], + from_token: FROM_KW@280..285 "from" [] [Whitespace(" ")], + source: CssString { + value_token: CSS_STRING_LITERAL@285..304 "\"./breakpoints.css\"" [] [], + }, + }, + semicolon_token: SEMICOLON@304..305 ";" [] [], + }, + }, + CssAtRule { + at_token: AT@305..336 "@" [Newline("\n"), Comments("/* value as selector ..."), Newline("\n")] [], + rule: CssValueAtRule { + value_token: VALUE_KW@336..342 "value" [] [Whitespace(" ")], + clause: CssValueAtRuleDeclarationClause { + properties: CssValueAtRulePropertyList [ + CssValueAtRuleGenericProperty { + name: CssIdentifier { + value_token: IDENT@342..355 "selectorValue" [] [], + }, + colon_token: COLON@355..357 ":" [] [Whitespace(" ")], + value: CssValueAtRuleGenericValue { + items: [ + IDENT@357..372 "secondary-color" [] [], + ], + }, + }, + ], + }, + semicolon_token: SEMICOLON@372..373 ";" [] [], + }, + }, + CssAtRule { + at_token: AT@373..375 "@" [Newline("\n")] [], + rule: CssValueAtRule { + value_token: VALUE_KW@375..381 "value" [] [Whitespace(" ")], + clause: CssValueAtRuleDeclarationClause { + properties: CssValueAtRulePropertyList [ + CssValueAtRuleGenericProperty { + name: CssIdentifier { + value_token: IDENT@381..386 "small" [] [], + }, + colon_token: COLON@386..388 ":" [] [Whitespace(" ")], + value: CssValueAtRuleGenericValue { + items: [ + L_PAREN@388..389 "(" [] [], + IDENT@389..398 "max-width" [] [], + COLON@398..400 ":" [] [Whitespace(" ")], + CSS_DIMENSION_VALUE@400..403 "599" [] [], + PX_KW@403..405 "px" [] [], + R_PAREN@405..406 ")" [] [], + ], + }, + }, + ], + }, + semicolon_token: SEMICOLON@406..407 ";" [] [], + }, + }, + CssAtRule { + at_token: AT@407..409 "@" [Newline("\n")] [], + rule: CssValueAtRule { + value_token: VALUE_KW@409..415 "value" [] [Whitespace(" ")], + clause: CssValueAtRuleDeclarationClause { + properties: CssValueAtRulePropertyList [ + CssValueAtRuleGenericProperty { + name: CssIdentifier { + value_token: IDENT@415..421 "medium" [] [], + }, + colon_token: COLON@421..423 ":" [] [Whitespace(" ")], + value: CssValueAtRuleGenericValue { + items: [ + L_PAREN@423..424 "(" [] [], + IDENT@424..433 "min-width" [] [], + COLON@433..435 ":" [] [Whitespace(" ")], + CSS_DIMENSION_VALUE@435..438 "600" [] [], + PX_KW@438..440 "px" [] [], + R_PAREN@440..442 ")" [] [Whitespace(" ")], + AND_KW@442..446 "and" [] [Whitespace(" ")], + L_PAREN@446..447 "(" [] [], + IDENT@447..456 "max-width" [] [], + COLON@456..458 ":" [] [Whitespace(" ")], + CSS_DIMENSION_VALUE@458..461 "959" [] [], + PX_KW@461..463 "px" [] [], + R_PAREN@463..464 ")" [] [], + ], + }, + }, + ], + }, + semicolon_token: SEMICOLON@464..465 ";" [] [], + }, + }, + CssAtRule { + at_token: AT@465..467 "@" [Newline("\n")] [], + rule: CssValueAtRule { + value_token: VALUE_KW@467..473 "value" [] [Whitespace(" ")], + clause: CssValueAtRuleDeclarationClause { + properties: CssValueAtRulePropertyList [ + CssValueAtRuleGenericProperty { + name: CssIdentifier { + value_token: IDENT@473..478 "large" [] [], + }, + colon_token: COLON@478..480 ":" [] [Whitespace(" ")], + value: CssValueAtRuleGenericValue { + items: [ + L_PAREN@480..481 "(" [] [], + IDENT@481..490 "min-width" [] [], + COLON@490..492 ":" [] [Whitespace(" ")], + CSS_DIMENSION_VALUE@492..495 "960" [] [], + PX_KW@495..497 "px" [] [], + R_PAREN@497..498 ")" [] [], + ], + }, + }, + ], + }, + semicolon_token: SEMICOLON@498..499 ";" [] [], + }, + }, + CssAtRule { + at_token: AT@499..501 "@" [Newline("\n")] [], + rule: CssValueAtRule { + value_token: VALUE_KW@501..507 "value" [] [Whitespace(" ")], + clause: CssValueAtRuleDeclarationClause { + properties: CssValueAtRulePropertyList [ + CssValueAtRuleGenericProperty { + name: CssIdentifier { + value_token: IDENT@507..514 "primary" [] [], + }, + colon_token: COLON@514..516 ":" [] [Whitespace(" ")], + value: CssValueAtRuleGenericValue { + items: [ + HASH@516..517 "#" [] [], + IDENT@517..523 "BF4040" [] [], + ], + }, + }, + ], + }, + semicolon_token: SEMICOLON@523..524 ";" [] [], + }, + }, + CssAtRule { + at_token: AT@524..526 "@" [Newline("\n")] [], + rule: CssValueAtRule { + value_token: VALUE_KW@526..532 "value" [] [Whitespace(" ")], + clause: CssValueAtRuleDeclarationClause { + properties: CssValueAtRulePropertyList [ + CssValueAtRuleGenericProperty { + name: CssIdentifier { + value_token: IDENT@532..541 "secondary" [] [], + }, + colon_token: COLON@541..543 ":" [] [Whitespace(" ")], + value: CssValueAtRuleGenericValue { + items: [ + HASH@543..544 "#" [] [], + CSS_DIMENSION_VALUE@544..545 "1" [] [], + IDENT@545..550 "F4F7F" [] [], + ], + }, + }, + ], + }, + semicolon_token: SEMICOLON@550..551 ";" [] [], + }, + }, + CssAtRule { + at_token: AT@551..553 "@" [Newline("\n")] [], + rule: CssValueAtRule { + value_token: VALUE_KW@553..559 "value" [] [Whitespace(" ")], + clause: CssValueAtRuleDeclarationClause { + properties: CssValueAtRulePropertyList [ + CssValueAtRuleGenericProperty { + name: CssIdentifier { + value_token: IDENT@559..574 "common-gradient" [] [], + }, + colon_token: COLON@574..576 ":" [] [Whitespace(" ")], + value: CssValueAtRuleGenericValue { + items: [ + IDENT@576..588 "transparent" [] [Whitespace(" ")], + CSS_PERCENTAGE_VALUE@588..590 "75" [] [], + PERCENT@590..591 "%" [] [], + COMMA@591..593 "," [] [Whitespace(" ")], + VAR_KW@593..596 "var" [] [], + L_PAREN@596..597 "(" [] [], + IDENT@597..614 "--ring-line-color" [] [], + R_PAREN@614..616 ")" [] [Whitespace(" ")], + CSS_PERCENTAGE_VALUE@616..618 "75" [] [], + PERCENT@618..619 "%" [] [], + COMMA@619..621 "," [] [Whitespace(" ")], + IDENT@621..634 "currentColor" [] [Whitespace(" ")], + CSS_PERCENTAGE_VALUE@634..636 "79" [] [], + PERCENT@636..637 "%" [] [], + ], + }, + }, + ], + }, + semicolon_token: SEMICOLON@637..638 ";" [] [], + }, + }, + ], + eof_token: EOF@638..639 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..639 + 0: (empty) + 1: CSS_RULE_LIST@0..638 + 0: CSS_AT_RULE@0..103 + 0: AT@0..74 "@" [Comments("/* my-component.css */"), Newline("\n"), Comments("/* alias paths for ot ..."), Newline("\n")] [] + 1: CSS_VALUE_AT_RULE@74..103 + 0: VALUE_KW@74..80 "value" [] [Whitespace(" ")] + 1: CSS_VALUE_AT_RULE_DECLARATION_CLAUSE@80..102 + 0: CSS_VALUE_AT_RULE_PROPERTY_LIST@80..102 + 0: CSS_VALUE_AT_RULE_GENERIC_PROPERTY@80..102 + 0: CSS_IDENTIFIER@80..86 + 0: IDENT@80..86 "colors" [] [] + 1: COLON@86..88 ":" [] [Whitespace(" ")] + 2: CSS_VALUE_AT_RULE_GENERIC_VALUE@88..102 + 0: CSS_STRING_LITERAL@88..102 "\"./colors.css\"" [] [] + 2: SEMICOLON@102..103 ";" [] [] + 1: CSS_AT_RULE@103..183 + 0: AT@103..146 "@" [Newline("\n"), Comments("/* import multiple fr ..."), Newline("\n")] [] + 1: CSS_VALUE_AT_RULE@146..183 + 0: VALUE_KW@146..152 "value" [] [Whitespace(" ")] + 1: CSS_VALUE_AT_RULE_IMPORT_CLAUSE@152..182 + 0: CSS_VALUE_AT_RULE_IMPORT_SPECIFIER_LIST@152..171 + 0: CSS_VALUE_AT_RULE_IMPORT_SPECIFIER@152..159 + 0: CSS_IDENTIFIER@152..159 + 0: IDENT@152..159 "primary" [] [] + 1: COMMA@159..161 "," [] [Whitespace(" ")] + 2: CSS_VALUE_AT_RULE_IMPORT_SPECIFIER@161..171 + 0: CSS_IDENTIFIER@161..171 + 0: IDENT@161..171 "secondary" [] [Whitespace(" ")] + 1: FROM_KW@171..176 "from" [] [Whitespace(" ")] + 2: CSS_IDENTIFIER@176..182 + 0: IDENT@176..182 "colors" [] [] + 2: SEMICOLON@182..183 ";" [] [] + 2: CSS_AT_RULE@183..305 + 0: AT@183..229 "@" [Newline("\n"), Comments("/* make local aliases ..."), Newline("\n")] [] + 1: CSS_VALUE_AT_RULE@229..305 + 0: VALUE_KW@229..235 "value" [] [Whitespace(" ")] + 1: CSS_VALUE_AT_RULE_IMPORT_CLAUSE@235..304 + 0: CSS_VALUE_AT_RULE_IMPORT_SPECIFIER_LIST@235..280 + 0: CSS_VALUE_AT_RULE_NAMED_IMPORT_SPECIFIER@235..252 + 0: CSS_IDENTIFIER@235..241 + 0: IDENT@235..241 "small" [] [Whitespace(" ")] + 1: AS_KW@241..244 "as" [] [Whitespace(" ")] + 2: CSS_IDENTIFIER@244..252 + 0: IDENT@244..252 "bp-small" [] [] + 1: COMMA@252..254 "," [] [Whitespace(" ")] + 2: CSS_VALUE_AT_RULE_IMPORT_SPECIFIER@254..260 + 0: CSS_IDENTIFIER@254..260 + 0: IDENT@254..260 "medium" [] [] + 3: COMMA@260..262 "," [] [Whitespace(" ")] + 4: CSS_VALUE_AT_RULE_NAMED_IMPORT_SPECIFIER@262..280 + 0: CSS_IDENTIFIER@262..268 + 0: IDENT@262..268 "large" [] [Whitespace(" ")] + 1: AS_KW@268..271 "as" [] [Whitespace(" ")] + 2: CSS_IDENTIFIER@271..280 + 0: IDENT@271..280 "bp-large" [] [Whitespace(" ")] + 1: FROM_KW@280..285 "from" [] [Whitespace(" ")] + 2: CSS_STRING@285..304 + 0: CSS_STRING_LITERAL@285..304 "\"./breakpoints.css\"" [] [] + 2: SEMICOLON@304..305 ";" [] [] + 3: CSS_AT_RULE@305..373 + 0: AT@305..336 "@" [Newline("\n"), Comments("/* value as selector ..."), Newline("\n")] [] + 1: CSS_VALUE_AT_RULE@336..373 + 0: VALUE_KW@336..342 "value" [] [Whitespace(" ")] + 1: CSS_VALUE_AT_RULE_DECLARATION_CLAUSE@342..372 + 0: CSS_VALUE_AT_RULE_PROPERTY_LIST@342..372 + 0: CSS_VALUE_AT_RULE_GENERIC_PROPERTY@342..372 + 0: CSS_IDENTIFIER@342..355 + 0: IDENT@342..355 "selectorValue" [] [] + 1: COLON@355..357 ":" [] [Whitespace(" ")] + 2: CSS_VALUE_AT_RULE_GENERIC_VALUE@357..372 + 0: IDENT@357..372 "secondary-color" [] [] + 2: SEMICOLON@372..373 ";" [] [] + 4: CSS_AT_RULE@373..407 + 0: AT@373..375 "@" [Newline("\n")] [] + 1: CSS_VALUE_AT_RULE@375..407 + 0: VALUE_KW@375..381 "value" [] [Whitespace(" ")] + 1: CSS_VALUE_AT_RULE_DECLARATION_CLAUSE@381..406 + 0: CSS_VALUE_AT_RULE_PROPERTY_LIST@381..406 + 0: CSS_VALUE_AT_RULE_GENERIC_PROPERTY@381..406 + 0: CSS_IDENTIFIER@381..386 + 0: IDENT@381..386 "small" [] [] + 1: COLON@386..388 ":" [] [Whitespace(" ")] + 2: CSS_VALUE_AT_RULE_GENERIC_VALUE@388..406 + 0: L_PAREN@388..389 "(" [] [] + 1: IDENT@389..398 "max-width" [] [] + 2: COLON@398..400 ":" [] [Whitespace(" ")] + 3: CSS_DIMENSION_VALUE@400..403 "599" [] [] + 4: PX_KW@403..405 "px" [] [] + 5: R_PAREN@405..406 ")" [] [] + 2: SEMICOLON@406..407 ";" [] [] + 5: CSS_AT_RULE@407..465 + 0: AT@407..409 "@" [Newline("\n")] [] + 1: CSS_VALUE_AT_RULE@409..465 + 0: VALUE_KW@409..415 "value" [] [Whitespace(" ")] + 1: CSS_VALUE_AT_RULE_DECLARATION_CLAUSE@415..464 + 0: CSS_VALUE_AT_RULE_PROPERTY_LIST@415..464 + 0: CSS_VALUE_AT_RULE_GENERIC_PROPERTY@415..464 + 0: CSS_IDENTIFIER@415..421 + 0: IDENT@415..421 "medium" [] [] + 1: COLON@421..423 ":" [] [Whitespace(" ")] + 2: CSS_VALUE_AT_RULE_GENERIC_VALUE@423..464 + 0: L_PAREN@423..424 "(" [] [] + 1: IDENT@424..433 "min-width" [] [] + 2: COLON@433..435 ":" [] [Whitespace(" ")] + 3: CSS_DIMENSION_VALUE@435..438 "600" [] [] + 4: PX_KW@438..440 "px" [] [] + 5: R_PAREN@440..442 ")" [] [Whitespace(" ")] + 6: AND_KW@442..446 "and" [] [Whitespace(" ")] + 7: L_PAREN@446..447 "(" [] [] + 8: IDENT@447..456 "max-width" [] [] + 9: COLON@456..458 ":" [] [Whitespace(" ")] + 10: CSS_DIMENSION_VALUE@458..461 "959" [] [] + 11: PX_KW@461..463 "px" [] [] + 12: R_PAREN@463..464 ")" [] [] + 2: SEMICOLON@464..465 ";" [] [] + 6: CSS_AT_RULE@465..499 + 0: AT@465..467 "@" [Newline("\n")] [] + 1: CSS_VALUE_AT_RULE@467..499 + 0: VALUE_KW@467..473 "value" [] [Whitespace(" ")] + 1: CSS_VALUE_AT_RULE_DECLARATION_CLAUSE@473..498 + 0: CSS_VALUE_AT_RULE_PROPERTY_LIST@473..498 + 0: CSS_VALUE_AT_RULE_GENERIC_PROPERTY@473..498 + 0: CSS_IDENTIFIER@473..478 + 0: IDENT@473..478 "large" [] [] + 1: COLON@478..480 ":" [] [Whitespace(" ")] + 2: CSS_VALUE_AT_RULE_GENERIC_VALUE@480..498 + 0: L_PAREN@480..481 "(" [] [] + 1: IDENT@481..490 "min-width" [] [] + 2: COLON@490..492 ":" [] [Whitespace(" ")] + 3: CSS_DIMENSION_VALUE@492..495 "960" [] [] + 4: PX_KW@495..497 "px" [] [] + 5: R_PAREN@497..498 ")" [] [] + 2: SEMICOLON@498..499 ";" [] [] + 7: CSS_AT_RULE@499..524 + 0: AT@499..501 "@" [Newline("\n")] [] + 1: CSS_VALUE_AT_RULE@501..524 + 0: VALUE_KW@501..507 "value" [] [Whitespace(" ")] + 1: CSS_VALUE_AT_RULE_DECLARATION_CLAUSE@507..523 + 0: CSS_VALUE_AT_RULE_PROPERTY_LIST@507..523 + 0: CSS_VALUE_AT_RULE_GENERIC_PROPERTY@507..523 + 0: CSS_IDENTIFIER@507..514 + 0: IDENT@507..514 "primary" [] [] + 1: COLON@514..516 ":" [] [Whitespace(" ")] + 2: CSS_VALUE_AT_RULE_GENERIC_VALUE@516..523 + 0: HASH@516..517 "#" [] [] + 1: IDENT@517..523 "BF4040" [] [] + 2: SEMICOLON@523..524 ";" [] [] + 8: CSS_AT_RULE@524..551 + 0: AT@524..526 "@" [Newline("\n")] [] + 1: CSS_VALUE_AT_RULE@526..551 + 0: VALUE_KW@526..532 "value" [] [Whitespace(" ")] + 1: CSS_VALUE_AT_RULE_DECLARATION_CLAUSE@532..550 + 0: CSS_VALUE_AT_RULE_PROPERTY_LIST@532..550 + 0: CSS_VALUE_AT_RULE_GENERIC_PROPERTY@532..550 + 0: CSS_IDENTIFIER@532..541 + 0: IDENT@532..541 "secondary" [] [] + 1: COLON@541..543 ":" [] [Whitespace(" ")] + 2: CSS_VALUE_AT_RULE_GENERIC_VALUE@543..550 + 0: HASH@543..544 "#" [] [] + 1: CSS_DIMENSION_VALUE@544..545 "1" [] [] + 2: IDENT@545..550 "F4F7F" [] [] + 2: SEMICOLON@550..551 ";" [] [] + 9: CSS_AT_RULE@551..638 + 0: AT@551..553 "@" [Newline("\n")] [] + 1: CSS_VALUE_AT_RULE@553..638 + 0: VALUE_KW@553..559 "value" [] [Whitespace(" ")] + 1: CSS_VALUE_AT_RULE_DECLARATION_CLAUSE@559..637 + 0: CSS_VALUE_AT_RULE_PROPERTY_LIST@559..637 + 0: CSS_VALUE_AT_RULE_GENERIC_PROPERTY@559..637 + 0: CSS_IDENTIFIER@559..574 + 0: IDENT@559..574 "common-gradient" [] [] + 1: COLON@574..576 ":" [] [Whitespace(" ")] + 2: CSS_VALUE_AT_RULE_GENERIC_VALUE@576..637 + 0: IDENT@576..588 "transparent" [] [Whitespace(" ")] + 1: CSS_PERCENTAGE_VALUE@588..590 "75" [] [] + 2: PERCENT@590..591 "%" [] [] + 3: COMMA@591..593 "," [] [Whitespace(" ")] + 4: VAR_KW@593..596 "var" [] [] + 5: L_PAREN@596..597 "(" [] [] + 6: IDENT@597..614 "--ring-line-color" [] [] + 7: R_PAREN@614..616 ")" [] [Whitespace(" ")] + 8: CSS_PERCENTAGE_VALUE@616..618 "75" [] [] + 9: PERCENT@618..619 "%" [] [] + 10: COMMA@619..621 "," [] [Whitespace(" ")] + 11: IDENT@621..634 "currentColor" [] [Whitespace(" ")] + 12: CSS_PERCENTAGE_VALUE@634..636 "79" [] [] + 13: PERCENT@636..637 "%" [] [] + 2: SEMICOLON@637..638 ";" [] [] + 2: EOF@638..639 "" [Newline("\n")] [] + +``` diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/at_rule/value/options.json b/crates/biome_css_parser/tests/css_test_suite/ok/at_rule/value/options.json new file mode 100644 index 000000000000..f789463bbcbd --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/at_rule/value/options.json @@ -0,0 +1,8 @@ +{ + "$schema": "../../../../../../../packages/@biomejs/biome/configuration_schema.json", + "css": { + "parser": { + "cssModules": true + } + } +} diff --git a/crates/biome_css_parser/tests/spec_test.rs b/crates/biome_css_parser/tests/spec_test.rs index 215e8aab3167..caec24673d40 100644 --- a/crates/biome_css_parser/tests/spec_test.rs +++ b/crates/biome_css_parser/tests/spec_test.rs @@ -1,10 +1,14 @@ +use biome_configuration::PartialConfiguration; use biome_console::fmt::{Formatter, Termcolor}; use biome_console::markup; use biome_css_parser::{parse_css, CssParserOptions}; +use biome_deserialize::json::deserialize_from_str; use biome_diagnostics::display::PrintDiagnostic; -use biome_diagnostics::termcolor; use biome_diagnostics::DiagnosticExt; +use biome_diagnostics::{print_diagnostic_to_string, termcolor}; +use biome_fs::BiomePath; use biome_rowan::SyntaxKind; +use biome_service::settings::Settings; use biome_test_utils::has_bogus_nodes_or_empty_slots; use std::fmt::Write; use std::fs; @@ -36,8 +40,44 @@ pub fn run(test_case: &str, _snapshot_name: &str, test_directory: &str, outcome_ let content = fs::read_to_string(test_case_path) .expect("Expected test path to be a readable file in UTF8 encoding"); - let parse_config = CssParserOptions::default().allow_wrong_line_comments(); - let parsed = parse_css(&content, parse_config); + let mut options = CssParserOptions::default(); + + let options_path = Path::new(test_directory).join("options.json"); + + if options_path.exists() { + let mut options_path = BiomePath::new(&options_path); + + let mut settings = Settings::default(); + // SAFETY: we checked its existence already, we assume we have rights to read it + let (test_options, diagnostics) = deserialize_from_str::( + options_path.get_buffer_from_file().as_str(), + ) + .consume(); + + settings + .merge_with_configuration(test_options.unwrap_or_default(), None, None, &[]) + .unwrap(); + + let settings = settings.languages.css.parser; + + if settings.css_modules { + options = options.allow_css_modules(); + } + + if settings.allow_wrong_line_comments { + options = options.allow_wrong_line_comments(); + } + + if !diagnostics.is_empty() { + for diagnostic in diagnostics { + println!("{:?}", print_diagnostic_to_string(&diagnostic)); + } + + panic!("Configuration is invalid"); + } + } + + let parsed = parse_css(&content, options); let formatted_ast = format!("{:#?}", parsed.tree()); let mut snapshot = String::new(); @@ -134,16 +174,14 @@ pub fn run(test_case: &str, _snapshot_name: &str, test_directory: &str, outcome_ #[test] pub fn quick_test() { let code = r#" -div { - - background: src(var(--foo)); -} - +@value large: (min-width: 960px), medium: (d) and (c); "#; let root = parse_css( code, - CssParserOptions::default().allow_wrong_line_comments(), + CssParserOptions::default() + .allow_wrong_line_comments() + .allow_css_modules(), ); let syntax = root.syntax(); dbg!(&syntax, root.diagnostics(), root.has_errors()); diff --git a/crates/biome_css_syntax/src/generated/kind.rs b/crates/biome_css_syntax/src/generated/kind.rs index 38843b376028..0d300237b92a 100644 --- a/crates/biome_css_syntax/src/generated/kind.rs +++ b/crates/biome_css_syntax/src/generated/kind.rs @@ -227,6 +227,8 @@ pub enum CssSyntaxKind { DOMAIN_KW, MEDIA_DOCUMENT_KW, REGEXP_KW, + VALUE_KW, + AS_KW, FONT_FACE_KW, CSS_STRING_LITERAL, CSS_NUMBER_LITERAL, @@ -418,6 +420,15 @@ pub enum CssSyntaxKind { CSS_DOCUMENT_AT_RULE, CSS_DOCUMENT_MATCHER_LIST, CSS_DOCUMENT_CUSTOM_MATCHER, + CSS_VALUE_AT_RULE, + CSS_VALUE_AT_RULE_PROPERTY_LIST, + CSS_VALUE_AT_RULE_IMPORT_SPECIFIER_LIST, + CSS_VALUE_AT_RULE_IMPORT_CLAUSE, + CSS_VALUE_AT_RULE_IMPORT_SPECIFIER, + CSS_VALUE_AT_RULE_NAMED_IMPORT_SPECIFIER, + CSS_VALUE_AT_RULE_DECLARATION_CLAUSE, + CSS_VALUE_AT_RULE_GENERIC_PROPERTY, + CSS_VALUE_AT_RULE_GENERIC_VALUE, CSS_BOGUS, CSS_BOGUS_BLOCK, CSS_BOGUS_KEYFRAMES_ITEM, @@ -502,7 +513,9 @@ impl CssSyntaxKind { | CSS_PAGE_AT_RULE_ITEM_LIST | CSS_LAYER_REFERENCE_LIST | CSS_LAYER_NAME_LIST - | CSS_DOCUMENT_MATCHER_LIST => true, + | CSS_DOCUMENT_MATCHER_LIST + | CSS_VALUE_AT_RULE_PROPERTY_LIST + | CSS_VALUE_AT_RULE_IMPORT_SPECIFIER_LIST => true, _ => false, } } @@ -681,6 +694,8 @@ impl CssSyntaxKind { "domain" => DOMAIN_KW, "media-document" => MEDIA_DOCUMENT_KW, "regexp" => REGEXP_KW, + "value" => VALUE_KW, + "as" => AS_KW, "font-face" => FONT_FACE_KW, _ => return None, }; @@ -902,6 +917,8 @@ impl CssSyntaxKind { DOMAIN_KW => "domain", MEDIA_DOCUMENT_KW => "media-document", REGEXP_KW => "regexp", + VALUE_KW => "value", + AS_KW => "as", FONT_FACE_KW => "font-face", CSS_STRING_LITERAL => "string literal", _ => return None, @@ -911,4 +928,4 @@ impl CssSyntaxKind { } #[doc = r" Utility macro for creating a SyntaxKind through simple macro syntax"] #[macro_export] -macro_rules ! T { [;] => { $ crate :: CssSyntaxKind :: SEMICOLON } ; [,] => { $ crate :: CssSyntaxKind :: COMMA } ; ['('] => { $ crate :: CssSyntaxKind :: L_PAREN } ; [')'] => { $ crate :: CssSyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: CssSyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: CssSyntaxKind :: R_CURLY } ; ['['] => { $ crate :: CssSyntaxKind :: L_BRACK } ; [']'] => { $ crate :: CssSyntaxKind :: R_BRACK } ; [<] => { $ crate :: CssSyntaxKind :: L_ANGLE } ; [>] => { $ crate :: CssSyntaxKind :: R_ANGLE } ; [~] => { $ crate :: CssSyntaxKind :: TILDE } ; [#] => { $ crate :: CssSyntaxKind :: HASH } ; [&] => { $ crate :: CssSyntaxKind :: AMP } ; [|] => { $ crate :: CssSyntaxKind :: PIPE } ; [||] => { $ crate :: CssSyntaxKind :: PIPE2 } ; [+] => { $ crate :: CssSyntaxKind :: PLUS } ; [*] => { $ crate :: CssSyntaxKind :: STAR } ; [/] => { $ crate :: CssSyntaxKind :: SLASH } ; [^] => { $ crate :: CssSyntaxKind :: CARET } ; [%] => { $ crate :: CssSyntaxKind :: PERCENT } ; [.] => { $ crate :: CssSyntaxKind :: DOT } ; [:] => { $ crate :: CssSyntaxKind :: COLON } ; [::] => { $ crate :: CssSyntaxKind :: COLON2 } ; [=] => { $ crate :: CssSyntaxKind :: EQ } ; [!] => { $ crate :: CssSyntaxKind :: BANG } ; [!=] => { $ crate :: CssSyntaxKind :: NEQ } ; [-] => { $ crate :: CssSyntaxKind :: MINUS } ; [<=] => { $ crate :: CssSyntaxKind :: LTEQ } ; [>=] => { $ crate :: CssSyntaxKind :: GTEQ } ; [+=] => { $ crate :: CssSyntaxKind :: PLUSEQ } ; [|=] => { $ crate :: CssSyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: CssSyntaxKind :: AMPEQ } ; [^=] => { $ crate :: CssSyntaxKind :: CARETEQ } ; [/=] => { $ crate :: CssSyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: CssSyntaxKind :: STAREQ } ; [%=] => { $ crate :: CssSyntaxKind :: PERCENTEQ } ; [@] => { $ crate :: CssSyntaxKind :: AT } ; ["$="] => { $ crate :: CssSyntaxKind :: DOLLAR_EQ } ; [~=] => { $ crate :: CssSyntaxKind :: TILDE_EQ } ; [-->] => { $ crate :: CssSyntaxKind :: CDC } ; [] => { $ crate :: CssSyntaxKind :: CDC } ; [