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 } ; [