diff --git a/.changeset/angry-women-accept.md b/.changeset/angry-women-accept.md
new file mode 100644
index 000000000000..ea2fa52908c4
--- /dev/null
+++ b/.changeset/angry-women-accept.md
@@ -0,0 +1,7 @@
+---
+"@biomejs/biome": minor
+---
+
+The Biome CSS parser is now able to parse Vue SFC syntax such as `:slotted` and `:deep`. These pseudo functions are only correctly parsed when the CSS is defined inside `.vue` components. Otherwise, Biome will a emit a parse error.
+
+This capability is only available when `experimentalFullHtmlSupportedEnabled` is set to `true`.
diff --git a/.changeset/odd-flies-nail.md b/.changeset/odd-flies-nail.md
new file mode 100644
index 000000000000..d2de2e8572f4
--- /dev/null
+++ b/.changeset/odd-flies-nail.md
@@ -0,0 +1,17 @@
+---
+"@biomejs/biome": minor
+---
+
+Improved the CSS parser for CSS modules. Biome now automatically enables CSS modules parsing for `*.module.css` files.
+
+If your codebase has only `*.module.css` files, you can remove the parser feature as follows, because now Biome does it for you:
+
+```diff
+{
+ "css": {
+ "parser": {
+- "cssModules": true
+ }
+ }
+}
+```
diff --git a/.changeset/plenty-hornets-hide.md b/.changeset/plenty-hornets-hide.md
new file mode 100644
index 000000000000..f999a1285b0e
--- /dev/null
+++ b/.changeset/plenty-hornets-hide.md
@@ -0,0 +1,7 @@
+---
+"@biomejs/biome": minor
+---
+
+Added support for parsing `:global` and `:local` inside `.astro`, `.svelte` and `.vue` files, in `
"#
.as_bytes(),
diff --git a/crates/biome_cli/tests/cases/handle_svelte_files.rs b/crates/biome_cli/tests/cases/handle_svelte_files.rs
index 161bc3d36f9e..df5a00040be9 100644
--- a/crates/biome_cli/tests/cases/handle_svelte_files.rs
+++ b/crates/biome_cli/tests/cases/handle_svelte_files.rs
@@ -239,6 +239,9 @@ schema + sure()
"#
.as_bytes(),
diff --git a/crates/biome_cli/tests/cases/handle_vue_files.rs b/crates/biome_cli/tests/cases/handle_vue_files.rs
index af214bb3e814..9edee0a850b7 100644
--- a/crates/biome_cli/tests/cases/handle_vue_files.rs
+++ b/crates/biome_cli/tests/cases/handle_vue_files.rs
@@ -493,6 +493,12 @@ schema + sure()
"#
.as_bytes(),
diff --git a/crates/biome_cli/tests/snapshots/main_cases_handle_astro_files/full_support.snap b/crates/biome_cli/tests/snapshots/main_cases_handle_astro_files/full_support.snap
index eeebac345ccf..6ab88fae6695 100644
--- a/crates/biome_cli/tests/snapshots/main_cases_handle_astro_files/full_support.snap
+++ b/crates/biome_cli/tests/snapshots/main_cases_handle_astro_files/full_support.snap
@@ -39,6 +39,9 @@ schema + sure();
.class {
background: red;
}
+:global(div) {
+ color: red;
+}
```
@@ -79,8 +82,8 @@ file.astro:16:20 lint/a11y/useGenericFontNames ━━━━━━━━━━━
15 │
- 18 │
+ 17 │ :global(div) {
+ 18 │ color: red;
i Consider adding a generic font family as a fallback.
diff --git a/crates/biome_cli/tests/snapshots/main_cases_handle_svelte_files/full_support.snap b/crates/biome_cli/tests/snapshots/main_cases_handle_svelte_files/full_support.snap
index 23105634d1b1..8384b715c45a 100644
--- a/crates/biome_cli/tests/snapshots/main_cases_handle_svelte_files/full_support.snap
+++ b/crates/biome_cli/tests/snapshots/main_cases_handle_svelte_files/full_support.snap
@@ -39,6 +39,9 @@ schema + sure();
.class {
background: red;
}
+:global(div) {
+ color: red;
+}
```
@@ -81,8 +84,8 @@ file.svelte:12:20 lint/a11y/useGenericFontNames ━━━━━━━━━━
11 │
- 14 │
+ 13 │ :global(div) {
+ 14 │ color: red;
i Consider adding a generic font family as a fallback.
diff --git a/crates/biome_cli/tests/snapshots/main_cases_handle_vue_files/full_support.snap b/crates/biome_cli/tests/snapshots/main_cases_handle_vue_files/full_support.snap
index 96e82a597ce0..aff213e5d671 100644
--- a/crates/biome_cli/tests/snapshots/main_cases_handle_vue_files/full_support.snap
+++ b/crates/biome_cli/tests/snapshots/main_cases_handle_vue_files/full_support.snap
@@ -39,6 +39,12 @@ schema + sure();
.class {
background: red;
}
+:slotted(div) {
+ color: red;
+}
+:global(div) {
+ color: red;
+}
```
@@ -81,8 +87,8 @@ file.vue:12:20 lint/a11y/useGenericFontNames ━━━━━━━━━━━
11 │
- 14 │
+ 13 │ :slotted(div) {
+ 14 │ color: red;
i Consider adding a generic font family as a fallback.
diff --git a/crates/biome_cli/tests/snapshots/main_commands_check/check_help.snap b/crates/biome_cli/tests/snapshots/main_commands_check/check_help.snap
index c595bf424a8b..88c1eda40932 100644
--- a/crates/biome_cli/tests/snapshots/main_commands_check/check_help.snap
+++ b/crates/biome_cli/tests/snapshots/main_commands_check/check_help.snap
@@ -137,6 +137,7 @@ The configuration that is contained inside the file `biome.json`
--json-assist-enabled= Control the assist for JSON (and its super languages)
files.
--css-parse-css-modules= Enables parsing of CSS Modules specific features.
+ Enable this feature only when your files don't end in `.module.css`.
--css-parse-tailwind-directives= Enables parsing of Tailwind CSS 4.0 directives
and functions.
--css-formatter-enabled= Control the formatter for CSS (and its super
diff --git a/crates/biome_cli/tests/snapshots/main_commands_ci/ci_help.snap b/crates/biome_cli/tests/snapshots/main_commands_ci/ci_help.snap
index d734799315f7..4680476626ee 100644
--- a/crates/biome_cli/tests/snapshots/main_commands_ci/ci_help.snap
+++ b/crates/biome_cli/tests/snapshots/main_commands_ci/ci_help.snap
@@ -139,6 +139,7 @@ The configuration that is contained inside the file `biome.json`
--json-assist-enabled= Control the assist for JSON (and its super languages)
files.
--css-parse-css-modules= Enables parsing of CSS Modules specific features.
+ Enable this feature only when your files don't end in `.module.css`.
--css-parse-tailwind-directives= Enables parsing of Tailwind CSS 4.0 directives
and functions.
--css-formatter-enabled= Control the formatter for CSS (and its super
diff --git a/crates/biome_configuration/src/analyzer/linter/rules.rs b/crates/biome_configuration/src/analyzer/linter/rules.rs
index 6adc44656162..8ad9cf604c59 100644
--- a/crates/biome_configuration/src/analyzer/linter/rules.rs
+++ b/crates/biome_configuration/src/analyzer/linter/rules.rs
@@ -6382,7 +6382,7 @@ impl From for Security {
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", default, deny_unknown_fields)]
#[doc = r" A list of rules that belong to this group"]
-pub struct Style { # [doc = r" Enables the recommended rules for this group"] # [serde (skip_serializing_if = "Option::is_none")] pub recommended : Option < bool > , # [doc = "Disallow use of CommonJs module system in favor of ESM style imports.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_common_js : Option < RuleConfiguration < biome_rule_options :: no_common_js :: NoCommonJsOptions >> , # [doc = "Disallow default exports.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_default_export : Option < RuleConfiguration < biome_rule_options :: no_default_export :: NoDefaultExportOptions >> , # [doc = "Disallow a lower specificity selector from coming after a higher specificity selector.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_descending_specificity : Option < RuleConfiguration < biome_rule_options :: no_descending_specificity :: NoDescendingSpecificityOptions >> , # [doc = "Disallow using a callback in asynchronous tests and hooks.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_done_callback : Option < RuleConfiguration < biome_rule_options :: no_done_callback :: NoDoneCallbackOptions >> , # [doc = "Disallow TypeScript enum.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_enum : Option < RuleConfiguration < biome_rule_options :: no_enum :: NoEnumOptions >> , # [doc = "Disallow exporting an imported variable.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_exported_imports : Option < RuleConfiguration < biome_rule_options :: no_exported_imports :: NoExportedImportsOptions >> , # [doc = "Prevent usage of \\ element in a Next.js project.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_head_element : Option < RuleConfiguration < biome_rule_options :: no_head_element :: NoHeadElementOptions >> , # [doc = "Disallow implicit true values on JSX boolean attributes.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_implicit_boolean : Option < RuleFixConfiguration < biome_rule_options :: no_implicit_boolean :: NoImplicitBooleanOptions >> , # [doc = "Disallow type annotations for variables, parameters, and class properties initialized with a literal expression.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_inferrable_types : Option < RuleFixConfiguration < biome_rule_options :: no_inferrable_types :: NoInferrableTypesOptions >> , # [doc = "Reports usage of \"magic numbers\" — numbers used directly instead of being assigned to named constants.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_magic_numbers : Option < RuleConfiguration < biome_rule_options :: no_magic_numbers :: NoMagicNumbersOptions >> , # [doc = "Disallow the use of TypeScript's namespaces.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_namespace : Option < RuleConfiguration < biome_rule_options :: no_namespace :: NoNamespaceOptions >> , # [doc = "Disallow negation in the condition of an if statement if it has an else clause.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_negation_else : Option < RuleFixConfiguration < biome_rule_options :: no_negation_else :: NoNegationElseOptions >> , # [doc = "Disallow nested ternary expressions.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_nested_ternary : Option < RuleConfiguration < biome_rule_options :: no_nested_ternary :: NoNestedTernaryOptions >> , # [doc = "Disallow non-null assertions using the ! postfix operator.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_non_null_assertion : Option < RuleFixConfiguration < biome_rule_options :: no_non_null_assertion :: NoNonNullAssertionOptions >> , # [doc = "Disallow reassigning function parameters.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_parameter_assign : Option < RuleConfiguration < biome_rule_options :: no_parameter_assign :: NoParameterAssignOptions >> , # [doc = "Disallow the use of parameter properties in class constructors.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_parameter_properties : Option < RuleConfiguration < biome_rule_options :: no_parameter_properties :: NoParameterPropertiesOptions >> , # [doc = "Disallow the use of process.env.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_process_env : Option < RuleConfiguration < biome_rule_options :: no_process_env :: NoProcessEnvOptions >> , # [doc = "This rule allows you to specify global variable names that you don’t want to use in your application.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_restricted_globals : Option < RuleConfiguration < biome_rule_options :: no_restricted_globals :: NoRestrictedGlobalsOptions >> , # [doc = "Disallow specified modules when loaded by import or require.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_restricted_imports : Option < RuleConfiguration < biome_rule_options :: no_restricted_imports :: NoRestrictedImportsOptions >> , # [doc = "Disallow user defined types.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_restricted_types : Option < RuleFixConfiguration < biome_rule_options :: no_restricted_types :: NoRestrictedTypesOptions >> , # [doc = "Disallow the use of constants which its value is the upper-case version of its name.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_shouty_constants : Option < RuleFixConfiguration < biome_rule_options :: no_shouty_constants :: NoShoutyConstantsOptions >> , # [doc = "Enforce the use of String.slice() over String.substr() and String.substring().\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_substr : Option < RuleFixConfiguration < biome_rule_options :: no_substr :: NoSubstrOptions >> , # [doc = "Disallow template literals if interpolation and special-character handling are not needed.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_unused_template_literal : Option < RuleFixConfiguration < biome_rule_options :: no_unused_template_literal :: NoUnusedTemplateLiteralOptions >> , # [doc = "Disallow else block when the if block breaks early.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_useless_else : Option < RuleFixConfiguration < biome_rule_options :: no_useless_else :: NoUselessElseOptions >> , # [doc = "Disallow use of @value rule in css modules.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_value_at_rule : Option < RuleConfiguration < biome_rule_options :: no_value_at_rule :: NoValueAtRuleOptions >> , # [doc = "Disallow the use of yoda expressions.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_yoda_expression : Option < RuleFixConfiguration < biome_rule_options :: no_yoda_expression :: NoYodaExpressionOptions >> , # [doc = "Disallow Array constructors.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_array_literals : Option < RuleFixConfiguration < biome_rule_options :: use_array_literals :: UseArrayLiteralsOptions >> , # [doc = "Enforce the use of as const over literal type and type annotation.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_as_const_assertion : Option < RuleFixConfiguration < biome_rule_options :: use_as_const_assertion :: UseAsConstAssertionOptions >> , # [doc = "Use at() instead of integer index access.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_at_index : Option < RuleFixConfiguration < biome_rule_options :: use_at_index :: UseAtIndexOptions >> , # [doc = "Requires following curly brace conventions.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_block_statements : Option < RuleFixConfiguration < biome_rule_options :: use_block_statements :: UseBlockStatementsOptions >> , # [doc = "Enforce using else if instead of nested if in else clauses.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_collapsed_else_if : Option < RuleFixConfiguration < biome_rule_options :: use_collapsed_else_if :: UseCollapsedElseIfOptions >> , # [doc = "Enforce using single if instead of nested if clauses.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_collapsed_if : Option < RuleFixConfiguration < biome_rule_options :: use_collapsed_if :: UseCollapsedIfOptions >> , # [doc = "Enforce declaring components only within modules that export React Components exclusively.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_component_export_only_modules : Option < RuleConfiguration < biome_rule_options :: use_component_export_only_modules :: UseComponentExportOnlyModulesOptions >> , # [doc = "Require consistently using either T\\[] or Array\\.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_consistent_array_type : Option < RuleFixConfiguration < biome_rule_options :: use_consistent_array_type :: UseConsistentArrayTypeOptions >> , # [doc = "Enforce the use of new for all builtins, except String, Number and Boolean.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_consistent_builtin_instantiation : Option < RuleFixConfiguration < biome_rule_options :: use_consistent_builtin_instantiation :: UseConsistentBuiltinInstantiationOptions >> , # [doc = "This rule enforces consistent use of curly braces inside JSX attributes and JSX children.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_consistent_curly_braces : Option < RuleFixConfiguration < biome_rule_options :: use_consistent_curly_braces :: UseConsistentCurlyBracesOptions >> , # [doc = "Require consistent accessibility modifiers on class properties and methods.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_consistent_member_accessibility : Option < RuleConfiguration < biome_rule_options :: use_consistent_member_accessibility :: UseConsistentMemberAccessibilityOptions >> , # [doc = "Require the consistent declaration of object literals. Defaults to explicit definitions.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_consistent_object_definitions : Option < RuleFixConfiguration < biome_rule_options :: use_consistent_object_definitions :: UseConsistentObjectDefinitionsOptions >> , # [doc = "Enforce type definitions to consistently use either interface or type.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_consistent_type_definitions : Option < RuleFixConfiguration < biome_rule_options :: use_consistent_type_definitions :: UseConsistentTypeDefinitionsOptions >> , # [doc = "Require const declarations for variables that are only assigned once.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_const : Option < RuleFixConfiguration < biome_rule_options :: use_const :: UseConstOptions >> , # [doc = "Enforce default function parameters and optional function parameters to be last.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_default_parameter_last : Option < RuleFixConfiguration < biome_rule_options :: use_default_parameter_last :: UseDefaultParameterLastOptions >> , # [doc = "Require the default clause in switch statements.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_default_switch_clause : Option < RuleConfiguration < biome_rule_options :: use_default_switch_clause :: UseDefaultSwitchClauseOptions >> , # [doc = "Require specifying the reason argument when using @deprecated directive.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_deprecated_reason : Option < RuleConfiguration < biome_rule_options :: use_deprecated_reason :: UseDeprecatedReasonOptions >> , # [doc = "Require that each enum member value be explicitly initialized.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_enum_initializers : Option < RuleFixConfiguration < biome_rule_options :: use_enum_initializers :: UseEnumInitializersOptions >> , # [doc = "Enforce explicitly comparing the length, size, byteLength or byteOffset property of a value.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_explicit_length_check : Option < RuleFixConfiguration < biome_rule_options :: use_explicit_length_check :: UseExplicitLengthCheckOptions >> , # [doc = "Disallow the use of Math.pow in favor of the ** operator.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_exponentiation_operator : Option < RuleFixConfiguration < biome_rule_options :: use_exponentiation_operator :: UseExponentiationOperatorOptions >> , # [doc = "Promotes the use of export type for types.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_export_type : Option < RuleFixConfiguration < biome_rule_options :: use_export_type :: UseExportTypeOptions >> , # [doc = "Require that all exports are declared after all non-export statements.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_exports_last : Option < RuleConfiguration < biome_rule_options :: use_exports_last :: UseExportsLastOptions >> , # [doc = "Enforce naming conventions for JavaScript and TypeScript filenames.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_filenaming_convention : Option < RuleConfiguration < biome_rule_options :: use_filenaming_convention :: UseFilenamingConventionOptions >> , # [doc = "Prefer using for...of loops over standard for loops where possible.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_for_of : Option < RuleConfiguration < biome_rule_options :: use_for_of :: UseForOfOptions >> , # [doc = "This rule enforces the use of \\<>...\\> over \\...\\.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_fragment_syntax : Option < RuleFixConfiguration < biome_rule_options :: use_fragment_syntax :: UseFragmentSyntaxOptions >> , # [doc = "Validates that all enum values are capitalized.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_graphql_naming_convention : Option < RuleConfiguration < biome_rule_options :: use_graphql_naming_convention :: UseGraphqlNamingConventionOptions >> , # [doc = "Enforce that getters and setters for the same property are adjacent in class and object definitions.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_grouped_accessor_pairs : Option < RuleConfiguration < biome_rule_options :: use_grouped_accessor_pairs :: UseGroupedAccessorPairsOptions >> , # [doc = "Promotes the use of import type for types.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_import_type : Option < RuleFixConfiguration < biome_rule_options :: use_import_type :: UseImportTypeOptions >> , # [doc = "Require all enum members to be literal values.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_literal_enum_members : Option < RuleConfiguration < biome_rule_options :: use_literal_enum_members :: UseLiteralEnumMembersOptions >> , # [doc = "Enforce naming conventions for everything across a codebase.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_naming_convention : Option < RuleFixConfiguration < biome_rule_options :: use_naming_convention :: UseNamingConventionOptions >> , # [doc = "Promotes the usage of node:assert/strict over node:assert.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_node_assert_strict : Option < RuleFixConfiguration < biome_rule_options :: use_node_assert_strict :: UseNodeAssertStrictOptions >> , # [doc = "Enforces using the node: protocol for Node.js builtin modules.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_nodejs_import_protocol : Option < RuleFixConfiguration < biome_rule_options :: use_nodejs_import_protocol :: UseNodejsImportProtocolOptions >> , # [doc = "Use the Number properties instead of global ones.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_number_namespace : Option < RuleFixConfiguration < biome_rule_options :: use_number_namespace :: UseNumberNamespaceOptions >> , # [doc = "Enforce the use of numeric separators in numeric literals.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_numeric_separators : Option < RuleFixConfiguration < biome_rule_options :: use_numeric_separators :: UseNumericSeparatorsOptions >> , # [doc = "Prefer object spread over Object.assign() when constructing new objects.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_object_spread : Option < RuleFixConfiguration < biome_rule_options :: use_object_spread :: UseObjectSpreadOptions >> , # [doc = "Enforce that components are defined as functions and never as classes.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_react_function_components : Option < RuleConfiguration < biome_rule_options :: use_react_function_components :: UseReactFunctionComponentsOptions >> , # [doc = "Enforce marking members as readonly if they are never modified outside the constructor.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_readonly_class_properties : Option < RuleFixConfiguration < biome_rule_options :: use_readonly_class_properties :: UseReadonlyClassPropertiesOptions >> , # [doc = "Prevent extra closing tags for components without children.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_self_closing_elements : Option < RuleFixConfiguration < biome_rule_options :: use_self_closing_elements :: UseSelfClosingElementsOptions >> , # [doc = "Require assignment operator shorthand where possible.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_shorthand_assign : Option < RuleFixConfiguration < biome_rule_options :: use_shorthand_assign :: UseShorthandAssignOptions >> , # [doc = "Enforce using function types instead of object type with call signatures.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_shorthand_function_type : Option < RuleFixConfiguration < biome_rule_options :: use_shorthand_function_type :: UseShorthandFunctionTypeOptions >> , # [doc = "Disallow multiple variable declarations in the same variable statement.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_single_var_declarator : Option < RuleFixConfiguration < biome_rule_options :: use_single_var_declarator :: UseSingleVarDeclaratorOptions >> , # [doc = "Require a description parameter for the Symbol().\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_symbol_description : Option < RuleConfiguration < biome_rule_options :: use_symbol_description :: UseSymbolDescriptionOptions >> , # [doc = "Prefer template literals over string concatenation.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_template : Option < RuleFixConfiguration < biome_rule_options :: use_template :: UseTemplateOptions >> , # [doc = "Require new when throwing an error.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_throw_new_error : Option < RuleFixConfiguration < biome_rule_options :: use_throw_new_error :: UseThrowNewErrorOptions >> , # [doc = "Disallow throwing non-Error values.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_throw_only_error : Option < RuleConfiguration < biome_rule_options :: use_throw_only_error :: UseThrowOnlyErrorOptions >> , # [doc = "Enforce the use of String.trimStart() and String.trimEnd() over String.trimLeft() and String.trimRight().\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_trim_start_end : Option < RuleFixConfiguration < biome_rule_options :: use_trim_start_end :: UseTrimStartEndOptions >> , # [doc = "Disallow overload signatures that can be unified into a single signature.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_unified_type_signatures : Option < RuleFixConfiguration < biome_rule_options :: use_unified_type_signatures :: UseUnifiedTypeSignaturesOptions >> }
+pub struct Style { # [doc = r" Enables the recommended rules for this group"] # [serde (skip_serializing_if = "Option::is_none")] pub recommended : Option < bool > , # [doc = "Disallow use of CommonJs module system in favor of ESM style imports.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_common_js : Option < RuleConfiguration < biome_rule_options :: no_common_js :: NoCommonJsOptions >> , # [doc = "Disallow default exports.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_default_export : Option < RuleConfiguration < biome_rule_options :: no_default_export :: NoDefaultExportOptions >> , # [doc = "Disallow a lower specificity selector from coming after a higher specificity selector.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_descending_specificity : Option < RuleConfiguration < biome_rule_options :: no_descending_specificity :: NoDescendingSpecificityOptions >> , # [doc = "Disallow using a callback in asynchronous tests and hooks.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_done_callback : Option < RuleConfiguration < biome_rule_options :: no_done_callback :: NoDoneCallbackOptions >> , # [doc = "Disallow TypeScript enum.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_enum : Option < RuleConfiguration < biome_rule_options :: no_enum :: NoEnumOptions >> , # [doc = "Disallow exporting an imported variable.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_exported_imports : Option < RuleConfiguration < biome_rule_options :: no_exported_imports :: NoExportedImportsOptions >> , # [doc = "Prevent usage of \\ element in a Next.js project.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_head_element : Option < RuleConfiguration < biome_rule_options :: no_head_element :: NoHeadElementOptions >> , # [doc = "Disallow implicit true values on JSX boolean attributes.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_implicit_boolean : Option < RuleFixConfiguration < biome_rule_options :: no_implicit_boolean :: NoImplicitBooleanOptions >> , # [doc = "Disallow type annotations for variables, parameters, and class properties initialized with a literal expression.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_inferrable_types : Option < RuleFixConfiguration < biome_rule_options :: no_inferrable_types :: NoInferrableTypesOptions >> , # [doc = "Reports usage of \"magic numbers\" — numbers used directly instead of being assigned to named constants.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_magic_numbers : Option < RuleConfiguration < biome_rule_options :: no_magic_numbers :: NoMagicNumbersOptions >> , # [doc = "Disallow the use of TypeScript's namespaces.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_namespace : Option < RuleConfiguration < biome_rule_options :: no_namespace :: NoNamespaceOptions >> , # [doc = "Disallow negation in the condition of an if statement if it has an else clause.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_negation_else : Option < RuleFixConfiguration < biome_rule_options :: no_negation_else :: NoNegationElseOptions >> , # [doc = "Disallow nested ternary expressions.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_nested_ternary : Option < RuleConfiguration < biome_rule_options :: no_nested_ternary :: NoNestedTernaryOptions >> , # [doc = "Disallow non-null assertions using the ! postfix operator.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_non_null_assertion : Option < RuleFixConfiguration < biome_rule_options :: no_non_null_assertion :: NoNonNullAssertionOptions >> , # [doc = "Disallow reassigning function parameters.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_parameter_assign : Option < RuleConfiguration < biome_rule_options :: no_parameter_assign :: NoParameterAssignOptions >> , # [doc = "Disallow the use of parameter properties in class constructors.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_parameter_properties : Option < RuleConfiguration < biome_rule_options :: no_parameter_properties :: NoParameterPropertiesOptions >> , # [doc = "Disallow the use of process.env.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_process_env : Option < RuleConfiguration < biome_rule_options :: no_process_env :: NoProcessEnvOptions >> , # [doc = "This rule allows you to specify global variable names that you don’t want to use in your application.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_restricted_globals : Option < RuleConfiguration < biome_rule_options :: no_restricted_globals :: NoRestrictedGlobalsOptions >> , # [doc = "Disallow specified modules when loaded by import or require.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_restricted_imports : Option < RuleConfiguration < biome_rule_options :: no_restricted_imports :: NoRestrictedImportsOptions >> , # [doc = "Disallow user defined types.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_restricted_types : Option < RuleFixConfiguration < biome_rule_options :: no_restricted_types :: NoRestrictedTypesOptions >> , # [doc = "Disallow the use of constants which its value is the upper-case version of its name.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_shouty_constants : Option < RuleFixConfiguration < biome_rule_options :: no_shouty_constants :: NoShoutyConstantsOptions >> , # [doc = "Enforce the use of String.slice() over String.substr() and String.substring().\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_substr : Option < RuleFixConfiguration < biome_rule_options :: no_substr :: NoSubstrOptions >> , # [doc = "Disallow template literals if interpolation and special-character handling are not needed.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_unused_template_literal : Option < RuleFixConfiguration < biome_rule_options :: no_unused_template_literal :: NoUnusedTemplateLiteralOptions >> , # [doc = "Disallow else block when the if block breaks early.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_useless_else : Option < RuleFixConfiguration < biome_rule_options :: no_useless_else :: NoUselessElseOptions >> , # [doc = "Disallow use of @value rule in CSS modules.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_value_at_rule : Option < RuleConfiguration < biome_rule_options :: no_value_at_rule :: NoValueAtRuleOptions >> , # [doc = "Disallow the use of yoda expressions.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub no_yoda_expression : Option < RuleFixConfiguration < biome_rule_options :: no_yoda_expression :: NoYodaExpressionOptions >> , # [doc = "Disallow Array constructors.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_array_literals : Option < RuleFixConfiguration < biome_rule_options :: use_array_literals :: UseArrayLiteralsOptions >> , # [doc = "Enforce the use of as const over literal type and type annotation.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_as_const_assertion : Option < RuleFixConfiguration < biome_rule_options :: use_as_const_assertion :: UseAsConstAssertionOptions >> , # [doc = "Use at() instead of integer index access.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_at_index : Option < RuleFixConfiguration < biome_rule_options :: use_at_index :: UseAtIndexOptions >> , # [doc = "Requires following curly brace conventions.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_block_statements : Option < RuleFixConfiguration < biome_rule_options :: use_block_statements :: UseBlockStatementsOptions >> , # [doc = "Enforce using else if instead of nested if in else clauses.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_collapsed_else_if : Option < RuleFixConfiguration < biome_rule_options :: use_collapsed_else_if :: UseCollapsedElseIfOptions >> , # [doc = "Enforce using single if instead of nested if clauses.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_collapsed_if : Option < RuleFixConfiguration < biome_rule_options :: use_collapsed_if :: UseCollapsedIfOptions >> , # [doc = "Enforce declaring components only within modules that export React Components exclusively.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_component_export_only_modules : Option < RuleConfiguration < biome_rule_options :: use_component_export_only_modules :: UseComponentExportOnlyModulesOptions >> , # [doc = "Require consistently using either T\\[] or Array\\.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_consistent_array_type : Option < RuleFixConfiguration < biome_rule_options :: use_consistent_array_type :: UseConsistentArrayTypeOptions >> , # [doc = "Enforce the use of new for all builtins, except String, Number and Boolean.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_consistent_builtin_instantiation : Option < RuleFixConfiguration < biome_rule_options :: use_consistent_builtin_instantiation :: UseConsistentBuiltinInstantiationOptions >> , # [doc = "This rule enforces consistent use of curly braces inside JSX attributes and JSX children.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_consistent_curly_braces : Option < RuleFixConfiguration < biome_rule_options :: use_consistent_curly_braces :: UseConsistentCurlyBracesOptions >> , # [doc = "Require consistent accessibility modifiers on class properties and methods.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_consistent_member_accessibility : Option < RuleConfiguration < biome_rule_options :: use_consistent_member_accessibility :: UseConsistentMemberAccessibilityOptions >> , # [doc = "Require the consistent declaration of object literals. Defaults to explicit definitions.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_consistent_object_definitions : Option < RuleFixConfiguration < biome_rule_options :: use_consistent_object_definitions :: UseConsistentObjectDefinitionsOptions >> , # [doc = "Enforce type definitions to consistently use either interface or type.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_consistent_type_definitions : Option < RuleFixConfiguration < biome_rule_options :: use_consistent_type_definitions :: UseConsistentTypeDefinitionsOptions >> , # [doc = "Require const declarations for variables that are only assigned once.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_const : Option < RuleFixConfiguration < biome_rule_options :: use_const :: UseConstOptions >> , # [doc = "Enforce default function parameters and optional function parameters to be last.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_default_parameter_last : Option < RuleFixConfiguration < biome_rule_options :: use_default_parameter_last :: UseDefaultParameterLastOptions >> , # [doc = "Require the default clause in switch statements.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_default_switch_clause : Option < RuleConfiguration < biome_rule_options :: use_default_switch_clause :: UseDefaultSwitchClauseOptions >> , # [doc = "Require specifying the reason argument when using @deprecated directive.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_deprecated_reason : Option < RuleConfiguration < biome_rule_options :: use_deprecated_reason :: UseDeprecatedReasonOptions >> , # [doc = "Require that each enum member value be explicitly initialized.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_enum_initializers : Option < RuleFixConfiguration < biome_rule_options :: use_enum_initializers :: UseEnumInitializersOptions >> , # [doc = "Enforce explicitly comparing the length, size, byteLength or byteOffset property of a value.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_explicit_length_check : Option < RuleFixConfiguration < biome_rule_options :: use_explicit_length_check :: UseExplicitLengthCheckOptions >> , # [doc = "Disallow the use of Math.pow in favor of the ** operator.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_exponentiation_operator : Option < RuleFixConfiguration < biome_rule_options :: use_exponentiation_operator :: UseExponentiationOperatorOptions >> , # [doc = "Promotes the use of export type for types.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_export_type : Option < RuleFixConfiguration < biome_rule_options :: use_export_type :: UseExportTypeOptions >> , # [doc = "Require that all exports are declared after all non-export statements.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_exports_last : Option < RuleConfiguration < biome_rule_options :: use_exports_last :: UseExportsLastOptions >> , # [doc = "Enforce naming conventions for JavaScript and TypeScript filenames.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_filenaming_convention : Option < RuleConfiguration < biome_rule_options :: use_filenaming_convention :: UseFilenamingConventionOptions >> , # [doc = "Prefer using for...of loops over standard for loops where possible.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_for_of : Option < RuleConfiguration < biome_rule_options :: use_for_of :: UseForOfOptions >> , # [doc = "This rule enforces the use of \\<>...\\> over \\...\\.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_fragment_syntax : Option < RuleFixConfiguration < biome_rule_options :: use_fragment_syntax :: UseFragmentSyntaxOptions >> , # [doc = "Validates that all enum values are capitalized.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_graphql_naming_convention : Option < RuleConfiguration < biome_rule_options :: use_graphql_naming_convention :: UseGraphqlNamingConventionOptions >> , # [doc = "Enforce that getters and setters for the same property are adjacent in class and object definitions.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_grouped_accessor_pairs : Option < RuleConfiguration < biome_rule_options :: use_grouped_accessor_pairs :: UseGroupedAccessorPairsOptions >> , # [doc = "Promotes the use of import type for types.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_import_type : Option < RuleFixConfiguration < biome_rule_options :: use_import_type :: UseImportTypeOptions >> , # [doc = "Require all enum members to be literal values.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_literal_enum_members : Option < RuleConfiguration < biome_rule_options :: use_literal_enum_members :: UseLiteralEnumMembersOptions >> , # [doc = "Enforce naming conventions for everything across a codebase.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_naming_convention : Option < RuleFixConfiguration < biome_rule_options :: use_naming_convention :: UseNamingConventionOptions >> , # [doc = "Promotes the usage of node:assert/strict over node:assert.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_node_assert_strict : Option < RuleFixConfiguration < biome_rule_options :: use_node_assert_strict :: UseNodeAssertStrictOptions >> , # [doc = "Enforces using the node: protocol for Node.js builtin modules.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_nodejs_import_protocol : Option < RuleFixConfiguration < biome_rule_options :: use_nodejs_import_protocol :: UseNodejsImportProtocolOptions >> , # [doc = "Use the Number properties instead of global ones.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_number_namespace : Option < RuleFixConfiguration < biome_rule_options :: use_number_namespace :: UseNumberNamespaceOptions >> , # [doc = "Enforce the use of numeric separators in numeric literals.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_numeric_separators : Option < RuleFixConfiguration < biome_rule_options :: use_numeric_separators :: UseNumericSeparatorsOptions >> , # [doc = "Prefer object spread over Object.assign() when constructing new objects.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_object_spread : Option < RuleFixConfiguration < biome_rule_options :: use_object_spread :: UseObjectSpreadOptions >> , # [doc = "Enforce that components are defined as functions and never as classes.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_react_function_components : Option < RuleConfiguration < biome_rule_options :: use_react_function_components :: UseReactFunctionComponentsOptions >> , # [doc = "Enforce marking members as readonly if they are never modified outside the constructor.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_readonly_class_properties : Option < RuleFixConfiguration < biome_rule_options :: use_readonly_class_properties :: UseReadonlyClassPropertiesOptions >> , # [doc = "Prevent extra closing tags for components without children.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_self_closing_elements : Option < RuleFixConfiguration < biome_rule_options :: use_self_closing_elements :: UseSelfClosingElementsOptions >> , # [doc = "Require assignment operator shorthand where possible.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_shorthand_assign : Option < RuleFixConfiguration < biome_rule_options :: use_shorthand_assign :: UseShorthandAssignOptions >> , # [doc = "Enforce using function types instead of object type with call signatures.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_shorthand_function_type : Option < RuleFixConfiguration < biome_rule_options :: use_shorthand_function_type :: UseShorthandFunctionTypeOptions >> , # [doc = "Disallow multiple variable declarations in the same variable statement.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_single_var_declarator : Option < RuleFixConfiguration < biome_rule_options :: use_single_var_declarator :: UseSingleVarDeclaratorOptions >> , # [doc = "Require a description parameter for the Symbol().\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_symbol_description : Option < RuleConfiguration < biome_rule_options :: use_symbol_description :: UseSymbolDescriptionOptions >> , # [doc = "Prefer template literals over string concatenation.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_template : Option < RuleFixConfiguration < biome_rule_options :: use_template :: UseTemplateOptions >> , # [doc = "Require new when throwing an error.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_throw_new_error : Option < RuleFixConfiguration < biome_rule_options :: use_throw_new_error :: UseThrowNewErrorOptions >> , # [doc = "Disallow throwing non-Error values.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_throw_only_error : Option < RuleConfiguration < biome_rule_options :: use_throw_only_error :: UseThrowOnlyErrorOptions >> , # [doc = "Enforce the use of String.trimStart() and String.trimEnd() over String.trimLeft() and String.trimRight().\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_trim_start_end : Option < RuleFixConfiguration < biome_rule_options :: use_trim_start_end :: UseTrimStartEndOptions >> , # [doc = "Disallow overload signatures that can be unified into a single signature.\nSee "] # [serde (skip_serializing_if = "Option::is_none")] pub use_unified_type_signatures : Option < RuleFixConfiguration < biome_rule_options :: use_unified_type_signatures :: UseUnifiedTypeSignaturesOptions >> }
impl Style {
const GROUP_NAME: &'static str = "style";
pub(crate) const GROUP_RULES: &'static [&'static str] = &[
diff --git a/crates/biome_configuration/src/css.rs b/crates/biome_configuration/src/css.rs
index 0d5cabee9cd6..b179bcdf8757 100644
--- a/crates/biome_configuration/src/css.rs
+++ b/crates/biome_configuration/src/css.rs
@@ -49,7 +49,8 @@ pub struct CssParserConfiguration {
#[serde(skip_serializing_if = "Option::is_none")]
pub allow_wrong_line_comments: Option,
- /// Enables parsing of CSS Modules specific features.
+ /// Enables parsing of CSS Modules specific features. Enable this feature only
+ /// when your files don't end in `.module.css`.
#[serde(skip_serializing_if = "Option::is_none")]
#[bpaf(long("css-parse-css-modules"), argument("true|false"))]
pub css_modules: Option,
diff --git a/crates/biome_css_analyze/src/lint/correctness/no_unknown_pseudo_class.rs b/crates/biome_css_analyze/src/lint/correctness/no_unknown_pseudo_class.rs
index 828f031a8731..73552fe14c50 100644
--- a/crates/biome_css_analyze/src/lint/correctness/no_unknown_pseudo_class.rs
+++ b/crates/biome_css_analyze/src/lint/correctness/no_unknown_pseudo_class.rs
@@ -10,12 +10,12 @@ use biome_analyze::{
};
use biome_console::markup;
use biome_css_syntax::{
- CssBogusPseudoClass, CssPageSelectorPseudo, CssPseudoClassFunctionCompoundSelector,
- CssPseudoClassFunctionCompoundSelectorList, CssPseudoClassFunctionIdentifier,
- CssPseudoClassFunctionNth, CssPseudoClassFunctionRelativeSelectorList,
- CssPseudoClassFunctionSelector, CssPseudoClassFunctionSelectorList,
- CssPseudoClassFunctionValueList, CssPseudoClassIdentifier, CssPseudoElementSelector,
- CssSyntaxToken,
+ CssBogusPseudoClass, CssFileSource, CssPageSelectorPseudo,
+ CssPseudoClassFunctionCompoundSelector, CssPseudoClassFunctionCompoundSelectorList,
+ CssPseudoClassFunctionIdentifier, CssPseudoClassFunctionNth,
+ CssPseudoClassFunctionRelativeSelectorList, CssPseudoClassFunctionSelector,
+ CssPseudoClassFunctionSelectorList, CssPseudoClassFunctionValueList, CssPseudoClassIdentifier,
+ CssPseudoElementSelector, CssSyntaxToken,
};
use biome_diagnostics::Severity;
use biome_rowan::{AstNode, TextRange, declare_node_union};
@@ -146,9 +146,9 @@ impl Rule for NoUnknownPseudoClass {
fn run(ctx: &RuleContext) -> Option {
let pseudo_class = ctx.query();
- let is_css_modules = ctx.is_css_modules();
let span = pseudo_class.name_range()?;
let name = pseudo_class.name()?;
+ let file_source = ctx.source_type::();
let pseudo_type = match &pseudo_class {
AnyPseudoLike::CssPageSelectorPseudo(_) => PseudoClassType::PagePseudoClass,
@@ -177,7 +177,8 @@ impl Rule for NoUnknownPseudoClass {
}
};
- if is_valid_class || is_css_modules && is_css_module_pseudo_class(lower_name) {
+ if is_valid_class || file_source.is_css_modules() && is_css_module_pseudo_class(lower_name)
+ {
None
} else {
Some(NoUnknownPseudoClassSelectorState {
diff --git a/crates/biome_css_analyze/src/lint/correctness/no_unknown_pseudo_element.rs b/crates/biome_css_analyze/src/lint/correctness/no_unknown_pseudo_element.rs
index f4a1aa34099e..d2872aa6c107 100644
--- a/crates/biome_css_analyze/src/lint/correctness/no_unknown_pseudo_element.rs
+++ b/crates/biome_css_analyze/src/lint/correctness/no_unknown_pseudo_element.rs
@@ -2,7 +2,7 @@ use biome_analyze::{
Ast, Rule, RuleDiagnostic, RuleSource, context::RuleContext, declare_lint_rule,
};
use biome_console::markup;
-use biome_css_syntax::{AnyCssPseudoElement, CssPseudoElementSelector};
+use biome_css_syntax::{AnyCssPseudoElement, CssFileSource, CssPseudoElementSelector};
use biome_diagnostics::Severity;
use biome_rowan::AstNode;
use biome_rule_options::no_unknown_pseudo_element::NoUnknownPseudoElementOptions;
@@ -70,22 +70,23 @@ impl Rule for NoUnknownPseudoElement {
fn run(ctx: &RuleContext) -> Option {
let node: &CssPseudoElementSelector = ctx.query();
let pseudo_element = node.element().ok()?;
+ let file_source = ctx.source_type::();
let should_not_trigger = match &pseudo_element {
AnyCssPseudoElement::CssBogusPseudoElement(element) => {
- should_not_trigger(element.to_trimmed_text().text())
+ should_not_trigger(element.to_trimmed_text().text(), file_source)
}
AnyCssPseudoElement::CssPseudoElementFunctionCustomIdentifier(ident) => {
- should_not_trigger(ident.name().ok()?.to_trimmed_text().text())
+ should_not_trigger(ident.name().ok()?.to_trimmed_text().text(), file_source)
}
AnyCssPseudoElement::CssPseudoElementFunctionSelector(selector) => {
- should_not_trigger(selector.name().ok()?.to_trimmed_text().text())
+ should_not_trigger(selector.name().ok()?.to_trimmed_text().text(), file_source)
}
AnyCssPseudoElement::CssPseudoElementIdentifier(ident) => {
- should_not_trigger(ident.name().ok()?.to_trimmed_text().text())
+ should_not_trigger(ident.name().ok()?.to_trimmed_text().text(), file_source)
}
AnyCssPseudoElement::CssPseudoElementFunction(ident) => {
- should_not_trigger(ident.name().ok()?.to_trimmed_text().text())
+ should_not_trigger(ident.name().ok()?.to_trimmed_text().text(), file_source)
}
};
@@ -120,7 +121,12 @@ impl Rule for NoUnknownPseudoElement {
}
/// It doesn't trigger the rule if the pseudo-element name isn't a vendor prefix or is a pseudo-element
-fn should_not_trigger(pseudo_element_name: &str) -> bool {
+fn should_not_trigger(pseudo_element_name: &str, file_source: &CssFileSource) -> bool {
+ if file_source.is_css_modules() {
+ return ["global", "local"]
+ .contains(&pseudo_element_name.to_ascii_lowercase_cow().as_ref());
+ }
+
!vender_prefix(pseudo_element_name).is_empty()
|| is_pseudo_elements(pseudo_element_name.to_ascii_lowercase_cow().as_ref())
}
diff --git a/crates/biome_css_analyze/src/lint/style/no_value_at_rule.rs b/crates/biome_css_analyze/src/lint/style/no_value_at_rule.rs
index e1c766bb7226..ee72882b63f7 100644
--- a/crates/biome_css_analyze/src/lint/style/no_value_at_rule.rs
+++ b/crates/biome_css_analyze/src/lint/style/no_value_at_rule.rs
@@ -1,12 +1,12 @@
use biome_analyze::{Ast, Rule, RuleDiagnostic, context::RuleContext, declare_lint_rule};
use biome_console::markup;
-use biome_css_syntax::CssAtRule;
+use biome_css_syntax::{CssAtRule, CssFileSource};
use biome_diagnostics::Severity;
use biome_rowan::AstNode;
use biome_rule_options::no_value_at_rule::NoValueAtRuleOptions;
declare_lint_rule! {
- /// Disallow use of `@value` rule in css modules.
+ /// Disallow use of `@value` rule in CSS modules.
///
/// Use of CSS variables is recommended instead of `@value` rule.
///
@@ -14,13 +14,13 @@ declare_lint_rule! {
///
/// ### Invalid
///
- /// ```css,expect_diagnostic
+ /// ```css,expect_diagnostic,file=example.module.css
/// @value red: #FF0000;
/// ```
///
/// ### Valid
///
- /// ```css
+ /// ```css,file=example.module.css
/// :root {
/// --red: #FF0000
/// }
@@ -47,8 +47,9 @@ impl Rule for NoValueAtRule {
fn run(ctx: &RuleContext) -> Option {
let node = ctx.query();
+ let file_source = ctx.source_type::();
- if node.rule().ok()?.as_css_value_at_rule().is_some() {
+ if node.rule().ok()?.as_css_value_at_rule().is_some() && file_source.is_css_modules() {
return Some(node.clone());
}
diff --git a/crates/biome_css_analyze/tests/spec_tests.rs b/crates/biome_css_analyze/tests/spec_tests.rs
index db41006b42ec..0e9a9d5663c4 100644
--- a/crates/biome_css_analyze/tests/spec_tests.rs
+++ b/crates/biome_css_analyze/tests/spec_tests.rs
@@ -11,7 +11,7 @@ use biome_plugin_loader::AnalyzerGritPlugin;
use biome_rowan::AstNode;
use biome_test_utils::{
CheckActionType, assert_diagnostics_expectation_comment, assert_errors_are_absent,
- code_fix_to_string, create_analyzer_options, diagnostic_to_string,
+ code_fix_to_string, create_analyzer_options, create_parser_options, diagnostic_to_string,
has_bogus_nodes_or_empty_slots, parse_test_path, register_leak_checker, scripts_from_json,
write_analyzer_snapshot,
};
@@ -53,23 +53,8 @@ fn run_test(input: &'static str, _: &str, _: &str, _: &str) {
let mut snapshot = String::new();
let extension = input_file.extension().unwrap_or_default();
-
- let parser_options = if file_name.ends_with(".module.css") {
- CssParserOptions {
- css_modules: true,
- ..CssParserOptions::default()
- }
- } else if file_name.ends_with(".tailwind.css") {
- // HACK: Our infra doesn't support loading parser options from test files yet,
- // so we hardcode enabling tailwind directives for files named *.tailwind.css
- CssParserOptions {
- tailwind_directives: true,
- ..CssParserOptions::default()
- }
- } else {
- CssParserOptions::default()
- };
-
+ let mut diagnostics = vec![];
+ let parser_options = create_parser_options::(input_file, &mut diagnostics);
let input_code = read_to_string(input_file)
.unwrap_or_else(|err| panic!("failed to read {input_file:?}: {err:?}"));
@@ -83,14 +68,20 @@ fn run_test(input: &'static str, _: &str, _: &str, _: &str) {
file_name,
input_file,
CheckActionType::Lint,
- parser_options,
+ parser_options.unwrap_or_default(),
&[],
);
}
} else {
- let Ok(source_type) = input_file.try_into() else {
+ let Ok(mut source_type): Result = input_file.try_into() else {
return;
};
+
+ let parser_options = parser_options.unwrap_or(CssParserOptions::from(&source_type));
+
+ if parser_options.tailwind_directives {
+ source_type = source_type.with_tailwind_directives()
+ }
analyze_and_snap(
&mut snapshot,
&input_code,
@@ -127,12 +118,6 @@ pub(crate) fn analyze_and_snap(
let mut diagnostics = Vec::new();
let options = create_analyzer_options::(input_file, &mut diagnostics);
- let parser_options = if options.css_modules() {
- parser_options.allow_css_modules()
- } else {
- parser_options
- };
-
let parsed = parse_css(input_code, source_type, parser_options);
let root = parsed.tree();
diff --git a/crates/biome_css_analyze/tests/specs/correctness/noUnknownProperty/valid.tailwind.options.json b/crates/biome_css_analyze/tests/specs/correctness/noUnknownProperty/valid.tailwind.options.json
new file mode 100644
index 000000000000..de136233942f
--- /dev/null
+++ b/crates/biome_css_analyze/tests/specs/correctness/noUnknownProperty/valid.tailwind.options.json
@@ -0,0 +1,8 @@
+{
+ "$schema": "../../../../../../packages/@biomejs/biome/configuration_schema.json",
+ "css": {
+ "parser": {
+ "tailwindDirectives": true
+ }
+ }
+}
diff --git a/crates/biome_css_analyze/tests/specs/correctness/noUnknownPseudoClass/validGlobal.css b/crates/biome_css_analyze/tests/specs/correctness/noUnknownPseudoClass/validGlobal.module.css
similarity index 100%
rename from crates/biome_css_analyze/tests/specs/correctness/noUnknownPseudoClass/validGlobal.css
rename to crates/biome_css_analyze/tests/specs/correctness/noUnknownPseudoClass/validGlobal.module.css
diff --git a/crates/biome_css_analyze/tests/specs/correctness/noUnknownPseudoClass/validGlobal.css.snap b/crates/biome_css_analyze/tests/specs/correctness/noUnknownPseudoClass/validGlobal.module.css.snap
similarity index 89%
rename from crates/biome_css_analyze/tests/specs/correctness/noUnknownPseudoClass/validGlobal.css.snap
rename to crates/biome_css_analyze/tests/specs/correctness/noUnknownPseudoClass/validGlobal.module.css.snap
index 98e3a70e5132..c1f6b4fd5764 100644
--- a/crates/biome_css_analyze/tests/specs/correctness/noUnknownPseudoClass/validGlobal.css.snap
+++ b/crates/biome_css_analyze/tests/specs/correctness/noUnknownPseudoClass/validGlobal.module.css.snap
@@ -1,6 +1,6 @@
---
source: crates/biome_css_analyze/tests/spec_tests.rs
-expression: validGlobal.css
+expression: validGlobal.module.css
---
# Input
```css
diff --git a/crates/biome_css_analyze/tests/specs/correctness/noUnknownPseudoClass/validGlobal.options.json b/crates/biome_css_analyze/tests/specs/correctness/noUnknownPseudoClass/validGlobal.options.json
deleted file mode 100644
index aa3813c03861..000000000000
--- a/crates/biome_css_analyze/tests/specs/correctness/noUnknownPseudoClass/validGlobal.options.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "css": {
- "parser": {
- "cssModules": true
- }
- }
-}
diff --git a/crates/biome_css_parser/src/lexer/mod.rs b/crates/biome_css_parser/src/lexer/mod.rs
index f30e2b69efdc..c5f2d35f5dab 100644
--- a/crates/biome_css_parser/src/lexer/mod.rs
+++ b/crates/biome_css_parser/src/lexer/mod.rs
@@ -749,6 +749,8 @@ impl<'src> CssLexer<'src> {
b"dir" => DIR_KW,
b"global" => GLOBAL_KW,
b"local" => LOCAL_KW,
+ b"slotted" => SLOTTED_KW,
+ b"deep" => DEEP_KW,
b"-moz-any" => ANY_KW,
b"-webkit-any" => ANY_KW,
b"past" => PAST_KW,
diff --git a/crates/biome_css_parser/src/lib.rs b/crates/biome_css_parser/src/lib.rs
index d6deab9f5979..9bd9e80ba660 100644
--- a/crates/biome_css_parser/src/lib.rs
+++ b/crates/biome_css_parser/src/lib.rs
@@ -9,7 +9,7 @@ use biome_css_syntax::{AnyCssRoot, CssFileSource, CssLanguage, CssSyntaxNode};
pub use biome_parser::prelude::*;
use biome_parser::{AnyParse, EmbeddedNodeParse, NodeParse};
use biome_rowan::{AstNode, NodeCache, SyntaxNodeWithOffset};
-pub use parser::CssParserOptions;
+pub use parser::{CssModulesKind, CssParserOptions};
mod lexer;
mod parser;
diff --git a/crates/biome_css_parser/src/parser.rs b/crates/biome_css_parser/src/parser.rs
index 31ce17e169bc..33a216053297 100644
--- a/crates/biome_css_parser/src/parser.rs
+++ b/crates/biome_css_parser/src/parser.rs
@@ -29,7 +29,7 @@ pub struct CssParserOptions {
/// Enables parsing of CSS Modules specific features.
/// Defaults to `false`.
- pub css_modules: bool,
+ pub css_modules: CssModulesKind,
/// Enables parsing of Grit metavariables.
/// Defaults to `false`.
@@ -40,6 +40,16 @@ pub struct CssParserOptions {
pub tailwind_directives: bool,
}
+#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
+pub enum CssModulesKind {
+ #[default]
+ None,
+ /// The classic CSS modules, which enable things like `:local` and `:global`
+ Classic,
+ /// Enhanced version of CSS modules, which supports `:deep` and `:slotted` too.
+ Vue,
+}
+
impl CssParserOptions {
/// Allows the parser to parse wrong line comments.
pub fn allow_wrong_line_comments(mut self) -> Self {
@@ -49,7 +59,7 @@ impl CssParserOptions {
/// Enables parsing of css modules selectors.
pub fn allow_css_modules(mut self) -> Self {
- self.css_modules = true;
+ self.css_modules = CssModulesKind::Classic;
self
}
@@ -67,7 +77,7 @@ impl CssParserOptions {
/// Checks if parsing of CSS Modules features is disabled.
pub fn is_css_modules_disabled(&self) -> bool {
- !self.css_modules
+ !self.is_css_modules_enabled()
}
/// Checks if parsing of Grit metavariables is enabled.
@@ -79,6 +89,17 @@ impl CssParserOptions {
pub fn is_tailwind_directives_enabled(&self) -> bool {
self.tailwind_directives
}
+
+ pub fn is_css_modules_enabled(&self) -> bool {
+ matches!(
+ self.css_modules,
+ CssModulesKind::Classic | CssModulesKind::Vue
+ )
+ }
+
+ pub fn is_css_modules_vue_enabled(&self) -> bool {
+ matches!(self.css_modules, CssModulesKind::Vue)
+ }
}
impl<'source> CssParser<'source> {
@@ -181,7 +202,7 @@ impl From<&CssFileSource> for CssParserOptions {
fn from(file_source: &CssFileSource) -> Self {
let mut options = Self::default();
if file_source.is_css_modules() {
- options.css_modules = true;
+ options.css_modules = CssModulesKind::Classic;
}
if file_source.is_tailwind_css() {
options.tailwind_directives = true;
diff --git a/crates/biome_css_parser/src/syntax/at_rule/keyframes.rs b/crates/biome_css_parser/src/syntax/at_rule/keyframes.rs
index a2c2c78e6e40..e44e7ceb463a 100644
--- a/crates/biome_css_parser/src/syntax/at_rule/keyframes.rs
+++ b/crates/biome_css_parser/src/syntax/at_rule/keyframes.rs
@@ -10,7 +10,8 @@ use crate::syntax::css_modules::{
use crate::syntax::parse_error::expected_non_css_wide_keyword_identifier;
use crate::syntax::value::dimension::{is_at_percentage_dimension, parse_percentage_dimension};
use crate::syntax::{
- is_at_declaration, is_at_identifier, is_at_string, parse_custom_identifier, parse_string,
+ CssSyntaxFeatures, is_at_declaration, is_at_identifier, is_at_string, parse_custom_identifier,
+ parse_string,
};
use biome_css_syntax::CssSyntaxKind::*;
use biome_css_syntax::{CssSyntaxKind, T};
@@ -106,7 +107,7 @@ fn parse_keyframes_scoped_name(p: &mut CssParser) -> ParsedSyntax {
p.bump(T![:]);
- if p.options().is_css_modules_disabled() {
+ if CssSyntaxFeatures::CssModules.is_unsupported(p) {
// :local and :global are not standard CSS features
// provide a hint on how to enable parsing of these pseudo-classes
p.error(local_or_global_not_allowed(p, p.cur_range()));
diff --git a/crates/biome_css_parser/src/syntax/at_rule/value.rs b/crates/biome_css_parser/src/syntax/at_rule/value.rs
index a8acee3b7654..1c60c5cd7634 100644
--- a/crates/biome_css_parser/src/syntax/at_rule/value.rs
+++ b/crates/biome_css_parser/src/syntax/at_rule/value.rs
@@ -6,12 +6,13 @@ 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::{Parser, parsed_syntax::ParsedSyntax, token_set};
+use biome_parser::{Parser, SyntaxFeature, parsed_syntax::ParsedSyntax, token_set};
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,
+ CssSyntaxFeatures, 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.
@@ -37,7 +38,7 @@ pub(crate) fn parse_value_at_rule(p: &mut CssParser) -> ParsedSyntax {
return Absent;
}
- if p.options().is_css_modules_disabled() {
+ if CssSyntaxFeatures::CssModules.is_unsupported(p) {
// @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()));
diff --git a/crates/biome_css_parser/src/syntax/css_modules.rs b/crates/biome_css_parser/src/syntax/css_modules.rs
index a33d5c6109c3..87788ac15780 100644
--- a/crates/biome_css_parser/src/syntax/css_modules.rs
+++ b/crates/biome_css_parser/src/syntax/css_modules.rs
@@ -6,6 +6,9 @@ use biome_parser::{Parser, TokenSet, token_set};
/// A set of tokens representing the CSS Modules pseudo-classes `:local` and `:global`.
pub(crate) const CSS_MODULES_SCOPE_SET: TokenSet = token_set![T![global], T![local]];
+pub(crate) const CSS_MODULES_VUE_ENHANCED_SET: TokenSet =
+ token_set![T![slotted], T![deep]];
+
/// Generates a parse diagnostic for when the `:local` or `:global` pseudo-classes are not allowed.
///
/// This function returns an error diagnostic indicating that the `:local` or `:global` pseudo-classes
@@ -22,6 +25,16 @@ pub(crate) fn local_or_global_not_allowed(p: &CssParser, range: TextRange) -> Pa
)
}
+/// This function generates a parsing diagnostic for the usage of the
+/// `:slotted` and `:deep` pseudo-classes in CSS, which are non-standard CSS features.
+pub(crate) fn slotted_or_deep_not_allowed(p: &CssParser, range: TextRange) -> ParseDiagnostic {
+ p.err_builder(
+ "`:slotted` and `:deep` pseudo-classes are not standard CSS features.",
+ range,
+ )
+ .with_hint("These are valid pseudo selectors only when defined inside SFC vue files.")
+}
+
pub(crate) fn expected_any_css_module_scope(p: &CssParser, range: TextRange) -> ParseDiagnostic {
expect_one_of(&["global", "local"], range).into_diagnostic(p)
}
diff --git a/crates/biome_css_parser/src/syntax/mod.rs b/crates/biome_css_parser/src/syntax/mod.rs
index f5bedc53217d..7e165e93bd76 100644
--- a/crates/biome_css_parser/src/syntax/mod.rs
+++ b/crates/biome_css_parser/src/syntax/mod.rs
@@ -38,6 +38,12 @@ use self::parse_error::{expected_component_value, expected_declaration_item};
pub(crate) enum CssSyntaxFeatures {
/// Enable support for Tailwind CSS directives and syntax.
Tailwind,
+
+ /// Enable support for CSS Modules syntax.
+ CssModules,
+
+ /// Enable support for CSS Modules syntax plus parsing of pseudo selectors fo `:slotted` and `:deep`
+ CssModulesWithVue,
}
impl SyntaxFeature for CssSyntaxFeatures {
@@ -46,6 +52,8 @@ impl SyntaxFeature for CssSyntaxFeatures {
fn is_supported(&self, p: &Self::Parser<'_>) -> bool {
match self {
Self::Tailwind => p.options().is_tailwind_directives_enabled(),
+ Self::CssModules => p.options().is_css_modules_enabled(),
+ Self::CssModulesWithVue => p.options().is_css_modules_vue_enabled(),
}
}
}
@@ -58,7 +66,7 @@ pub(crate) fn parse_root(p: &mut CssParser) {
m.complete(p, CSS_SNIPPET_ROOT);
}
- EmbeddingKind::None => {
+ EmbeddingKind::None | EmbeddingKind::Html(_) => {
p.eat(UNICODE_BOM);
RuleList::new(EOF).parse_list(p);
diff --git a/crates/biome_css_parser/src/syntax/property/mod.rs b/crates/biome_css_parser/src/syntax/property/mod.rs
index 0105e90f8599..97af4914507f 100644
--- a/crates/biome_css_parser/src/syntax/property/mod.rs
+++ b/crates/biome_css_parser/src/syntax/property/mod.rs
@@ -69,7 +69,7 @@ fn parse_composes_property(p: &mut CssParser) -> ParsedSyntax {
return Absent;
}
- if p.options().is_css_modules_disabled() {
+ if CssSyntaxFeatures::CssModules.is_unsupported(p) {
// `composes` is not a standard CSS feature.
// Provide a hint on how to enable parsing of the `composes` declaration.
p.error(composes_not_allowed(p, p.cur_range()));
diff --git a/crates/biome_css_parser/src/syntax/selector/pseudo_class/function_selector.rs b/crates/biome_css_parser/src/syntax/selector/pseudo_class/function_selector.rs
index 61f0d0fc361a..ae493d69c0af 100644
--- a/crates/biome_css_parser/src/syntax/selector/pseudo_class/function_selector.rs
+++ b/crates/biome_css_parser/src/syntax/selector/pseudo_class/function_selector.rs
@@ -1,27 +1,34 @@
use crate::parser::CssParser;
-use crate::syntax::css_modules::{CSS_MODULES_SCOPE_SET, local_or_global_not_allowed};
+use crate::syntax::css_modules::{
+ CSS_MODULES_SCOPE_SET, CSS_MODULES_VUE_ENHANCED_SET, local_or_global_not_allowed,
+ slotted_or_deep_not_allowed,
+};
use crate::syntax::parse_error::expected_selector;
-use crate::syntax::parse_regular_identifier;
use crate::syntax::selector::{
eat_or_recover_selector_function_close_token, parse_selector,
recover_selector_function_parameter,
};
+use crate::syntax::{CssSyntaxFeatures, parse_regular_identifier};
+use biome_css_syntax::CssSyntaxKind::CSS_PSEUDO_CLASS_FUNCTION_SELECTOR;
use biome_css_syntax::CssSyntaxKind::*;
-use biome_css_syntax::CssSyntaxKind::{self, CSS_PSEUDO_CLASS_FUNCTION_SELECTOR};
use biome_css_syntax::T;
-use biome_parser::Parser;
use biome_parser::parsed_syntax::ParsedSyntax;
use biome_parser::parsed_syntax::ParsedSyntax::{Absent, Present};
+use biome_parser::{Parser, SyntaxFeature};
-/// Checks if the current parser position is at a pseudo-class function selector for CSS Modules.
+/// Checks if the current parser position is at a pseudo-class function selector for CSS Modules and SFC Vue.
///
-/// This function determines if the parser is currently positioned at the start of a `:local` or `:global`
-/// pseudo-class function selector, which is part of the CSS Modules syntax.
+/// This function determines if the parser is currently positioned at the start of a `:local` or `:global`.
#[inline]
pub(crate) fn is_at_pseudo_class_function_selector(p: &mut CssParser) -> bool {
p.at_ts(CSS_MODULES_SCOPE_SET) && p.nth_at(1, T!['('])
}
+#[inline]
+pub(crate) fn is_at_vue_pseudo_class_function_selector(p: &mut CssParser) -> bool {
+ p.at_ts(CSS_MODULES_VUE_ENHANCED_SET) && p.nth_at(1, T!['('])
+}
+
/// Parses a pseudo-class function selector for CSS Modules.
///
/// This function parses a pseudo-class function selector, specifically `:local` or `:global`, in CSS Modules.
@@ -39,24 +46,24 @@ pub(crate) fn is_at_pseudo_class_function_selector(p: &mut CssParser) -> bool {
/// ```
#[inline]
pub(crate) fn parse_pseudo_class_function_selector(p: &mut CssParser) -> ParsedSyntax {
- if !is_at_pseudo_class_function_selector(p) {
- return Absent;
- }
-
- if p.options().is_css_modules_disabled() {
- // :local and :global are not standard CSS features
- // provide a hint on how to enable parsing of these pseudo-classes
- p.error(local_or_global_not_allowed(p, p.cur_range()));
-
- // Skip the entire pseudo-class function selector
- // Skip until the next closing parenthesis
- while !p.eat(T![')']) && !p.at(CssSyntaxKind::EOF) {
- p.bump_any();
- }
-
- return Absent;
+ if is_at_pseudo_class_function_selector(p) {
+ CssSyntaxFeatures::CssModules.parse_exclusive_syntax(
+ p,
+ parse_pseudo_selector,
+ |p, marker| local_or_global_not_allowed(p, marker.range(p)),
+ )
+ } else if is_at_vue_pseudo_class_function_selector(p) {
+ CssSyntaxFeatures::CssModulesWithVue.parse_exclusive_syntax(
+ p,
+ parse_pseudo_selector,
+ |p, marker| slotted_or_deep_not_allowed(p, marker.range(p)),
+ )
+ } else {
+ Absent
}
+}
+fn parse_pseudo_selector(p: &mut CssParser) -> ParsedSyntax {
let m = p.start();
parse_regular_identifier(p).ok();
diff --git a/crates/biome_css_parser/src/syntax/selector/pseudo_class/mod.rs b/crates/biome_css_parser/src/syntax/selector/pseudo_class/mod.rs
index 3ec90c39617d..5a44b447d321 100644
--- a/crates/biome_css_parser/src/syntax/selector/pseudo_class/mod.rs
+++ b/crates/biome_css_parser/src/syntax/selector/pseudo_class/mod.rs
@@ -42,6 +42,7 @@ use crate::syntax::selector::pseudo_class::function_custom_identifier_list::{
is_at_pseudo_class_function_custom_identifier_list,
parse_pseudo_class_function_custom_identifier_list,
};
+use crate::syntax::selector::pseudo_class::function_selector::is_at_vue_pseudo_class_function_selector;
use biome_css_syntax::CssSyntaxKind::*;
use biome_css_syntax::T;
use biome_parser::Parser;
@@ -83,7 +84,8 @@ fn parse_pseudo_class(p: &mut CssParser) -> ParsedSyntax {
if is_at_pseudo_class_function_identifier(p) {
parse_pseudo_class_function_identifier(p)
- } else if is_at_pseudo_class_function_selector(p) {
+ } else if is_at_pseudo_class_function_selector(p) || is_at_vue_pseudo_class_function_selector(p)
+ {
parse_pseudo_class_function_selector(p)
} else if is_at_pseudo_class_function_selector_list(p) {
parse_pseudo_class_function_selector_list(p)
diff --git a/crates/biome_css_parser/tests/css_test_suite/error/selector/pseudo_class/pseudo_class_function_selector/disabled/pseudo_class_function_selector_disabled.css.snap b/crates/biome_css_parser/tests/css_test_suite/error/selector/pseudo_class/pseudo_class_function_selector/disabled/pseudo_class_function_selector_disabled.css.snap
index 079075873387..52375f42a51b 100644
--- a/crates/biome_css_parser/tests/css_test_suite/error/selector/pseudo_class/pseudo_class_function_selector/disabled/pseudo_class_function_selector_disabled.css.snap
+++ b/crates/biome_css_parser/tests/css_test_suite/error/selector/pseudo_class/pseudo_class_function_selector/disabled/pseudo_class_function_selector_disabled.css.snap
@@ -30,16 +30,42 @@ CssRoot {
nesting_selectors: CssNestedSelectorList [],
simple_selector: missing (optional),
sub_selectors: CssSubSelectorList [
- CssBogusSubSelector {
- items: [
- COLON@0..1 ":" [] [],
- GLOBAL_KW@1..7 "global" [] [],
- L_PAREN@7..8 "(" [] [],
- DOT@8..9 "." [] [],
- IDENT@9..15 "class" [] [Whitespace(" ")],
- IDENT@15..18 "div" [] [],
- R_PAREN@18..20 ")" [] [Whitespace(" ")],
- ],
+ CssPseudoClassSelector {
+ colon_token: COLON@0..1 ":" [] [],
+ class: CssBogusPseudoClass {
+ items: [
+ CssIdentifier {
+ value_token: IDENT@1..7 "global" [] [],
+ },
+ L_PAREN@7..8 "(" [] [],
+ CssComplexSelector {
+ left: CssCompoundSelector {
+ nesting_selectors: CssNestedSelectorList [],
+ simple_selector: missing (optional),
+ sub_selectors: CssSubSelectorList [
+ CssClassSelector {
+ dot_token: DOT@8..9 "." [] [],
+ name: CssCustomIdentifier {
+ value_token: IDENT@9..14 "class" [] [],
+ },
+ },
+ ],
+ },
+ combinator: CSS_SPACE_LITERAL@14..15 " " [] [],
+ right: CssCompoundSelector {
+ nesting_selectors: CssNestedSelectorList [],
+ simple_selector: CssTypeSelector {
+ namespace: missing (optional),
+ ident: CssIdentifier {
+ value_token: IDENT@15..18 "div" [] [],
+ },
+ },
+ sub_selectors: CssSubSelectorList [],
+ },
+ },
+ R_PAREN@18..20 ")" [] [Whitespace(" ")],
+ ],
+ },
},
],
},
@@ -56,19 +82,57 @@ CssRoot {
nesting_selectors: CssNestedSelectorList [],
simple_selector: missing (optional),
sub_selectors: CssSubSelectorList [
- CssBogusSubSelector {
- items: [
- COLON@22..24 ":" [Newline("\n")] [],
- LOCAL_KW@24..29 "local" [] [],
- L_PAREN@29..30 "(" [] [],
- DOT@30..31 "." [] [],
- IDENT@31..37 "class" [] [Whitespace(" ")],
- IDENT@37..41 "div" [] [Whitespace(" ")],
- PLUS@41..43 "+" [] [Whitespace(" ")],
- HASH@43..44 "#" [] [],
- IDENT@44..46 "id" [] [],
- R_PAREN@46..48 ")" [] [Whitespace(" ")],
- ],
+ CssPseudoClassSelector {
+ colon_token: COLON@22..24 ":" [Newline("\n")] [],
+ class: CssBogusPseudoClass {
+ items: [
+ CssIdentifier {
+ value_token: IDENT@24..29 "local" [] [],
+ },
+ L_PAREN@29..30 "(" [] [],
+ CssComplexSelector {
+ left: CssComplexSelector {
+ left: CssCompoundSelector {
+ nesting_selectors: CssNestedSelectorList [],
+ simple_selector: missing (optional),
+ sub_selectors: CssSubSelectorList [
+ CssClassSelector {
+ dot_token: DOT@30..31 "." [] [],
+ name: CssCustomIdentifier {
+ value_token: IDENT@31..36 "class" [] [],
+ },
+ },
+ ],
+ },
+ combinator: CSS_SPACE_LITERAL@36..37 " " [] [],
+ right: CssCompoundSelector {
+ nesting_selectors: CssNestedSelectorList [],
+ simple_selector: CssTypeSelector {
+ namespace: missing (optional),
+ ident: CssIdentifier {
+ value_token: IDENT@37..41 "div" [] [Whitespace(" ")],
+ },
+ },
+ sub_selectors: CssSubSelectorList [],
+ },
+ },
+ combinator: PLUS@41..43 "+" [] [Whitespace(" ")],
+ right: CssCompoundSelector {
+ nesting_selectors: CssNestedSelectorList [],
+ simple_selector: missing (optional),
+ sub_selectors: CssSubSelectorList [
+ CssIdSelector {
+ hash_token: HASH@43..44 "#" [] [],
+ name: CssCustomIdentifier {
+ value_token: IDENT@44..46 "id" [] [],
+ },
+ },
+ ],
+ },
+ },
+ R_PAREN@46..48 ")" [] [Whitespace(" ")],
+ ],
+ },
},
],
},
@@ -79,36 +143,97 @@ CssRoot {
r_curly_token: R_CURLY@49..50 "}" [] [],
},
},
+ CssQualifiedRule {
+ prelude: CssSelectorList [
+ CssComplexSelector {
+ left: CssCompoundSelector {
+ nesting_selectors: CssNestedSelectorList [],
+ simple_selector: missing (optional),
+ sub_selectors: CssSubSelectorList [
+ CssPseudoClassSelector {
+ colon_token: COLON@50..52 ":" [Newline("\n")] [],
+ class: CssBogusPseudoClass {
+ items: [
+ CssIdentifier {
+ value_token: IDENT@52..58 "global" [] [],
+ },
+ L_PAREN@58..59 "(" [] [],
+ CssComplexSelector {
+ left: CssCompoundSelector {
+ nesting_selectors: CssNestedSelectorList [],
+ simple_selector: missing (optional),
+ sub_selectors: CssSubSelectorList [
+ CssClassSelector {
+ dot_token: DOT@59..60 "." [] [],
+ name: CssCustomIdentifier {
+ value_token: IDENT@60..65 "class" [] [],
+ },
+ },
+ ],
+ },
+ combinator: CSS_SPACE_LITERAL@65..66 " " [] [],
+ right: CssCompoundSelector {
+ nesting_selectors: CssNestedSelectorList [],
+ simple_selector: CssTypeSelector {
+ namespace: missing (optional),
+ ident: CssIdentifier {
+ value_token: IDENT@66..69 "div" [] [],
+ },
+ },
+ sub_selectors: CssSubSelectorList [],
+ },
+ },
+ R_PAREN@69..70 ")" [] [],
+ ],
+ },
+ },
+ ],
+ },
+ combinator: CSS_SPACE_LITERAL@70..71 " " [] [],
+ right: CssCompoundSelector {
+ nesting_selectors: CssNestedSelectorList [],
+ simple_selector: missing (optional),
+ sub_selectors: CssSubSelectorList [
+ CssClassSelector {
+ dot_token: DOT@71..72 "." [] [],
+ name: CssCustomIdentifier {
+ value_token: IDENT@72..76 "div" [] [Whitespace(" ")],
+ },
+ },
+ ],
+ },
+ },
+ ],
+ block: CssDeclarationOrRuleBlock {
+ l_curly_token: L_CURLY@76..77 "{" [] [],
+ items: CssDeclarationOrRuleList [],
+ r_curly_token: R_CURLY@77..78 "}" [] [],
+ },
+ },
CssQualifiedRule {
prelude: CssSelectorList [
CssCompoundSelector {
nesting_selectors: CssNestedSelectorList [],
simple_selector: missing (optional),
sub_selectors: CssSubSelectorList [
- CssBogusSubSelector {
- items: [
- COLON@50..52 ":" [Newline("\n")] [],
- GLOBAL_KW@52..58 "global" [] [],
- L_PAREN@58..59 "(" [] [],
- DOT@59..60 "." [] [],
- IDENT@60..66 "class" [] [Whitespace(" ")],
- IDENT@66..69 "div" [] [],
- R_PAREN@69..71 ")" [] [Whitespace(" ")],
- ],
- },
- CssClassSelector {
- dot_token: DOT@71..72 "." [] [],
- name: CssCustomIdentifier {
- value_token: IDENT@72..76 "div" [] [Whitespace(" ")],
+ CssPseudoClassSelector {
+ colon_token: COLON@78..80 ":" [Newline("\n")] [],
+ class: CssBogusPseudoClass {
+ items: [
+ CssIdentifier {
+ value_token: IDENT@80..86 "global" [] [],
+ },
+ L_PAREN@86..88 "(" [] [Whitespace(" ")],
+ ],
},
},
],
},
],
block: CssDeclarationOrRuleBlock {
- l_curly_token: L_CURLY@76..77 "{" [] [],
+ l_curly_token: L_CURLY@88..89 "{" [] [],
items: CssDeclarationOrRuleList [],
- r_curly_token: R_CURLY@77..78 "}" [] [],
+ r_curly_token: R_CURLY@89..90 "}" [] [],
},
},
CssQualifiedRule {
@@ -117,18 +242,17 @@ CssRoot {
nesting_selectors: CssNestedSelectorList [],
simple_selector: missing (optional),
sub_selectors: CssSubSelectorList [
- CssBogusSubSelector {
- items: [
- COLON@78..80 ":" [Newline("\n")] [],
- GLOBAL_KW@80..86 "global" [] [],
- L_PAREN@86..88 "(" [] [Whitespace(" ")],
- L_CURLY@88..89 "{" [] [],
- R_CURLY@89..90 "}" [] [],
- COLON@90..92 ":" [Newline("\n")] [],
- GLOBAL_KW@92..98 "global" [] [],
- L_PAREN@98..99 "(" [] [],
- R_PAREN@99..101 ")" [] [Whitespace(" ")],
- ],
+ CssPseudoClassSelector {
+ colon_token: COLON@90..92 ":" [Newline("\n")] [],
+ class: CssBogusPseudoClass {
+ items: [
+ CssIdentifier {
+ value_token: IDENT@92..98 "global" [] [],
+ },
+ L_PAREN@98..99 "(" [] [],
+ R_PAREN@99..101 ")" [] [Whitespace(" ")],
+ ],
+ },
},
],
},
@@ -145,18 +269,36 @@ CssRoot {
nesting_selectors: CssNestedSelectorList [],
simple_selector: missing (optional),
sub_selectors: CssSubSelectorList [
- CssBogusSubSelector {
- items: [
- COLON@103..105 ":" [Newline("\n")] [],
- GLOBAL_KW@105..111 "global" [] [],
- L_PAREN@111..112 "(" [] [],
- DOT@112..113 "." [] [],
- IDENT@113..116 "div" [] [],
- COMMA@116..118 "," [] [Whitespace(" ")],
- DOT@118..119 "." [] [],
- IDENT@119..124 "class" [] [],
- R_PAREN@124..126 ")" [] [Whitespace(" ")],
- ],
+ CssPseudoClassSelector {
+ colon_token: COLON@103..105 ":" [Newline("\n")] [],
+ class: CssBogusPseudoClass {
+ items: [
+ CssIdentifier {
+ value_token: IDENT@105..111 "global" [] [],
+ },
+ L_PAREN@111..112 "(" [] [],
+ CssCompoundSelector {
+ nesting_selectors: CssNestedSelectorList [],
+ simple_selector: missing (optional),
+ sub_selectors: CssSubSelectorList [
+ CssClassSelector {
+ dot_token: DOT@112..113 "." [] [],
+ name: CssCustomIdentifier {
+ value_token: IDENT@113..116 "div" [] [],
+ },
+ },
+ ],
+ },
+ CssBogus {
+ items: [
+ COMMA@116..118 "," [] [Whitespace(" ")],
+ DOT@118..119 "." [] [],
+ IDENT@119..124 "class" [] [],
+ ],
+ },
+ R_PAREN@124..126 ")" [] [Whitespace(" ")],
+ ],
+ },
},
],
},
@@ -173,35 +315,145 @@ CssRoot {
nesting_selectors: CssNestedSelectorList [],
simple_selector: missing (optional),
sub_selectors: CssSubSelectorList [
- CssBogusSubSelector {
- items: [
- COLON@128..130 ":" [Newline("\n")] [],
- GLOBAL_KW@130..136 "global" [] [],
- L_PAREN@136..137 "(" [] [],
- DOT@137..138 "." [] [],
- IDENT@138..141 "div" [] [],
- COMMA@141..143 "," [] [Whitespace(" ")],
- DOT@143..144 "." [] [],
- IDENT@144..150 "class" [] [Whitespace(" ")],
- L_CURLY@150..151 "{" [] [],
- R_CURLY@151..152 "}" [] [],
- COLON@152..154 ":" [Newline("\n")] [],
- GLOBAL_KW@154..160 "global" [] [],
- L_PAREN@160..161 "(" [] [],
- DOT@161..162 "." [] [],
- IDENT@162..166 "div" [] [Whitespace(" ")],
- DOT@166..167 "." [] [],
- IDENT@167..173 "class" [] [Whitespace(" ")],
- L_CURLY@173..174 "{" [] [],
- R_CURLY@174..175 "}" [] [],
- COLON@175..177 ":" [Newline("\n")] [],
- GLOBAL_KW@177..183 "global" [] [],
- L_PAREN@183..184 "(" [] [],
- DOT@184..185 "." [] [],
- IDENT@185..189 "div" [] [Whitespace(" ")],
- DOT@189..190 "." [] [],
- IDENT@190..195 "class" [] [],
- ],
+ CssPseudoClassSelector {
+ colon_token: COLON@128..130 ":" [Newline("\n")] [],
+ class: CssBogusPseudoClass {
+ items: [
+ CssIdentifier {
+ value_token: IDENT@130..136 "global" [] [],
+ },
+ L_PAREN@136..137 "(" [] [],
+ CssCompoundSelector {
+ nesting_selectors: CssNestedSelectorList [],
+ simple_selector: missing (optional),
+ sub_selectors: CssSubSelectorList [
+ CssClassSelector {
+ dot_token: DOT@137..138 "." [] [],
+ name: CssCustomIdentifier {
+ value_token: IDENT@138..141 "div" [] [],
+ },
+ },
+ ],
+ },
+ CssBogus {
+ items: [
+ COMMA@141..143 "," [] [Whitespace(" ")],
+ DOT@143..144 "." [] [],
+ IDENT@144..150 "class" [] [Whitespace(" ")],
+ ],
+ },
+ ],
+ },
+ },
+ ],
+ },
+ ],
+ block: CssDeclarationOrRuleBlock {
+ l_curly_token: L_CURLY@150..151 "{" [] [],
+ items: CssDeclarationOrRuleList [],
+ r_curly_token: R_CURLY@151..152 "}" [] [],
+ },
+ },
+ CssQualifiedRule {
+ prelude: CssSelectorList [
+ CssCompoundSelector {
+ nesting_selectors: CssNestedSelectorList [],
+ simple_selector: missing (optional),
+ sub_selectors: CssSubSelectorList [
+ CssPseudoClassSelector {
+ colon_token: COLON@152..154 ":" [Newline("\n")] [],
+ class: CssBogusPseudoClass {
+ items: [
+ CssIdentifier {
+ value_token: IDENT@154..160 "global" [] [],
+ },
+ L_PAREN@160..161 "(" [] [],
+ CssComplexSelector {
+ left: CssCompoundSelector {
+ nesting_selectors: CssNestedSelectorList [],
+ simple_selector: missing (optional),
+ sub_selectors: CssSubSelectorList [
+ CssClassSelector {
+ dot_token: DOT@161..162 "." [] [],
+ name: CssCustomIdentifier {
+ value_token: IDENT@162..165 "div" [] [],
+ },
+ },
+ ],
+ },
+ combinator: CSS_SPACE_LITERAL@165..166 " " [] [],
+ right: CssCompoundSelector {
+ nesting_selectors: CssNestedSelectorList [],
+ simple_selector: missing (optional),
+ sub_selectors: CssSubSelectorList [
+ CssClassSelector {
+ dot_token: DOT@166..167 "." [] [],
+ name: CssCustomIdentifier {
+ value_token: IDENT@167..173 "class" [] [Whitespace(" ")],
+ },
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ ],
+ },
+ ],
+ block: CssDeclarationOrRuleBlock {
+ l_curly_token: L_CURLY@173..174 "{" [] [],
+ items: CssDeclarationOrRuleList [],
+ r_curly_token: R_CURLY@174..175 "}" [] [],
+ },
+ },
+ CssQualifiedRule {
+ prelude: CssSelectorList [
+ CssCompoundSelector {
+ nesting_selectors: CssNestedSelectorList [],
+ simple_selector: missing (optional),
+ sub_selectors: CssSubSelectorList [
+ CssPseudoClassSelector {
+ colon_token: COLON@175..177 ":" [Newline("\n")] [],
+ class: CssBogusPseudoClass {
+ items: [
+ CssIdentifier {
+ value_token: IDENT@177..183 "global" [] [],
+ },
+ L_PAREN@183..184 "(" [] [],
+ CssComplexSelector {
+ left: CssComplexSelector {
+ left: CssCompoundSelector {
+ nesting_selectors: CssNestedSelectorList [],
+ simple_selector: missing (optional),
+ sub_selectors: CssSubSelectorList [
+ CssClassSelector {
+ dot_token: DOT@184..185 "." [] [],
+ name: CssCustomIdentifier {
+ value_token: IDENT@185..188 "div" [] [],
+ },
+ },
+ ],
+ },
+ combinator: CSS_SPACE_LITERAL@188..189 " " [] [],
+ right: CssCompoundSelector {
+ nesting_selectors: CssNestedSelectorList [],
+ simple_selector: missing (optional),
+ sub_selectors: CssSubSelectorList [
+ CssClassSelector {
+ dot_token: DOT@189..190 "." [] [],
+ name: CssCustomIdentifier {
+ value_token: IDENT@190..195 "class" [] [],
+ },
+ },
+ ],
+ },
+ },
+ combinator: CSS_SPACE_LITERAL@195..196 "\n" [] [],
+ right: missing (required),
+ },
+ ],
+ },
},
],
},
@@ -211,7 +463,7 @@ CssRoot {
},
},
],
- eof_token: EOF@195..196 "" [Newline("\n")] [],
+ eof_token: EOF@196..196 "" [] [],
}
```
@@ -220,21 +472,37 @@ CssRoot {
```
0: CSS_ROOT@0..196
0: (empty)
- 1: CSS_RULE_LIST@0..195
+ 1: CSS_RULE_LIST@0..196
0: CSS_QUALIFIED_RULE@0..22
0: CSS_SELECTOR_LIST@0..20
0: CSS_COMPOUND_SELECTOR@0..20
0: CSS_NESTED_SELECTOR_LIST@0..0
1: (empty)
2: CSS_SUB_SELECTOR_LIST@0..20
- 0: CSS_BOGUS_SUB_SELECTOR@0..20
+ 0: CSS_PSEUDO_CLASS_SELECTOR@0..20
0: COLON@0..1 ":" [] []
- 1: GLOBAL_KW@1..7 "global" [] []
- 2: L_PAREN@7..8 "(" [] []
- 3: DOT@8..9 "." [] []
- 4: IDENT@9..15 "class" [] [Whitespace(" ")]
- 5: IDENT@15..18 "div" [] []
- 6: R_PAREN@18..20 ")" [] [Whitespace(" ")]
+ 1: CSS_BOGUS_PSEUDO_CLASS@1..20
+ 0: CSS_IDENTIFIER@1..7
+ 0: IDENT@1..7 "global" [] []
+ 1: L_PAREN@7..8 "(" [] []
+ 2: CSS_COMPLEX_SELECTOR@8..18
+ 0: CSS_COMPOUND_SELECTOR@8..14
+ 0: CSS_NESTED_SELECTOR_LIST@8..8
+ 1: (empty)
+ 2: CSS_SUB_SELECTOR_LIST@8..14
+ 0: CSS_CLASS_SELECTOR@8..14
+ 0: DOT@8..9 "." [] []
+ 1: CSS_CUSTOM_IDENTIFIER@9..14
+ 0: IDENT@9..14 "class" [] []
+ 1: CSS_SPACE_LITERAL@14..15 " " [] []
+ 2: CSS_COMPOUND_SELECTOR@15..18
+ 0: CSS_NESTED_SELECTOR_LIST@15..15
+ 1: CSS_TYPE_SELECTOR@15..18
+ 0: (empty)
+ 1: CSS_IDENTIFIER@15..18
+ 0: IDENT@15..18 "div" [] []
+ 2: CSS_SUB_SELECTOR_LIST@18..18
+ 3: R_PAREN@18..20 ")" [] [Whitespace(" ")]
1: CSS_DECLARATION_OR_RULE_BLOCK@20..22
0: L_CURLY@20..21 "{" [] []
1: CSS_DECLARATION_OR_RULE_LIST@21..21
@@ -245,118 +513,247 @@ CssRoot {
0: CSS_NESTED_SELECTOR_LIST@22..22
1: (empty)
2: CSS_SUB_SELECTOR_LIST@22..48
- 0: CSS_BOGUS_SUB_SELECTOR@22..48
+ 0: CSS_PSEUDO_CLASS_SELECTOR@22..48
0: COLON@22..24 ":" [Newline("\n")] []
- 1: LOCAL_KW@24..29 "local" [] []
- 2: L_PAREN@29..30 "(" [] []
- 3: DOT@30..31 "." [] []
- 4: IDENT@31..37 "class" [] [Whitespace(" ")]
- 5: IDENT@37..41 "div" [] [Whitespace(" ")]
- 6: PLUS@41..43 "+" [] [Whitespace(" ")]
- 7: HASH@43..44 "#" [] []
- 8: IDENT@44..46 "id" [] []
- 9: R_PAREN@46..48 ")" [] [Whitespace(" ")]
+ 1: CSS_BOGUS_PSEUDO_CLASS@24..48
+ 0: CSS_IDENTIFIER@24..29
+ 0: IDENT@24..29 "local" [] []
+ 1: L_PAREN@29..30 "(" [] []
+ 2: CSS_COMPLEX_SELECTOR@30..46
+ 0: CSS_COMPLEX_SELECTOR@30..41
+ 0: CSS_COMPOUND_SELECTOR@30..36
+ 0: CSS_NESTED_SELECTOR_LIST@30..30
+ 1: (empty)
+ 2: CSS_SUB_SELECTOR_LIST@30..36
+ 0: CSS_CLASS_SELECTOR@30..36
+ 0: DOT@30..31 "." [] []
+ 1: CSS_CUSTOM_IDENTIFIER@31..36
+ 0: IDENT@31..36 "class" [] []
+ 1: CSS_SPACE_LITERAL@36..37 " " [] []
+ 2: CSS_COMPOUND_SELECTOR@37..41
+ 0: CSS_NESTED_SELECTOR_LIST@37..37
+ 1: CSS_TYPE_SELECTOR@37..41
+ 0: (empty)
+ 1: CSS_IDENTIFIER@37..41
+ 0: IDENT@37..41 "div" [] [Whitespace(" ")]
+ 2: CSS_SUB_SELECTOR_LIST@41..41
+ 1: PLUS@41..43 "+" [] [Whitespace(" ")]
+ 2: CSS_COMPOUND_SELECTOR@43..46
+ 0: CSS_NESTED_SELECTOR_LIST@43..43
+ 1: (empty)
+ 2: CSS_SUB_SELECTOR_LIST@43..46
+ 0: CSS_ID_SELECTOR@43..46
+ 0: HASH@43..44 "#" [] []
+ 1: CSS_CUSTOM_IDENTIFIER@44..46
+ 0: IDENT@44..46 "id" [] []
+ 3: R_PAREN@46..48 ")" [] [Whitespace(" ")]
1: CSS_DECLARATION_OR_RULE_BLOCK@48..50
0: L_CURLY@48..49 "{" [] []
1: CSS_DECLARATION_OR_RULE_LIST@49..49
2: R_CURLY@49..50 "}" [] []
2: CSS_QUALIFIED_RULE@50..78
0: CSS_SELECTOR_LIST@50..76
- 0: CSS_COMPOUND_SELECTOR@50..76
- 0: CSS_NESTED_SELECTOR_LIST@50..50
- 1: (empty)
- 2: CSS_SUB_SELECTOR_LIST@50..76
- 0: CSS_BOGUS_SUB_SELECTOR@50..71
- 0: COLON@50..52 ":" [Newline("\n")] []
- 1: GLOBAL_KW@52..58 "global" [] []
- 2: L_PAREN@58..59 "(" [] []
- 3: DOT@59..60 "." [] []
- 4: IDENT@60..66 "class" [] [Whitespace(" ")]
- 5: IDENT@66..69 "div" [] []
- 6: R_PAREN@69..71 ")" [] [Whitespace(" ")]
- 1: CSS_CLASS_SELECTOR@71..76
- 0: DOT@71..72 "." [] []
- 1: CSS_CUSTOM_IDENTIFIER@72..76
- 0: IDENT@72..76 "div" [] [Whitespace(" ")]
+ 0: CSS_COMPLEX_SELECTOR@50..76
+ 0: CSS_COMPOUND_SELECTOR@50..70
+ 0: CSS_NESTED_SELECTOR_LIST@50..50
+ 1: (empty)
+ 2: CSS_SUB_SELECTOR_LIST@50..70
+ 0: CSS_PSEUDO_CLASS_SELECTOR@50..70
+ 0: COLON@50..52 ":" [Newline("\n")] []
+ 1: CSS_BOGUS_PSEUDO_CLASS@52..70
+ 0: CSS_IDENTIFIER@52..58
+ 0: IDENT@52..58 "global" [] []
+ 1: L_PAREN@58..59 "(" [] []
+ 2: CSS_COMPLEX_SELECTOR@59..69
+ 0: CSS_COMPOUND_SELECTOR@59..65
+ 0: CSS_NESTED_SELECTOR_LIST@59..59
+ 1: (empty)
+ 2: CSS_SUB_SELECTOR_LIST@59..65
+ 0: CSS_CLASS_SELECTOR@59..65
+ 0: DOT@59..60 "." [] []
+ 1: CSS_CUSTOM_IDENTIFIER@60..65
+ 0: IDENT@60..65 "class" [] []
+ 1: CSS_SPACE_LITERAL@65..66 " " [] []
+ 2: CSS_COMPOUND_SELECTOR@66..69
+ 0: CSS_NESTED_SELECTOR_LIST@66..66
+ 1: CSS_TYPE_SELECTOR@66..69
+ 0: (empty)
+ 1: CSS_IDENTIFIER@66..69
+ 0: IDENT@66..69 "div" [] []
+ 2: CSS_SUB_SELECTOR_LIST@69..69
+ 3: R_PAREN@69..70 ")" [] []
+ 1: CSS_SPACE_LITERAL@70..71 " " [] []
+ 2: CSS_COMPOUND_SELECTOR@71..76
+ 0: CSS_NESTED_SELECTOR_LIST@71..71
+ 1: (empty)
+ 2: CSS_SUB_SELECTOR_LIST@71..76
+ 0: CSS_CLASS_SELECTOR@71..76
+ 0: DOT@71..72 "." [] []
+ 1: CSS_CUSTOM_IDENTIFIER@72..76
+ 0: IDENT@72..76 "div" [] [Whitespace(" ")]
1: CSS_DECLARATION_OR_RULE_BLOCK@76..78
0: L_CURLY@76..77 "{" [] []
1: CSS_DECLARATION_OR_RULE_LIST@77..77
2: R_CURLY@77..78 "}" [] []
- 3: CSS_QUALIFIED_RULE@78..103
- 0: CSS_SELECTOR_LIST@78..101
- 0: CSS_COMPOUND_SELECTOR@78..101
+ 3: CSS_QUALIFIED_RULE@78..90
+ 0: CSS_SELECTOR_LIST@78..88
+ 0: CSS_COMPOUND_SELECTOR@78..88
0: CSS_NESTED_SELECTOR_LIST@78..78
1: (empty)
- 2: CSS_SUB_SELECTOR_LIST@78..101
- 0: CSS_BOGUS_SUB_SELECTOR@78..101
+ 2: CSS_SUB_SELECTOR_LIST@78..88
+ 0: CSS_PSEUDO_CLASS_SELECTOR@78..88
0: COLON@78..80 ":" [Newline("\n")] []
- 1: GLOBAL_KW@80..86 "global" [] []
- 2: L_PAREN@86..88 "(" [] [Whitespace(" ")]
- 3: L_CURLY@88..89 "{" [] []
- 4: R_CURLY@89..90 "}" [] []
- 5: COLON@90..92 ":" [Newline("\n")] []
- 6: GLOBAL_KW@92..98 "global" [] []
- 7: L_PAREN@98..99 "(" [] []
- 8: R_PAREN@99..101 ")" [] [Whitespace(" ")]
+ 1: CSS_BOGUS_PSEUDO_CLASS@80..88
+ 0: CSS_IDENTIFIER@80..86
+ 0: IDENT@80..86 "global" [] []
+ 1: L_PAREN@86..88 "(" [] [Whitespace(" ")]
+ 1: CSS_DECLARATION_OR_RULE_BLOCK@88..90
+ 0: L_CURLY@88..89 "{" [] []
+ 1: CSS_DECLARATION_OR_RULE_LIST@89..89
+ 2: R_CURLY@89..90 "}" [] []
+ 4: CSS_QUALIFIED_RULE@90..103
+ 0: CSS_SELECTOR_LIST@90..101
+ 0: CSS_COMPOUND_SELECTOR@90..101
+ 0: CSS_NESTED_SELECTOR_LIST@90..90
+ 1: (empty)
+ 2: CSS_SUB_SELECTOR_LIST@90..101
+ 0: CSS_PSEUDO_CLASS_SELECTOR@90..101
+ 0: COLON@90..92 ":" [Newline("\n")] []
+ 1: CSS_BOGUS_PSEUDO_CLASS@92..101
+ 0: CSS_IDENTIFIER@92..98
+ 0: IDENT@92..98 "global" [] []
+ 1: L_PAREN@98..99 "(" [] []
+ 2: R_PAREN@99..101 ")" [] [Whitespace(" ")]
1: CSS_DECLARATION_OR_RULE_BLOCK@101..103
0: L_CURLY@101..102 "{" [] []
1: CSS_DECLARATION_OR_RULE_LIST@102..102
2: R_CURLY@102..103 "}" [] []
- 4: CSS_QUALIFIED_RULE@103..128
+ 5: CSS_QUALIFIED_RULE@103..128
0: CSS_SELECTOR_LIST@103..126
0: CSS_COMPOUND_SELECTOR@103..126
0: CSS_NESTED_SELECTOR_LIST@103..103
1: (empty)
2: CSS_SUB_SELECTOR_LIST@103..126
- 0: CSS_BOGUS_SUB_SELECTOR@103..126
+ 0: CSS_PSEUDO_CLASS_SELECTOR@103..126
0: COLON@103..105 ":" [Newline("\n")] []
- 1: GLOBAL_KW@105..111 "global" [] []
- 2: L_PAREN@111..112 "(" [] []
- 3: DOT@112..113 "." [] []
- 4: IDENT@113..116 "div" [] []
- 5: COMMA@116..118 "," [] [Whitespace(" ")]
- 6: DOT@118..119 "." [] []
- 7: IDENT@119..124 "class" [] []
- 8: R_PAREN@124..126 ")" [] [Whitespace(" ")]
+ 1: CSS_BOGUS_PSEUDO_CLASS@105..126
+ 0: CSS_IDENTIFIER@105..111
+ 0: IDENT@105..111 "global" [] []
+ 1: L_PAREN@111..112 "(" [] []
+ 2: CSS_COMPOUND_SELECTOR@112..116
+ 0: CSS_NESTED_SELECTOR_LIST@112..112
+ 1: (empty)
+ 2: CSS_SUB_SELECTOR_LIST@112..116
+ 0: CSS_CLASS_SELECTOR@112..116
+ 0: DOT@112..113 "." [] []
+ 1: CSS_CUSTOM_IDENTIFIER@113..116
+ 0: IDENT@113..116 "div" [] []
+ 3: CSS_BOGUS@116..124
+ 0: COMMA@116..118 "," [] [Whitespace(" ")]
+ 1: DOT@118..119 "." [] []
+ 2: IDENT@119..124 "class" [] []
+ 4: R_PAREN@124..126 ")" [] [Whitespace(" ")]
1: CSS_DECLARATION_OR_RULE_BLOCK@126..128
0: L_CURLY@126..127 "{" [] []
1: CSS_DECLARATION_OR_RULE_LIST@127..127
2: R_CURLY@127..128 "}" [] []
- 5: CSS_QUALIFIED_RULE@128..195
- 0: CSS_SELECTOR_LIST@128..195
- 0: CSS_COMPOUND_SELECTOR@128..195
+ 6: CSS_QUALIFIED_RULE@128..152
+ 0: CSS_SELECTOR_LIST@128..150
+ 0: CSS_COMPOUND_SELECTOR@128..150
0: CSS_NESTED_SELECTOR_LIST@128..128
1: (empty)
- 2: CSS_SUB_SELECTOR_LIST@128..195
- 0: CSS_BOGUS_SUB_SELECTOR@128..195
+ 2: CSS_SUB_SELECTOR_LIST@128..150
+ 0: CSS_PSEUDO_CLASS_SELECTOR@128..150
0: COLON@128..130 ":" [Newline("\n")] []
- 1: GLOBAL_KW@130..136 "global" [] []
- 2: L_PAREN@136..137 "(" [] []
- 3: DOT@137..138 "." [] []
- 4: IDENT@138..141 "div" [] []
- 5: COMMA@141..143 "," [] [Whitespace(" ")]
- 6: DOT@143..144 "." [] []
- 7: IDENT@144..150 "class" [] [Whitespace(" ")]
- 8: L_CURLY@150..151 "{" [] []
- 9: R_CURLY@151..152 "}" [] []
- 10: COLON@152..154 ":" [Newline("\n")] []
- 11: GLOBAL_KW@154..160 "global" [] []
- 12: L_PAREN@160..161 "(" [] []
- 13: DOT@161..162 "." [] []
- 14: IDENT@162..166 "div" [] [Whitespace(" ")]
- 15: DOT@166..167 "." [] []
- 16: IDENT@167..173 "class" [] [Whitespace(" ")]
- 17: L_CURLY@173..174 "{" [] []
- 18: R_CURLY@174..175 "}" [] []
- 19: COLON@175..177 ":" [Newline("\n")] []
- 20: GLOBAL_KW@177..183 "global" [] []
- 21: L_PAREN@183..184 "(" [] []
- 22: DOT@184..185 "." [] []
- 23: IDENT@185..189 "div" [] [Whitespace(" ")]
- 24: DOT@189..190 "." [] []
- 25: IDENT@190..195 "class" [] []
- 1: CSS_BOGUS_BLOCK@195..195
- 2: EOF@195..196 "" [Newline("\n")] []
+ 1: CSS_BOGUS_PSEUDO_CLASS@130..150
+ 0: CSS_IDENTIFIER@130..136
+ 0: IDENT@130..136 "global" [] []
+ 1: L_PAREN@136..137 "(" [] []
+ 2: CSS_COMPOUND_SELECTOR@137..141
+ 0: CSS_NESTED_SELECTOR_LIST@137..137
+ 1: (empty)
+ 2: CSS_SUB_SELECTOR_LIST@137..141
+ 0: CSS_CLASS_SELECTOR@137..141
+ 0: DOT@137..138 "." [] []
+ 1: CSS_CUSTOM_IDENTIFIER@138..141
+ 0: IDENT@138..141 "div" [] []
+ 3: CSS_BOGUS@141..150
+ 0: COMMA@141..143 "," [] [Whitespace(" ")]
+ 1: DOT@143..144 "." [] []
+ 2: IDENT@144..150 "class" [] [Whitespace(" ")]
+ 1: CSS_DECLARATION_OR_RULE_BLOCK@150..152
+ 0: L_CURLY@150..151 "{" [] []
+ 1: CSS_DECLARATION_OR_RULE_LIST@151..151
+ 2: R_CURLY@151..152 "}" [] []
+ 7: CSS_QUALIFIED_RULE@152..175
+ 0: CSS_SELECTOR_LIST@152..173
+ 0: CSS_COMPOUND_SELECTOR@152..173
+ 0: CSS_NESTED_SELECTOR_LIST@152..152
+ 1: (empty)
+ 2: CSS_SUB_SELECTOR_LIST@152..173
+ 0: CSS_PSEUDO_CLASS_SELECTOR@152..173
+ 0: COLON@152..154 ":" [Newline("\n")] []
+ 1: CSS_BOGUS_PSEUDO_CLASS@154..173
+ 0: CSS_IDENTIFIER@154..160
+ 0: IDENT@154..160 "global" [] []
+ 1: L_PAREN@160..161 "(" [] []
+ 2: CSS_COMPLEX_SELECTOR@161..173
+ 0: CSS_COMPOUND_SELECTOR@161..165
+ 0: CSS_NESTED_SELECTOR_LIST@161..161
+ 1: (empty)
+ 2: CSS_SUB_SELECTOR_LIST@161..165
+ 0: CSS_CLASS_SELECTOR@161..165
+ 0: DOT@161..162 "." [] []
+ 1: CSS_CUSTOM_IDENTIFIER@162..165
+ 0: IDENT@162..165 "div" [] []
+ 1: CSS_SPACE_LITERAL@165..166 " " [] []
+ 2: CSS_COMPOUND_SELECTOR@166..173
+ 0: CSS_NESTED_SELECTOR_LIST@166..166
+ 1: (empty)
+ 2: CSS_SUB_SELECTOR_LIST@166..173
+ 0: CSS_CLASS_SELECTOR@166..173
+ 0: DOT@166..167 "." [] []
+ 1: CSS_CUSTOM_IDENTIFIER@167..173
+ 0: IDENT@167..173 "class" [] [Whitespace(" ")]
+ 1: CSS_DECLARATION_OR_RULE_BLOCK@173..175
+ 0: L_CURLY@173..174 "{" [] []
+ 1: CSS_DECLARATION_OR_RULE_LIST@174..174
+ 2: R_CURLY@174..175 "}" [] []
+ 8: CSS_QUALIFIED_RULE@175..196
+ 0: CSS_SELECTOR_LIST@175..196
+ 0: CSS_COMPOUND_SELECTOR@175..196
+ 0: CSS_NESTED_SELECTOR_LIST@175..175
+ 1: (empty)
+ 2: CSS_SUB_SELECTOR_LIST@175..196
+ 0: CSS_PSEUDO_CLASS_SELECTOR@175..196
+ 0: COLON@175..177 ":" [Newline("\n")] []
+ 1: CSS_BOGUS_PSEUDO_CLASS@177..196
+ 0: CSS_IDENTIFIER@177..183
+ 0: IDENT@177..183 "global" [] []
+ 1: L_PAREN@183..184 "(" [] []
+ 2: CSS_COMPLEX_SELECTOR@184..196
+ 0: CSS_COMPLEX_SELECTOR@184..195
+ 0: CSS_COMPOUND_SELECTOR@184..188
+ 0: CSS_NESTED_SELECTOR_LIST@184..184
+ 1: (empty)
+ 2: CSS_SUB_SELECTOR_LIST@184..188
+ 0: CSS_CLASS_SELECTOR@184..188
+ 0: DOT@184..185 "." [] []
+ 1: CSS_CUSTOM_IDENTIFIER@185..188
+ 0: IDENT@185..188 "div" [] []
+ 1: CSS_SPACE_LITERAL@188..189 " " [] []
+ 2: CSS_COMPOUND_SELECTOR@189..195
+ 0: CSS_NESTED_SELECTOR_LIST@189..189
+ 1: (empty)
+ 2: CSS_SUB_SELECTOR_LIST@189..195
+ 0: CSS_CLASS_SELECTOR@189..195
+ 0: DOT@189..190 "." [] []
+ 1: CSS_CUSTOM_IDENTIFIER@190..195
+ 0: IDENT@190..195 "class" [] []
+ 1: CSS_SPACE_LITERAL@195..196 "\n" [] []
+ 2: (empty)
+ 1: CSS_BOGUS_BLOCK@196..196
+ 2: EOF@196..196 "" [] []
```
@@ -368,7 +765,7 @@ pseudo_class_function_selector_disabled.css:1:2 parse ━━━━━━━━
× `:local` and `:global` pseudo-classes are not standard CSS features.
> 1 │ :global(.class div) {}
- │ ^^^^^^
+ │ ^^^^^^^^^^^^^^^^^^
2 │ :local(.class div + #id) {}
3 │ :global(.class div) .div {}
@@ -380,7 +777,7 @@ pseudo_class_function_selector_disabled.css:2:2 parse ━━━━━━━━
1 │ :global(.class div) {}
> 2 │ :local(.class div + #id) {}
- │ ^^^^^
+ │ ^^^^^^^^^^^^^^^^^^^^^^^
3 │ :global(.class div) .div {}
4 │ :global( {}
@@ -393,7 +790,7 @@ pseudo_class_function_selector_disabled.css:3:2 parse ━━━━━━━━
1 │ :global(.class div) {}
2 │ :local(.class div + #id) {}
> 3 │ :global(.class div) .div {}
- │ ^^^^^^
+ │ ^^^^^^^^^^^^^^^^^^
4 │ :global( {}
5 │ :global() {}
@@ -406,12 +803,25 @@ pseudo_class_function_selector_disabled.css:4:2 parse ━━━━━━━━
2 │ :local(.class div + #id) {}
3 │ :global(.class div) .div {}
> 4 │ :global( {}
- │ ^^^^^^
+ │ ^^^^^^^
5 │ :global() {}
6 │ :global(.div, .class) {}
i You can enable `:local` and `:global` pseudo-class parsing by setting the `css.parser.cssModules` option to `true` in your configuration file.
+pseudo_class_function_selector_disabled.css:5:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ × `:local` and `:global` pseudo-classes are not standard CSS features.
+
+ 3 │ :global(.class div) .div {}
+ 4 │ :global( {}
+ > 5 │ :global() {}
+ │ ^^^^^^^^
+ 6 │ :global(.div, .class) {}
+ 7 │ :global(.div, .class {}
+
+ i You can enable `:local` and `:global` pseudo-class parsing by setting the `css.parser.cssModules` option to `true` in your configuration file.
+
pseudo_class_function_selector_disabled.css:6:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
× `:local` and `:global` pseudo-classes are not standard CSS features.
@@ -419,7 +829,7 @@ pseudo_class_function_selector_disabled.css:6:2 parse ━━━━━━━━
4 │ :global( {}
5 │ :global() {}
> 6 │ :global(.div, .class) {}
- │ ^^^^^^
+ │ ^^^^^^^^^^^^^^^^^^^^
7 │ :global(.div, .class {}
8 │ :global(.div .class {}
@@ -432,12 +842,38 @@ pseudo_class_function_selector_disabled.css:7:2 parse ━━━━━━━━
5 │ :global() {}
6 │ :global(.div, .class) {}
> 7 │ :global(.div, .class {}
- │ ^^^^^^
+ │ ^^^^^^^^^^^^^^^^^^^
8 │ :global(.div .class {}
9 │ :global(.div .class
i You can enable `:local` and `:global` pseudo-class parsing by setting the `css.parser.cssModules` option to `true` in your configuration file.
+pseudo_class_function_selector_disabled.css:8:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ × `:local` and `:global` pseudo-classes are not standard CSS features.
+
+ 6 │ :global(.div, .class) {}
+ 7 │ :global(.div, .class {}
+ > 8 │ :global(.div .class {}
+ │ ^^^^^^^^^^^^^^^^^^
+ 9 │ :global(.div .class
+ 10 │
+
+ i You can enable `:local` and `:global` pseudo-class parsing by setting the `css.parser.cssModules` option to `true` in your configuration file.
+
+pseudo_class_function_selector_disabled.css:9:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ × `:local` and `:global` pseudo-classes are not standard CSS features.
+
+ 7 │ :global(.div, .class {}
+ 8 │ :global(.div .class {}
+ > 9 │ :global(.div .class
+ │ ^^^^^^^^^^^^^^^^^^
+ > 10 │
+ │
+
+ i You can enable `:local` and `:global` pseudo-class parsing by setting the `css.parser.cssModules` option to `true` in your configuration file.
+
pseudo_class_function_selector_disabled.css:10:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
× expected `{` but instead the file ends
diff --git a/crates/biome_css_syntax/src/file_source.rs b/crates/biome_css_syntax/src/file_source.rs
index 5a40e2fd938f..88308712f1e3 100644
--- a/crates/biome_css_syntax/src/file_source.rs
+++ b/crates/biome_css_syntax/src/file_source.rs
@@ -10,10 +10,30 @@ pub enum EmbeddingKind {
/// styled-components or Emotion embedded CSS
Styled,
+ /// The CSS is embedded inside HTML-like files
+ Html(EmbeddingHtmlKind),
+
#[default]
None,
}
+#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
+#[derive(
+ Debug, Clone, Default, Copy, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize,
+)]
+pub enum EmbeddingHtmlKind {
+ #[default]
+ None,
+ /// `.html` files
+ Html,
+ /// `.vue` files
+ Vue,
+ /// `.astro` files
+ Astro,
+ /// `.svelte` files
+ Svelte,
+}
+
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(
Debug, Clone, Default, Copy, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize,
@@ -62,6 +82,13 @@ impl CssFileSource {
}
}
+ pub fn new_css_modules() -> Self {
+ Self {
+ variant: CssVariant::CssModules,
+ embedding_kind: EmbeddingKind::None,
+ }
+ }
+
pub const fn with_embedding_kind(mut self, kind: EmbeddingKind) -> Self {
self.embedding_kind = kind;
self
@@ -71,10 +98,27 @@ impl CssFileSource {
&self.embedding_kind
}
+ pub fn with_css_modules(mut self) -> Self {
+ self.variant = CssVariant::CssModules;
+ self
+ }
+
+ pub fn with_tailwind_directives(mut self) -> Self {
+ self.variant = CssVariant::TailwindCss;
+ self
+ }
+
pub fn is_css_modules(&self) -> bool {
self.variant == CssVariant::CssModules
}
+ pub fn is_vue_embedded(&self) -> bool {
+ matches!(
+ self.embedding_kind,
+ EmbeddingKind::Html(EmbeddingHtmlKind::Vue)
+ )
+ }
+
pub fn is_tailwind_css(&self) -> bool {
self.variant == CssVariant::TailwindCss
}
@@ -84,9 +128,18 @@ impl CssFileSource {
}
/// Try to return the CSS file source corresponding to this file name from well-known files
- pub fn try_from_well_known(_: &Utf8Path) -> Result {
- // TODO: to be implemented
- Err(FileSourceError::UnknownFileName)
+ pub fn try_from_well_known(path: &Utf8Path) -> Result {
+ // Be careful with definition files, because `Path::extension()` only
+ // returns the extension after the _last_ dot:
+ let file_name = path.file_name().ok_or(FileSourceError::MissingFileName)?;
+ if file_name.ends_with(".module.css") {
+ return Self::try_from_extension("module.css");
+ }
+
+ match path.extension() {
+ Some(extension) => Self::try_from_extension(extension),
+ None => Err(FileSourceError::MissingFileExtension),
+ }
}
/// Try to return the CSS file source corresponding to this file extension
@@ -94,6 +147,7 @@ impl CssFileSource {
// We assume the file extension is normalized to lowercase
match extension {
"css" => Ok(Self::css()),
+ "module.css" => Ok(Self::new_css_modules()),
_ => Err(FileSourceError::UnknownExtension),
}
}
diff --git a/crates/biome_css_syntax/src/generated/kind.rs b/crates/biome_css_syntax/src/generated/kind.rs
index fa5aef1c0f87..3e74569794b1 100644
--- a/crates/biome_css_syntax/src/generated/kind.rs
+++ b/crates/biome_css_syntax/src/generated/kind.rs
@@ -67,6 +67,8 @@ pub enum CssSyntaxKind {
DIR_KW,
LOCAL_KW,
GLOBAL_KW,
+ SLOTTED_KW,
+ DEEP_KW,
ANY_KW,
CURRENT_KW,
PAST_KW,
@@ -702,6 +704,8 @@ impl CssSyntaxKind {
"dir" => DIR_KW,
"local" => LOCAL_KW,
"global" => GLOBAL_KW,
+ "slotted" => SLOTTED_KW,
+ "deep" => DEEP_KW,
"any" => ANY_KW,
"current" => CURRENT_KW,
"past" => PAST_KW,
@@ -948,6 +952,8 @@ impl CssSyntaxKind {
DIR_KW => "dir",
LOCAL_KW => "local",
GLOBAL_KW => "global",
+ SLOTTED_KW => "slotted",
+ DEEP_KW => "deep",
ANY_KW => "any",
CURRENT_KW => "current",
PAST_KW => "past",
@@ -1142,4 +1148,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 } ; [