diff --git a/.changeset/shaggy-keys-camp.md b/.changeset/shaggy-keys-camp.md
new file mode 100644
index 000000000000..043a232f9aec
--- /dev/null
+++ b/.changeset/shaggy-keys-camp.md
@@ -0,0 +1,21 @@
+---
+"@biomejs/biome": patch
+---
+
+Added the rule [`noNextAsyncClientComponent`](https://biomejs.dev/linter/rules/no-next-async-client-component).
+
+This rule prevents the use of async functions for client components in Next.js applications. Client components marked with "use client" directive should not be async as this can cause hydration mismatches, break component rendering lifecycle, and lead to unexpected behavior with React's concurrent features.
+
+```jsx
+"use client";
+
+// Invalid - async client component
+export default async function MyComponent() {
+ return
Hello
;
+}
+
+// Valid - synchronous client component
+export default function MyComponent() {
+ return Hello
;
+}
+```
diff --git a/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs b/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs
index f9fd9b06b4c3..89038b0c5f56 100644
--- a/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs
+++ b/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs
@@ -61,6 +61,18 @@ pub(crate) fn migrate_eslint_any_rule(
.get_or_insert(Default::default());
rule.set_level(rule.level().max(rule_severity.into()));
}
+ "@next/no-async-client-component" => {
+ if !options.include_nursery {
+ results.add(eslint_name, eslint_to_biome::RuleMigrationResult::Nursery);
+ return false;
+ }
+ let group = rules.nursery.get_or_insert_with(Default::default);
+ let rule = group
+ .unwrap_group_as_mut()
+ .no_next_async_client_component
+ .get_or_insert(Default::default());
+ rule.set_level(rule.level().max(rule_severity.into()));
+ }
"@next/no-document-import-in-page" => {
let group = rules.suspicious.get_or_insert_with(Default::default);
let rule = group
diff --git a/crates/biome_configuration/src/analyzer/linter/rules.rs b/crates/biome_configuration/src/analyzer/linter/rules.rs
index ba29a254ffd3..d076cc9eb01f 100644
--- a/crates/biome_configuration/src/analyzer/linter/rules.rs
+++ b/crates/biome_configuration/src/analyzer/linter/rules.rs
@@ -212,6 +212,7 @@ pub enum RuleName {
NoNegationElse,
NoNestedComponentDefinitions,
NoNestedTernary,
+ NoNextAsyncClientComponent,
NoNodejsModules,
NoNonNullAssertedOptionalChain,
NoNonNullAssertion,
@@ -563,6 +564,7 @@ impl RuleName {
Self::NoNegationElse => "noNegationElse",
Self::NoNestedComponentDefinitions => "noNestedComponentDefinitions",
Self::NoNestedTernary => "noNestedTernary",
+ Self::NoNextAsyncClientComponent => "noNextAsyncClientComponent",
Self::NoNodejsModules => "noNodejsModules",
Self::NoNonNullAssertedOptionalChain => "noNonNullAssertedOptionalChain",
Self::NoNonNullAssertion => "noNonNullAssertion",
@@ -914,6 +916,7 @@ impl RuleName {
Self::NoNegationElse => RuleGroup::Style,
Self::NoNestedComponentDefinitions => RuleGroup::Nursery,
Self::NoNestedTernary => RuleGroup::Style,
+ Self::NoNextAsyncClientComponent => RuleGroup::Nursery,
Self::NoNodejsModules => RuleGroup::Correctness,
Self::NoNonNullAssertedOptionalChain => RuleGroup::Nursery,
Self::NoNonNullAssertion => RuleGroup::Style,
@@ -1270,6 +1273,7 @@ impl std::str::FromStr for RuleName {
"noNegationElse" => Ok(Self::NoNegationElse),
"noNestedComponentDefinitions" => Ok(Self::NoNestedComponentDefinitions),
"noNestedTernary" => Ok(Self::NoNestedTernary),
+ "noNextAsyncClientComponent" => Ok(Self::NoNextAsyncClientComponent),
"noNodejsModules" => Ok(Self::NoNodejsModules),
"noNonNullAssertedOptionalChain" => Ok(Self::NoNonNullAssertedOptionalChain),
"noNonNullAssertion" => Ok(Self::NoNonNullAssertion),
@@ -4302,7 +4306,7 @@ impl From for Correctness {
#[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 Nursery { # [doc = r" It enables the recommended rules for this group"] # [serde (skip_serializing_if = "Option::is_none")] pub recommended : Option < bool > , # [doc = "Disallow await inside loops."] # [serde (skip_serializing_if = "Option::is_none")] pub no_await_in_loop : Option < RuleConfiguration < biome_rule_options :: no_await_in_loop :: NoAwaitInLoopOptions >> , # [doc = "Disallow bitwise operators."] # [serde (skip_serializing_if = "Option::is_none")] pub no_bitwise_operators : Option < RuleConfiguration < biome_rule_options :: no_bitwise_operators :: NoBitwiseOperatorsOptions >> , # [doc = "Disallow expressions where the operation doesn't affect the value"] # [serde (skip_serializing_if = "Option::is_none")] pub no_constant_binary_expression : Option < RuleConfiguration < biome_rule_options :: no_constant_binary_expression :: NoConstantBinaryExpressionOptions >> , # [doc = "Disallow destructuring props inside JSX components in Solid projects."] # [serde (skip_serializing_if = "Option::is_none")] pub no_destructured_props : Option < RuleConfiguration < biome_rule_options :: no_destructured_props :: NoDestructuredPropsOptions >> , # [doc = "Restrict the number of lines of code in a function."] # [serde (skip_serializing_if = "Option::is_none")] pub no_excessive_lines_per_function : Option < RuleConfiguration < biome_rule_options :: no_excessive_lines_per_function :: NoExcessiveLinesPerFunctionOptions >> , # [doc = "Require Promise-like statements to be handled appropriately."] # [serde (skip_serializing_if = "Option::is_none")] pub no_floating_promises : Option < RuleFixConfiguration < biome_rule_options :: no_floating_promises :: NoFloatingPromisesOptions >> , # [doc = "Disallow the use of __dirname and __filename in the global scope."] # [serde (skip_serializing_if = "Option::is_none")] pub no_global_dirname_filename : Option < RuleFixConfiguration < biome_rule_options :: no_global_dirname_filename :: NoGlobalDirnameFilenameOptions >> , # [doc = "Disallow shorthand type conversions."] # [serde (skip_serializing_if = "Option::is_none")] pub no_implicit_coercion : Option < RuleFixConfiguration < biome_rule_options :: no_implicit_coercion :: NoImplicitCoercionOptions >> , # [doc = "Prevent import cycles."] # [serde (skip_serializing_if = "Option::is_none")] pub no_import_cycles : Option < RuleConfiguration < biome_rule_options :: no_import_cycles :: NoImportCyclesOptions >> , # [doc = "Disallow the use of the !important style."] # [serde (skip_serializing_if = "Option::is_none")] pub no_important_styles : Option < RuleFixConfiguration < biome_rule_options :: no_important_styles :: NoImportantStylesOptions >> , # [doc = "Reports usage of \"magic numbers\" — numbers used directly instead of being assigned to named constants."] # [serde (skip_serializing_if = "Option::is_none")] pub no_magic_numbers : Option < RuleConfiguration < biome_rule_options :: no_magic_numbers :: NoMagicNumbersOptions >> , # [doc = "Disallow Promises to be used in places where they are almost certainly a mistake."] # [serde (skip_serializing_if = "Option::is_none")] pub no_misused_promises : Option < RuleFixConfiguration < biome_rule_options :: no_misused_promises :: NoMisusedPromisesOptions >> , # [doc = "Disallows defining React components inside other components."] # [serde (skip_serializing_if = "Option::is_none")] pub no_nested_component_definitions : Option < RuleConfiguration < biome_rule_options :: no_nested_component_definitions :: NoNestedComponentDefinitionsOptions >> , # [doc = "Disallow non-null assertions after optional chaining expressions."] # [serde (skip_serializing_if = "Option::is_none")] pub no_non_null_asserted_optional_chain : Option < RuleConfiguration < biome_rule_options :: no_non_null_asserted_optional_chain :: NoNonNullAssertedOptionalChainOptions >> , # [doc = "Disallow use event handlers on non-interactive elements."] # [serde (skip_serializing_if = "Option::is_none")] pub no_noninteractive_element_interactions : Option < RuleConfiguration < biome_rule_options :: no_noninteractive_element_interactions :: NoNoninteractiveElementInteractionsOptions >> , # [doc = "Disallow the use of process global."] # [serde (skip_serializing_if = "Option::is_none")] pub no_process_global : Option < RuleFixConfiguration < biome_rule_options :: no_process_global :: NoProcessGlobalOptions >> , # [doc = "Disallow the use if quickfix.biome inside editor settings file."] # [serde (skip_serializing_if = "Option::is_none")] pub no_quickfix_biome : Option < RuleFixConfiguration < biome_rule_options :: no_quickfix_biome :: NoQuickfixBiomeOptions >> , # [doc = "Disallow useVisibleTask$() functions in Qwik components."] # [serde (skip_serializing_if = "Option::is_none")] pub no_qwik_use_visible_task : Option < RuleConfiguration < biome_rule_options :: no_qwik_use_visible_task :: NoQwikUseVisibleTaskOptions >> , # [doc = "Disallow assigning to React component props."] # [serde (skip_serializing_if = "Option::is_none")] pub no_react_prop_assign : Option < RuleConfiguration < biome_rule_options :: no_react_prop_assign :: NoReactPropAssignOptions >> , # [doc = "Disallow the use of configured elements."] # [serde (skip_serializing_if = "Option::is_none")] pub no_restricted_elements : Option < RuleConfiguration < biome_rule_options :: no_restricted_elements :: NoRestrictedElementsOptions >> , # [doc = "Disallow usage of sensitive data such as API keys and tokens."] # [serde (skip_serializing_if = "Option::is_none")] pub no_secrets : Option < RuleConfiguration < biome_rule_options :: no_secrets :: NoSecretsOptions >> , # [doc = "Disallow variable declarations from shadowing variables declared in the outer scope."] # [serde (skip_serializing_if = "Option::is_none")] pub no_shadow : Option < RuleConfiguration < biome_rule_options :: no_shadow :: NoShadowOptions >> , # [doc = "Prevents the use of the TypeScript directive @ts-ignore."] # [serde (skip_serializing_if = "Option::is_none")] pub no_ts_ignore : Option < RuleFixConfiguration < biome_rule_options :: no_ts_ignore :: NoTsIgnoreOptions >> , # [doc = "Disallow let or var variables that are read but never assigned."] # [serde (skip_serializing_if = "Option::is_none")] pub no_unassigned_variables : Option < RuleConfiguration < biome_rule_options :: no_unassigned_variables :: NoUnassignedVariablesOptions >> , # [doc = "Disallow unknown at-rules."] # [serde (skip_serializing_if = "Option::is_none")] pub no_unknown_at_rule : Option < RuleConfiguration < biome_rule_options :: no_unknown_at_rule :: NoUnknownAtRuleOptions >> , # [doc = "Disallow unnecessary type-based conditions that can be statically determined as redundant."] # [serde (skip_serializing_if = "Option::is_none")] pub no_unnecessary_conditions : Option < RuleConfiguration < biome_rule_options :: no_unnecessary_conditions :: NoUnnecessaryConditionsOptions >> , # [doc = "Warn when importing non-existing exports."] # [serde (skip_serializing_if = "Option::is_none")] pub no_unresolved_imports : Option < RuleConfiguration < biome_rule_options :: no_unresolved_imports :: NoUnresolvedImportsOptions >> , # [doc = "Prevent duplicate polyfills from Polyfill.io."] # [serde (skip_serializing_if = "Option::is_none")] pub no_unwanted_polyfillio : Option < RuleConfiguration < biome_rule_options :: no_unwanted_polyfillio :: NoUnwantedPolyfillioOptions >> , # [doc = "Disallow useless backreferences in regular expression literals that always match an empty string."] # [serde (skip_serializing_if = "Option::is_none")] pub no_useless_backref_in_regex : Option < RuleConfiguration < biome_rule_options :: no_useless_backref_in_regex :: NoUselessBackrefInRegexOptions >> , # [doc = "Disallow unnecessary escapes in string literals."] # [serde (skip_serializing_if = "Option::is_none")] pub no_useless_escape_in_string : Option < RuleFixConfiguration < biome_rule_options :: no_useless_escape_in_string :: NoUselessEscapeInStringOptions >> , # [doc = "Disallow the use of useless undefined."] # [serde (skip_serializing_if = "Option::is_none")] pub no_useless_undefined : Option < RuleFixConfiguration < biome_rule_options :: no_useless_undefined :: NoUselessUndefinedOptions >> , # [doc = "Enforce that Vue component data options are declared as functions."] # [serde (skip_serializing_if = "Option::is_none")] pub no_vue_data_object_declaration : Option < RuleFixConfiguration < biome_rule_options :: no_vue_data_object_declaration :: NoVueDataObjectDeclarationOptions >> , # [doc = "Disallow reserved keys in Vue component data and computed properties."] # [serde (skip_serializing_if = "Option::is_none")] pub no_vue_reserved_keys : Option < RuleConfiguration < biome_rule_options :: no_vue_reserved_keys :: NoVueReservedKeysOptions >> , # [doc = "Disallow reserved names to be used as props."] # [serde (skip_serializing_if = "Option::is_none")] pub no_vue_reserved_props : Option < RuleConfiguration < biome_rule_options :: no_vue_reserved_props :: NoVueReservedPropsOptions >> , # [doc = "Enforce that getters and setters for the same property are adjacent in class and object definitions."] # [serde (skip_serializing_if = "Option::is_none")] pub use_adjacent_getter_setter : Option < RuleConfiguration < biome_rule_options :: use_adjacent_getter_setter :: UseAdjacentGetterSetterOptions >> , # [doc = "Enforces href attribute for \\ elements."] # [serde (skip_serializing_if = "Option::is_none")] pub use_anchor_href : Option < RuleConfiguration < biome_rule_options :: use_anchor_href :: UseAnchorHrefOptions >> , # [doc = "Require the consistent declaration of object literals. Defaults to explicit definitions."] # [serde (skip_serializing_if = "Option::is_none")] pub use_consistent_object_definition : Option < RuleFixConfiguration < biome_rule_options :: use_consistent_object_definition :: UseConsistentObjectDefinitionOptions >> , # [doc = "Use static Response methods instead of new Response() constructor when possible."] # [serde (skip_serializing_if = "Option::is_none")] pub use_consistent_response : Option < RuleFixConfiguration < biome_rule_options :: use_consistent_response :: UseConsistentResponseOptions >> , # [doc = "Enforce type definitions to consistently use either interface or type."] # [serde (skip_serializing_if = "Option::is_none")] pub use_consistent_type_definitions : Option < RuleFixConfiguration < biome_rule_options :: use_consistent_type_definitions :: UseConsistentTypeDefinitionsOptions >> , # [doc = "Require switch-case statements to be exhaustive."] # [serde (skip_serializing_if = "Option::is_none")] pub use_exhaustive_switch_cases : Option < RuleFixConfiguration < biome_rule_options :: use_exhaustive_switch_cases :: UseExhaustiveSwitchCasesOptions >> , # [doc = "Enforce types in functions, methods, variables, and parameters."] # [serde (skip_serializing_if = "Option::is_none")] pub use_explicit_type : Option < RuleConfiguration < biome_rule_options :: use_explicit_type :: UseExplicitTypeOptions >> , # [doc = "Require that all exports are declared after all non-export statements."] # [serde (skip_serializing_if = "Option::is_none")] pub use_exports_last : Option < RuleConfiguration < biome_rule_options :: use_exports_last :: UseExportsLastOptions >> , # [doc = "Enforce using Solid's \\ component for mapping an array to JSX elements."] # [serde (skip_serializing_if = "Option::is_none")] pub use_for_component : Option < RuleConfiguration < biome_rule_options :: use_for_component :: UseForComponentOptions >> , # [doc = "Ensure the preconnect attribute is used when using Google Fonts."] # [serde (skip_serializing_if = "Option::is_none")] pub use_google_font_preconnect : Option < RuleFixConfiguration < biome_rule_options :: use_google_font_preconnect :: UseGoogleFontPreconnectOptions >> , # [doc = "Enforces that \\
elements have both width and height attributes."] # [serde (skip_serializing_if = "Option::is_none")] pub use_image_size : Option < RuleConfiguration < biome_rule_options :: use_image_size :: UseImageSizeOptions >> , # [doc = "Prefer Array#{indexOf,lastIndexOf}() over Array#{findIndex,findLastIndex}() when looking for the index of an item."] # [serde (skip_serializing_if = "Option::is_none")] pub use_index_of : Option < RuleFixConfiguration < biome_rule_options :: use_index_of :: UseIndexOfOptions >> , # [doc = "Enforce consistent return values in iterable callbacks."] # [serde (skip_serializing_if = "Option::is_none")] pub use_iterable_callback_return : Option < RuleConfiguration < biome_rule_options :: use_iterable_callback_return :: UseIterableCallbackReturnOptions >> , # [doc = "Enforces the use of with { type: \"json\" } for JSON module imports."] # [serde (skip_serializing_if = "Option::is_none")] pub use_json_import_attribute : Option < RuleFixConfiguration < biome_rule_options :: use_json_import_attribute :: UseJsonImportAttributeOptions >> , # [doc = "Enforce a maximum number of parameters in function definitions."] # [serde (skip_serializing_if = "Option::is_none")] pub use_max_params : Option < RuleConfiguration < biome_rule_options :: use_max_params :: UseMaxParamsOptions >> , # [doc = "Enforce specifying the name of GraphQL operations."] # [serde (skip_serializing_if = "Option::is_none")] pub use_named_operation : Option < RuleFixConfiguration < biome_rule_options :: use_named_operation :: UseNamedOperationOptions >> , # [doc = "Validates that all enum values are capitalized."] # [serde (skip_serializing_if = "Option::is_none")] pub use_naming_convention : Option < RuleConfiguration < biome_rule_options :: use_naming_convention :: UseNamingConventionOptions >> , # [doc = "Enforce the use of numeric separators in numeric literals."] # [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."] # [serde (skip_serializing_if = "Option::is_none")] pub use_object_spread : Option < RuleFixConfiguration < biome_rule_options :: use_object_spread :: UseObjectSpreadOptions >> , # [doc = "Enforce the consistent use of the radix argument when using parseInt()."] # [serde (skip_serializing_if = "Option::is_none")] pub use_parse_int_radix : Option < RuleFixConfiguration < biome_rule_options :: use_parse_int_radix :: UseParseIntRadixOptions >> , # [doc = "Prefer using the class prop as a classlist over the classnames helper."] # [serde (skip_serializing_if = "Option::is_none")] pub use_qwik_classlist : Option < RuleConfiguration < biome_rule_options :: use_qwik_classlist :: UseQwikClasslistOptions >> , # [doc = "Enforce that components are defined as functions and never as classes."] # [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."] # [serde (skip_serializing_if = "Option::is_none")] pub use_readonly_class_properties : Option < RuleFixConfiguration < biome_rule_options :: use_readonly_class_properties :: UseReadonlyClassPropertiesOptions >> , # [doc = "Enforce JSDoc comment lines to start with a single asterisk, except for the first one."] # [serde (skip_serializing_if = "Option::is_none")] pub use_single_js_doc_asterisk : Option < RuleFixConfiguration < biome_rule_options :: use_single_js_doc_asterisk :: UseSingleJsDocAsteriskOptions >> , # [doc = "Enforce the sorting of CSS utility classes."] # [serde (skip_serializing_if = "Option::is_none")] pub use_sorted_classes : Option < RuleFixConfiguration < biome_rule_options :: use_sorted_classes :: UseSortedClassesOptions >> , # [doc = "Require a description parameter for the Symbol()."] # [serde (skip_serializing_if = "Option::is_none")] pub use_symbol_description : Option < RuleConfiguration < biome_rule_options :: use_symbol_description :: UseSymbolDescriptionOptions >> , # [doc = "Disallow overload signatures that can be unified into a single signature."] # [serde (skip_serializing_if = "Option::is_none")] pub use_unified_type_signature : Option < RuleFixConfiguration < biome_rule_options :: use_unified_type_signature :: UseUnifiedTypeSignatureOptions >> , # [doc = "Prevent the usage of static string literal id attribute on elements."] # [serde (skip_serializing_if = "Option::is_none")] pub use_unique_element_ids : Option < RuleConfiguration < biome_rule_options :: use_unique_element_ids :: UseUniqueElementIdsOptions >> }
+pub struct Nursery { # [doc = r" It enables the recommended rules for this group"] # [serde (skip_serializing_if = "Option::is_none")] pub recommended : Option < bool > , # [doc = "Disallow await inside loops."] # [serde (skip_serializing_if = "Option::is_none")] pub no_await_in_loop : Option < RuleConfiguration < biome_rule_options :: no_await_in_loop :: NoAwaitInLoopOptions >> , # [doc = "Disallow bitwise operators."] # [serde (skip_serializing_if = "Option::is_none")] pub no_bitwise_operators : Option < RuleConfiguration < biome_rule_options :: no_bitwise_operators :: NoBitwiseOperatorsOptions >> , # [doc = "Disallow expressions where the operation doesn't affect the value"] # [serde (skip_serializing_if = "Option::is_none")] pub no_constant_binary_expression : Option < RuleConfiguration < biome_rule_options :: no_constant_binary_expression :: NoConstantBinaryExpressionOptions >> , # [doc = "Disallow destructuring props inside JSX components in Solid projects."] # [serde (skip_serializing_if = "Option::is_none")] pub no_destructured_props : Option < RuleConfiguration < biome_rule_options :: no_destructured_props :: NoDestructuredPropsOptions >> , # [doc = "Restrict the number of lines of code in a function."] # [serde (skip_serializing_if = "Option::is_none")] pub no_excessive_lines_per_function : Option < RuleConfiguration < biome_rule_options :: no_excessive_lines_per_function :: NoExcessiveLinesPerFunctionOptions >> , # [doc = "Require Promise-like statements to be handled appropriately."] # [serde (skip_serializing_if = "Option::is_none")] pub no_floating_promises : Option < RuleFixConfiguration < biome_rule_options :: no_floating_promises :: NoFloatingPromisesOptions >> , # [doc = "Disallow the use of __dirname and __filename in the global scope."] # [serde (skip_serializing_if = "Option::is_none")] pub no_global_dirname_filename : Option < RuleFixConfiguration < biome_rule_options :: no_global_dirname_filename :: NoGlobalDirnameFilenameOptions >> , # [doc = "Disallow shorthand type conversions."] # [serde (skip_serializing_if = "Option::is_none")] pub no_implicit_coercion : Option < RuleFixConfiguration < biome_rule_options :: no_implicit_coercion :: NoImplicitCoercionOptions >> , # [doc = "Prevent import cycles."] # [serde (skip_serializing_if = "Option::is_none")] pub no_import_cycles : Option < RuleConfiguration < biome_rule_options :: no_import_cycles :: NoImportCyclesOptions >> , # [doc = "Disallow the use of the !important style."] # [serde (skip_serializing_if = "Option::is_none")] pub no_important_styles : Option < RuleFixConfiguration < biome_rule_options :: no_important_styles :: NoImportantStylesOptions >> , # [doc = "Reports usage of \"magic numbers\" — numbers used directly instead of being assigned to named constants."] # [serde (skip_serializing_if = "Option::is_none")] pub no_magic_numbers : Option < RuleConfiguration < biome_rule_options :: no_magic_numbers :: NoMagicNumbersOptions >> , # [doc = "Disallow Promises to be used in places where they are almost certainly a mistake."] # [serde (skip_serializing_if = "Option::is_none")] pub no_misused_promises : Option < RuleFixConfiguration < biome_rule_options :: no_misused_promises :: NoMisusedPromisesOptions >> , # [doc = "Disallows defining React components inside other components."] # [serde (skip_serializing_if = "Option::is_none")] pub no_nested_component_definitions : Option < RuleConfiguration < biome_rule_options :: no_nested_component_definitions :: NoNestedComponentDefinitionsOptions >> , # [doc = "Prevent client components from being async functions."] # [serde (skip_serializing_if = "Option::is_none")] pub no_next_async_client_component : Option < RuleConfiguration < biome_rule_options :: no_next_async_client_component :: NoNextAsyncClientComponentOptions >> , # [doc = "Disallow non-null assertions after optional chaining expressions."] # [serde (skip_serializing_if = "Option::is_none")] pub no_non_null_asserted_optional_chain : Option < RuleConfiguration < biome_rule_options :: no_non_null_asserted_optional_chain :: NoNonNullAssertedOptionalChainOptions >> , # [doc = "Disallow use event handlers on non-interactive elements."] # [serde (skip_serializing_if = "Option::is_none")] pub no_noninteractive_element_interactions : Option < RuleConfiguration < biome_rule_options :: no_noninteractive_element_interactions :: NoNoninteractiveElementInteractionsOptions >> , # [doc = "Disallow the use of process global."] # [serde (skip_serializing_if = "Option::is_none")] pub no_process_global : Option < RuleFixConfiguration < biome_rule_options :: no_process_global :: NoProcessGlobalOptions >> , # [doc = "Disallow the use if quickfix.biome inside editor settings file."] # [serde (skip_serializing_if = "Option::is_none")] pub no_quickfix_biome : Option < RuleFixConfiguration < biome_rule_options :: no_quickfix_biome :: NoQuickfixBiomeOptions >> , # [doc = "Disallow useVisibleTask$() functions in Qwik components."] # [serde (skip_serializing_if = "Option::is_none")] pub no_qwik_use_visible_task : Option < RuleConfiguration < biome_rule_options :: no_qwik_use_visible_task :: NoQwikUseVisibleTaskOptions >> , # [doc = "Disallow assigning to React component props."] # [serde (skip_serializing_if = "Option::is_none")] pub no_react_prop_assign : Option < RuleConfiguration < biome_rule_options :: no_react_prop_assign :: NoReactPropAssignOptions >> , # [doc = "Disallow the use of configured elements."] # [serde (skip_serializing_if = "Option::is_none")] pub no_restricted_elements : Option < RuleConfiguration < biome_rule_options :: no_restricted_elements :: NoRestrictedElementsOptions >> , # [doc = "Disallow usage of sensitive data such as API keys and tokens."] # [serde (skip_serializing_if = "Option::is_none")] pub no_secrets : Option < RuleConfiguration < biome_rule_options :: no_secrets :: NoSecretsOptions >> , # [doc = "Disallow variable declarations from shadowing variables declared in the outer scope."] # [serde (skip_serializing_if = "Option::is_none")] pub no_shadow : Option < RuleConfiguration < biome_rule_options :: no_shadow :: NoShadowOptions >> , # [doc = "Prevents the use of the TypeScript directive @ts-ignore."] # [serde (skip_serializing_if = "Option::is_none")] pub no_ts_ignore : Option < RuleFixConfiguration < biome_rule_options :: no_ts_ignore :: NoTsIgnoreOptions >> , # [doc = "Disallow let or var variables that are read but never assigned."] # [serde (skip_serializing_if = "Option::is_none")] pub no_unassigned_variables : Option < RuleConfiguration < biome_rule_options :: no_unassigned_variables :: NoUnassignedVariablesOptions >> , # [doc = "Disallow unknown at-rules."] # [serde (skip_serializing_if = "Option::is_none")] pub no_unknown_at_rule : Option < RuleConfiguration < biome_rule_options :: no_unknown_at_rule :: NoUnknownAtRuleOptions >> , # [doc = "Disallow unnecessary type-based conditions that can be statically determined as redundant."] # [serde (skip_serializing_if = "Option::is_none")] pub no_unnecessary_conditions : Option < RuleConfiguration < biome_rule_options :: no_unnecessary_conditions :: NoUnnecessaryConditionsOptions >> , # [doc = "Warn when importing non-existing exports."] # [serde (skip_serializing_if = "Option::is_none")] pub no_unresolved_imports : Option < RuleConfiguration < biome_rule_options :: no_unresolved_imports :: NoUnresolvedImportsOptions >> , # [doc = "Prevent duplicate polyfills from Polyfill.io."] # [serde (skip_serializing_if = "Option::is_none")] pub no_unwanted_polyfillio : Option < RuleConfiguration < biome_rule_options :: no_unwanted_polyfillio :: NoUnwantedPolyfillioOptions >> , # [doc = "Disallow useless backreferences in regular expression literals that always match an empty string."] # [serde (skip_serializing_if = "Option::is_none")] pub no_useless_backref_in_regex : Option < RuleConfiguration < biome_rule_options :: no_useless_backref_in_regex :: NoUselessBackrefInRegexOptions >> , # [doc = "Disallow unnecessary escapes in string literals."] # [serde (skip_serializing_if = "Option::is_none")] pub no_useless_escape_in_string : Option < RuleFixConfiguration < biome_rule_options :: no_useless_escape_in_string :: NoUselessEscapeInStringOptions >> , # [doc = "Disallow the use of useless undefined."] # [serde (skip_serializing_if = "Option::is_none")] pub no_useless_undefined : Option < RuleFixConfiguration < biome_rule_options :: no_useless_undefined :: NoUselessUndefinedOptions >> , # [doc = "Enforce that Vue component data options are declared as functions."] # [serde (skip_serializing_if = "Option::is_none")] pub no_vue_data_object_declaration : Option < RuleFixConfiguration < biome_rule_options :: no_vue_data_object_declaration :: NoVueDataObjectDeclarationOptions >> , # [doc = "Disallow reserved keys in Vue component data and computed properties."] # [serde (skip_serializing_if = "Option::is_none")] pub no_vue_reserved_keys : Option < RuleConfiguration < biome_rule_options :: no_vue_reserved_keys :: NoVueReservedKeysOptions >> , # [doc = "Disallow reserved names to be used as props."] # [serde (skip_serializing_if = "Option::is_none")] pub no_vue_reserved_props : Option < RuleConfiguration < biome_rule_options :: no_vue_reserved_props :: NoVueReservedPropsOptions >> , # [doc = "Enforce that getters and setters for the same property are adjacent in class and object definitions."] # [serde (skip_serializing_if = "Option::is_none")] pub use_adjacent_getter_setter : Option < RuleConfiguration < biome_rule_options :: use_adjacent_getter_setter :: UseAdjacentGetterSetterOptions >> , # [doc = "Enforces href attribute for \\ elements."] # [serde (skip_serializing_if = "Option::is_none")] pub use_anchor_href : Option < RuleConfiguration < biome_rule_options :: use_anchor_href :: UseAnchorHrefOptions >> , # [doc = "Require the consistent declaration of object literals. Defaults to explicit definitions."] # [serde (skip_serializing_if = "Option::is_none")] pub use_consistent_object_definition : Option < RuleFixConfiguration < biome_rule_options :: use_consistent_object_definition :: UseConsistentObjectDefinitionOptions >> , # [doc = "Use static Response methods instead of new Response() constructor when possible."] # [serde (skip_serializing_if = "Option::is_none")] pub use_consistent_response : Option < RuleFixConfiguration < biome_rule_options :: use_consistent_response :: UseConsistentResponseOptions >> , # [doc = "Enforce type definitions to consistently use either interface or type."] # [serde (skip_serializing_if = "Option::is_none")] pub use_consistent_type_definitions : Option < RuleFixConfiguration < biome_rule_options :: use_consistent_type_definitions :: UseConsistentTypeDefinitionsOptions >> , # [doc = "Require switch-case statements to be exhaustive."] # [serde (skip_serializing_if = "Option::is_none")] pub use_exhaustive_switch_cases : Option < RuleFixConfiguration < biome_rule_options :: use_exhaustive_switch_cases :: UseExhaustiveSwitchCasesOptions >> , # [doc = "Enforce types in functions, methods, variables, and parameters."] # [serde (skip_serializing_if = "Option::is_none")] pub use_explicit_type : Option < RuleConfiguration < biome_rule_options :: use_explicit_type :: UseExplicitTypeOptions >> , # [doc = "Require that all exports are declared after all non-export statements."] # [serde (skip_serializing_if = "Option::is_none")] pub use_exports_last : Option < RuleConfiguration < biome_rule_options :: use_exports_last :: UseExportsLastOptions >> , # [doc = "Enforce using Solid's \\ component for mapping an array to JSX elements."] # [serde (skip_serializing_if = "Option::is_none")] pub use_for_component : Option < RuleConfiguration < biome_rule_options :: use_for_component :: UseForComponentOptions >> , # [doc = "Ensure the preconnect attribute is used when using Google Fonts."] # [serde (skip_serializing_if = "Option::is_none")] pub use_google_font_preconnect : Option < RuleFixConfiguration < biome_rule_options :: use_google_font_preconnect :: UseGoogleFontPreconnectOptions >> , # [doc = "Enforces that \\
elements have both width and height attributes."] # [serde (skip_serializing_if = "Option::is_none")] pub use_image_size : Option < RuleConfiguration < biome_rule_options :: use_image_size :: UseImageSizeOptions >> , # [doc = "Prefer Array#{indexOf,lastIndexOf}() over Array#{findIndex,findLastIndex}() when looking for the index of an item."] # [serde (skip_serializing_if = "Option::is_none")] pub use_index_of : Option < RuleFixConfiguration < biome_rule_options :: use_index_of :: UseIndexOfOptions >> , # [doc = "Enforce consistent return values in iterable callbacks."] # [serde (skip_serializing_if = "Option::is_none")] pub use_iterable_callback_return : Option < RuleConfiguration < biome_rule_options :: use_iterable_callback_return :: UseIterableCallbackReturnOptions >> , # [doc = "Enforces the use of with { type: \"json\" } for JSON module imports."] # [serde (skip_serializing_if = "Option::is_none")] pub use_json_import_attribute : Option < RuleFixConfiguration < biome_rule_options :: use_json_import_attribute :: UseJsonImportAttributeOptions >> , # [doc = "Enforce a maximum number of parameters in function definitions."] # [serde (skip_serializing_if = "Option::is_none")] pub use_max_params : Option < RuleConfiguration < biome_rule_options :: use_max_params :: UseMaxParamsOptions >> , # [doc = "Enforce specifying the name of GraphQL operations."] # [serde (skip_serializing_if = "Option::is_none")] pub use_named_operation : Option < RuleFixConfiguration < biome_rule_options :: use_named_operation :: UseNamedOperationOptions >> , # [doc = "Validates that all enum values are capitalized."] # [serde (skip_serializing_if = "Option::is_none")] pub use_naming_convention : Option < RuleConfiguration < biome_rule_options :: use_naming_convention :: UseNamingConventionOptions >> , # [doc = "Enforce the use of numeric separators in numeric literals."] # [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."] # [serde (skip_serializing_if = "Option::is_none")] pub use_object_spread : Option < RuleFixConfiguration < biome_rule_options :: use_object_spread :: UseObjectSpreadOptions >> , # [doc = "Enforce the consistent use of the radix argument when using parseInt()."] # [serde (skip_serializing_if = "Option::is_none")] pub use_parse_int_radix : Option < RuleFixConfiguration < biome_rule_options :: use_parse_int_radix :: UseParseIntRadixOptions >> , # [doc = "Prefer using the class prop as a classlist over the classnames helper."] # [serde (skip_serializing_if = "Option::is_none")] pub use_qwik_classlist : Option < RuleConfiguration < biome_rule_options :: use_qwik_classlist :: UseQwikClasslistOptions >> , # [doc = "Enforce that components are defined as functions and never as classes."] # [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."] # [serde (skip_serializing_if = "Option::is_none")] pub use_readonly_class_properties : Option < RuleFixConfiguration < biome_rule_options :: use_readonly_class_properties :: UseReadonlyClassPropertiesOptions >> , # [doc = "Enforce JSDoc comment lines to start with a single asterisk, except for the first one."] # [serde (skip_serializing_if = "Option::is_none")] pub use_single_js_doc_asterisk : Option < RuleFixConfiguration < biome_rule_options :: use_single_js_doc_asterisk :: UseSingleJsDocAsteriskOptions >> , # [doc = "Enforce the sorting of CSS utility classes."] # [serde (skip_serializing_if = "Option::is_none")] pub use_sorted_classes : Option < RuleFixConfiguration < biome_rule_options :: use_sorted_classes :: UseSortedClassesOptions >> , # [doc = "Require a description parameter for the Symbol()."] # [serde (skip_serializing_if = "Option::is_none")] pub use_symbol_description : Option < RuleConfiguration < biome_rule_options :: use_symbol_description :: UseSymbolDescriptionOptions >> , # [doc = "Disallow overload signatures that can be unified into a single signature."] # [serde (skip_serializing_if = "Option::is_none")] pub use_unified_type_signature : Option < RuleFixConfiguration < biome_rule_options :: use_unified_type_signature :: UseUnifiedTypeSignatureOptions >> , # [doc = "Prevent the usage of static string literal id attribute on elements."] # [serde (skip_serializing_if = "Option::is_none")] pub use_unique_element_ids : Option < RuleConfiguration < biome_rule_options :: use_unique_element_ids :: UseUniqueElementIdsOptions >> }
impl Nursery {
const GROUP_NAME: &'static str = "nursery";
pub(crate) const GROUP_RULES: &'static [&'static str] = &[
@@ -4319,6 +4323,7 @@ impl Nursery {
"noMagicNumbers",
"noMisusedPromises",
"noNestedComponentDefinitions",
+ "noNextAsyncClientComponent",
"noNonNullAssertedOptionalChain",
"noNoninteractiveElementInteractions",
"noProcessGlobal",
@@ -4371,16 +4376,16 @@ impl Nursery {
];
const RECOMMENDED_RULES_AS_FILTERS: &'static [RuleFilter<'static>] = &[
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[9]),
- RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13]),
- RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16]),
- RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22]),
- RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24]),
- RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[28]),
+ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14]),
+ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17]),
+ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23]),
+ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25]),
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[29]),
- RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45]),
+ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[30]),
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46]),
- RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49]),
- RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[53]),
+ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47]),
+ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50]),
+ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[54]),
];
const ALL_RULES_AS_FILTERS: &'static [RuleFilter<'static>] = &[
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[0]),
@@ -4445,6 +4450,7 @@ impl Nursery {
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[59]),
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[60]),
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[61]),
+ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[62]),
];
}
impl RuleGroupExt for Nursery {
@@ -4521,251 +4527,256 @@ impl RuleGroupExt for Nursery {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12]));
}
}
- if let Some(rule) = self.no_non_null_asserted_optional_chain.as_ref() {
+ if let Some(rule) = self.no_next_async_client_component.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13]));
}
}
- if let Some(rule) = self.no_noninteractive_element_interactions.as_ref() {
+ if let Some(rule) = self.no_non_null_asserted_optional_chain.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14]));
}
}
- if let Some(rule) = self.no_process_global.as_ref() {
+ if let Some(rule) = self.no_noninteractive_element_interactions.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15]));
}
}
- if let Some(rule) = self.no_quickfix_biome.as_ref() {
+ if let Some(rule) = self.no_process_global.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16]));
}
}
- if let Some(rule) = self.no_qwik_use_visible_task.as_ref() {
+ if let Some(rule) = self.no_quickfix_biome.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17]));
}
}
- if let Some(rule) = self.no_react_prop_assign.as_ref() {
+ if let Some(rule) = self.no_qwik_use_visible_task.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18]));
}
}
- if let Some(rule) = self.no_restricted_elements.as_ref() {
+ if let Some(rule) = self.no_react_prop_assign.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19]));
}
}
- if let Some(rule) = self.no_secrets.as_ref() {
+ if let Some(rule) = self.no_restricted_elements.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20]));
}
}
- if let Some(rule) = self.no_shadow.as_ref() {
+ if let Some(rule) = self.no_secrets.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[21]));
}
}
- if let Some(rule) = self.no_ts_ignore.as_ref() {
+ if let Some(rule) = self.no_shadow.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22]));
}
}
- if let Some(rule) = self.no_unassigned_variables.as_ref() {
+ if let Some(rule) = self.no_ts_ignore.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23]));
}
}
- if let Some(rule) = self.no_unknown_at_rule.as_ref() {
+ if let Some(rule) = self.no_unassigned_variables.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24]));
}
}
- if let Some(rule) = self.no_unnecessary_conditions.as_ref() {
+ if let Some(rule) = self.no_unknown_at_rule.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25]));
}
}
- if let Some(rule) = self.no_unresolved_imports.as_ref() {
+ if let Some(rule) = self.no_unnecessary_conditions.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26]));
}
}
- if let Some(rule) = self.no_unwanted_polyfillio.as_ref() {
+ if let Some(rule) = self.no_unresolved_imports.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27]));
}
}
- if let Some(rule) = self.no_useless_backref_in_regex.as_ref() {
+ if let Some(rule) = self.no_unwanted_polyfillio.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[28]));
}
}
- if let Some(rule) = self.no_useless_escape_in_string.as_ref() {
+ if let Some(rule) = self.no_useless_backref_in_regex.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[29]));
}
}
- if let Some(rule) = self.no_useless_undefined.as_ref() {
+ if let Some(rule) = self.no_useless_escape_in_string.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[30]));
}
}
- if let Some(rule) = self.no_vue_data_object_declaration.as_ref() {
+ if let Some(rule) = self.no_useless_undefined.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[31]));
}
}
- if let Some(rule) = self.no_vue_reserved_keys.as_ref() {
+ if let Some(rule) = self.no_vue_data_object_declaration.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[32]));
}
}
- if let Some(rule) = self.no_vue_reserved_props.as_ref() {
+ if let Some(rule) = self.no_vue_reserved_keys.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[33]));
}
}
- if let Some(rule) = self.use_adjacent_getter_setter.as_ref() {
+ if let Some(rule) = self.no_vue_reserved_props.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[34]));
}
}
- if let Some(rule) = self.use_anchor_href.as_ref() {
+ if let Some(rule) = self.use_adjacent_getter_setter.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[35]));
}
}
- if let Some(rule) = self.use_consistent_object_definition.as_ref() {
+ if let Some(rule) = self.use_anchor_href.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[36]));
}
}
- if let Some(rule) = self.use_consistent_response.as_ref() {
+ if let Some(rule) = self.use_consistent_object_definition.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[37]));
}
}
- if let Some(rule) = self.use_consistent_type_definitions.as_ref() {
+ if let Some(rule) = self.use_consistent_response.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[38]));
}
}
- if let Some(rule) = self.use_exhaustive_switch_cases.as_ref() {
+ if let Some(rule) = self.use_consistent_type_definitions.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[39]));
}
}
- if let Some(rule) = self.use_explicit_type.as_ref() {
+ if let Some(rule) = self.use_exhaustive_switch_cases.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[40]));
}
}
- if let Some(rule) = self.use_exports_last.as_ref() {
+ if let Some(rule) = self.use_explicit_type.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[41]));
}
}
- if let Some(rule) = self.use_for_component.as_ref() {
+ if let Some(rule) = self.use_exports_last.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42]));
}
}
- if let Some(rule) = self.use_google_font_preconnect.as_ref() {
+ if let Some(rule) = self.use_for_component.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43]));
}
}
- if let Some(rule) = self.use_image_size.as_ref() {
+ if let Some(rule) = self.use_google_font_preconnect.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44]));
}
}
- if let Some(rule) = self.use_index_of.as_ref() {
+ if let Some(rule) = self.use_image_size.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45]));
}
}
- if let Some(rule) = self.use_iterable_callback_return.as_ref() {
+ if let Some(rule) = self.use_index_of.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46]));
}
}
- if let Some(rule) = self.use_json_import_attribute.as_ref() {
+ if let Some(rule) = self.use_iterable_callback_return.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47]));
}
}
- if let Some(rule) = self.use_max_params.as_ref() {
+ if let Some(rule) = self.use_json_import_attribute.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48]));
}
}
- if let Some(rule) = self.use_named_operation.as_ref() {
+ if let Some(rule) = self.use_max_params.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49]));
}
}
- if let Some(rule) = self.use_naming_convention.as_ref() {
+ if let Some(rule) = self.use_named_operation.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50]));
}
}
- if let Some(rule) = self.use_numeric_separators.as_ref() {
+ if let Some(rule) = self.use_naming_convention.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[51]));
}
}
- if let Some(rule) = self.use_object_spread.as_ref() {
+ if let Some(rule) = self.use_numeric_separators.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[52]));
}
}
- if let Some(rule) = self.use_parse_int_radix.as_ref() {
+ if let Some(rule) = self.use_object_spread.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[53]));
}
}
- if let Some(rule) = self.use_qwik_classlist.as_ref() {
+ if let Some(rule) = self.use_parse_int_radix.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[54]));
}
}
- if let Some(rule) = self.use_react_function_components.as_ref() {
+ if let Some(rule) = self.use_qwik_classlist.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[55]));
}
}
- if let Some(rule) = self.use_readonly_class_properties.as_ref() {
+ if let Some(rule) = self.use_react_function_components.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[56]));
}
}
- if let Some(rule) = self.use_single_js_doc_asterisk.as_ref() {
+ if let Some(rule) = self.use_readonly_class_properties.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[57]));
}
}
- if let Some(rule) = self.use_sorted_classes.as_ref() {
+ if let Some(rule) = self.use_single_js_doc_asterisk.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[58]));
}
}
- if let Some(rule) = self.use_symbol_description.as_ref() {
+ if let Some(rule) = self.use_sorted_classes.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[59]));
}
}
- if let Some(rule) = self.use_unified_type_signature.as_ref() {
+ if let Some(rule) = self.use_symbol_description.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[60]));
}
}
- if let Some(rule) = self.use_unique_element_ids.as_ref() {
+ if let Some(rule) = self.use_unified_type_signature.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[61]));
}
}
+ if let Some(rule) = self.use_unique_element_ids.as_ref() {
+ if rule.is_enabled() {
+ index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[62]));
+ }
+ }
index_set
}
fn get_disabled_rules(&self) -> FxHashSet> {
@@ -4835,251 +4846,256 @@ impl RuleGroupExt for Nursery {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12]));
}
}
- if let Some(rule) = self.no_non_null_asserted_optional_chain.as_ref() {
+ if let Some(rule) = self.no_next_async_client_component.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13]));
}
}
- if let Some(rule) = self.no_noninteractive_element_interactions.as_ref() {
+ if let Some(rule) = self.no_non_null_asserted_optional_chain.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14]));
}
}
- if let Some(rule) = self.no_process_global.as_ref() {
+ if let Some(rule) = self.no_noninteractive_element_interactions.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15]));
}
}
- if let Some(rule) = self.no_quickfix_biome.as_ref() {
+ if let Some(rule) = self.no_process_global.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16]));
}
}
- if let Some(rule) = self.no_qwik_use_visible_task.as_ref() {
+ if let Some(rule) = self.no_quickfix_biome.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17]));
}
}
- if let Some(rule) = self.no_react_prop_assign.as_ref() {
+ if let Some(rule) = self.no_qwik_use_visible_task.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18]));
}
}
- if let Some(rule) = self.no_restricted_elements.as_ref() {
+ if let Some(rule) = self.no_react_prop_assign.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19]));
}
}
- if let Some(rule) = self.no_secrets.as_ref() {
+ if let Some(rule) = self.no_restricted_elements.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20]));
}
}
- if let Some(rule) = self.no_shadow.as_ref() {
+ if let Some(rule) = self.no_secrets.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[21]));
}
}
- if let Some(rule) = self.no_ts_ignore.as_ref() {
+ if let Some(rule) = self.no_shadow.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22]));
}
}
- if let Some(rule) = self.no_unassigned_variables.as_ref() {
+ if let Some(rule) = self.no_ts_ignore.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23]));
}
}
- if let Some(rule) = self.no_unknown_at_rule.as_ref() {
+ if let Some(rule) = self.no_unassigned_variables.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24]));
}
}
- if let Some(rule) = self.no_unnecessary_conditions.as_ref() {
+ if let Some(rule) = self.no_unknown_at_rule.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25]));
}
}
- if let Some(rule) = self.no_unresolved_imports.as_ref() {
+ if let Some(rule) = self.no_unnecessary_conditions.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26]));
}
}
- if let Some(rule) = self.no_unwanted_polyfillio.as_ref() {
+ if let Some(rule) = self.no_unresolved_imports.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27]));
}
}
- if let Some(rule) = self.no_useless_backref_in_regex.as_ref() {
+ if let Some(rule) = self.no_unwanted_polyfillio.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[28]));
}
}
- if let Some(rule) = self.no_useless_escape_in_string.as_ref() {
+ if let Some(rule) = self.no_useless_backref_in_regex.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[29]));
}
}
- if let Some(rule) = self.no_useless_undefined.as_ref() {
+ if let Some(rule) = self.no_useless_escape_in_string.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[30]));
}
}
- if let Some(rule) = self.no_vue_data_object_declaration.as_ref() {
+ if let Some(rule) = self.no_useless_undefined.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[31]));
}
}
- if let Some(rule) = self.no_vue_reserved_keys.as_ref() {
+ if let Some(rule) = self.no_vue_data_object_declaration.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[32]));
}
}
- if let Some(rule) = self.no_vue_reserved_props.as_ref() {
+ if let Some(rule) = self.no_vue_reserved_keys.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[33]));
}
}
- if let Some(rule) = self.use_adjacent_getter_setter.as_ref() {
+ if let Some(rule) = self.no_vue_reserved_props.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[34]));
}
}
- if let Some(rule) = self.use_anchor_href.as_ref() {
+ if let Some(rule) = self.use_adjacent_getter_setter.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[35]));
}
}
- if let Some(rule) = self.use_consistent_object_definition.as_ref() {
+ if let Some(rule) = self.use_anchor_href.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[36]));
}
}
- if let Some(rule) = self.use_consistent_response.as_ref() {
+ if let Some(rule) = self.use_consistent_object_definition.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[37]));
}
}
- if let Some(rule) = self.use_consistent_type_definitions.as_ref() {
+ if let Some(rule) = self.use_consistent_response.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[38]));
}
}
- if let Some(rule) = self.use_exhaustive_switch_cases.as_ref() {
+ if let Some(rule) = self.use_consistent_type_definitions.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[39]));
}
}
- if let Some(rule) = self.use_explicit_type.as_ref() {
+ if let Some(rule) = self.use_exhaustive_switch_cases.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[40]));
}
}
- if let Some(rule) = self.use_exports_last.as_ref() {
+ if let Some(rule) = self.use_explicit_type.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[41]));
}
}
- if let Some(rule) = self.use_for_component.as_ref() {
+ if let Some(rule) = self.use_exports_last.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42]));
}
}
- if let Some(rule) = self.use_google_font_preconnect.as_ref() {
+ if let Some(rule) = self.use_for_component.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43]));
}
}
- if let Some(rule) = self.use_image_size.as_ref() {
+ if let Some(rule) = self.use_google_font_preconnect.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44]));
}
}
- if let Some(rule) = self.use_index_of.as_ref() {
+ if let Some(rule) = self.use_image_size.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45]));
}
}
- if let Some(rule) = self.use_iterable_callback_return.as_ref() {
+ if let Some(rule) = self.use_index_of.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46]));
}
}
- if let Some(rule) = self.use_json_import_attribute.as_ref() {
+ if let Some(rule) = self.use_iterable_callback_return.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47]));
}
}
- if let Some(rule) = self.use_max_params.as_ref() {
+ if let Some(rule) = self.use_json_import_attribute.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48]));
}
}
- if let Some(rule) = self.use_named_operation.as_ref() {
+ if let Some(rule) = self.use_max_params.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49]));
}
}
- if let Some(rule) = self.use_naming_convention.as_ref() {
+ if let Some(rule) = self.use_named_operation.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50]));
}
}
- if let Some(rule) = self.use_numeric_separators.as_ref() {
+ if let Some(rule) = self.use_naming_convention.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[51]));
}
}
- if let Some(rule) = self.use_object_spread.as_ref() {
+ if let Some(rule) = self.use_numeric_separators.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[52]));
}
}
- if let Some(rule) = self.use_parse_int_radix.as_ref() {
+ if let Some(rule) = self.use_object_spread.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[53]));
}
}
- if let Some(rule) = self.use_qwik_classlist.as_ref() {
+ if let Some(rule) = self.use_parse_int_radix.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[54]));
}
}
- if let Some(rule) = self.use_react_function_components.as_ref() {
+ if let Some(rule) = self.use_qwik_classlist.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[55]));
}
}
- if let Some(rule) = self.use_readonly_class_properties.as_ref() {
+ if let Some(rule) = self.use_react_function_components.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[56]));
}
}
- if let Some(rule) = self.use_single_js_doc_asterisk.as_ref() {
+ if let Some(rule) = self.use_readonly_class_properties.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[57]));
}
}
- if let Some(rule) = self.use_sorted_classes.as_ref() {
+ if let Some(rule) = self.use_single_js_doc_asterisk.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[58]));
}
}
- if let Some(rule) = self.use_symbol_description.as_ref() {
+ if let Some(rule) = self.use_sorted_classes.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[59]));
}
}
- if let Some(rule) = self.use_unified_type_signature.as_ref() {
+ if let Some(rule) = self.use_symbol_description.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[60]));
}
}
- if let Some(rule) = self.use_unique_element_ids.as_ref() {
+ if let Some(rule) = self.use_unified_type_signature.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[61]));
}
}
+ if let Some(rule) = self.use_unique_element_ids.as_ref() {
+ if rule.is_disabled() {
+ index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[62]));
+ }
+ }
index_set
}
#[doc = r" Checks if, given a rule name, matches one of the rules contained in this category"]
@@ -5162,6 +5178,10 @@ impl RuleGroupExt for Nursery {
.no_nested_component_definitions
.as_ref()
.map(|conf| (conf.level(), conf.get_options())),
+ "noNextAsyncClientComponent" => self
+ .no_next_async_client_component
+ .as_ref()
+ .map(|conf| (conf.level(), conf.get_options())),
"noNonNullAssertedOptionalChain" => self
.no_non_null_asserted_optional_chain
.as_ref()
@@ -5379,6 +5399,7 @@ impl From for Nursery {
no_magic_numbers: Some(value.into()),
no_misused_promises: Some(value.into()),
no_nested_component_definitions: Some(value.into()),
+ no_next_async_client_component: Some(value.into()),
no_non_null_asserted_optional_chain: Some(value.into()),
no_noninteractive_element_interactions: Some(value.into()),
no_process_global: Some(value.into()),
diff --git a/crates/biome_diagnostics_categories/src/categories.rs b/crates/biome_diagnostics_categories/src/categories.rs
index 086c231b7229..58a26cb301a1 100644
--- a/crates/biome_diagnostics_categories/src/categories.rs
+++ b/crates/biome_diagnostics_categories/src/categories.rs
@@ -146,6 +146,7 @@ define_categories! {
"lint/correctness/useValidTypeof": "https://biomejs.dev/linter/rules/use-valid-typeof",
"lint/correctness/useYield": "https://biomejs.dev/linter/rules/use-yield",
"lint/nursery/colorNoInvalidHex": "https://biomejs.dev/linter/rules/color-no-invalid-hex",
+ "lint/nursery/noNextAsyncClientComponent": "https://biomejs.dev/linter/rules/no-next-async-client-component",
"lint/nursery/noAwaitInLoop": "https://biomejs.dev/linter/rules/no-await-in-loop",
"lint/nursery/noBitwiseOperators": "https://biomejs.dev/linter/rules/no-bitwise-operators",
"lint/nursery/noColorInvalidHex": "https://biomejs.dev/linter/rules/no-color-invalid-hex",
diff --git a/crates/biome_js_analyze/src/lint/nursery.rs b/crates/biome_js_analyze/src/lint/nursery.rs
index 32bbb4796f00..ff1323652ec9 100644
--- a/crates/biome_js_analyze/src/lint/nursery.rs
+++ b/crates/biome_js_analyze/src/lint/nursery.rs
@@ -15,6 +15,7 @@ pub mod no_import_cycles;
pub mod no_magic_numbers;
pub mod no_misused_promises;
pub mod no_nested_component_definitions;
+pub mod no_next_async_client_component;
pub mod no_non_null_asserted_optional_chain;
pub mod no_noninteractive_element_interactions;
pub mod no_process_global;
@@ -60,4 +61,4 @@ pub mod use_sorted_classes;
pub mod use_symbol_description;
pub mod use_unified_type_signature;
pub mod use_unique_element_ids;
-declare_lint_group! { pub Nursery { name : "nursery" , rules : [self :: no_await_in_loop :: NoAwaitInLoop , self :: no_bitwise_operators :: NoBitwiseOperators , self :: no_constant_binary_expression :: NoConstantBinaryExpression , self :: no_destructured_props :: NoDestructuredProps , self :: no_excessive_lines_per_function :: NoExcessiveLinesPerFunction , self :: no_floating_promises :: NoFloatingPromises , self :: no_global_dirname_filename :: NoGlobalDirnameFilename , self :: no_implicit_coercion :: NoImplicitCoercion , self :: no_import_cycles :: NoImportCycles , self :: no_magic_numbers :: NoMagicNumbers , self :: no_misused_promises :: NoMisusedPromises , self :: no_nested_component_definitions :: NoNestedComponentDefinitions , self :: no_non_null_asserted_optional_chain :: NoNonNullAssertedOptionalChain , self :: no_noninteractive_element_interactions :: NoNoninteractiveElementInteractions , self :: no_process_global :: NoProcessGlobal , self :: no_qwik_use_visible_task :: NoQwikUseVisibleTask , self :: no_react_prop_assign :: NoReactPropAssign , self :: no_restricted_elements :: NoRestrictedElements , self :: no_secrets :: NoSecrets , self :: no_shadow :: NoShadow , self :: no_ts_ignore :: NoTsIgnore , self :: no_unassigned_variables :: NoUnassignedVariables , self :: no_unnecessary_conditions :: NoUnnecessaryConditions , self :: no_unresolved_imports :: NoUnresolvedImports , self :: no_unwanted_polyfillio :: NoUnwantedPolyfillio , self :: no_useless_backref_in_regex :: NoUselessBackrefInRegex , self :: no_useless_escape_in_string :: NoUselessEscapeInString , self :: no_useless_undefined :: NoUselessUndefined , self :: no_vue_data_object_declaration :: NoVueDataObjectDeclaration , self :: no_vue_reserved_keys :: NoVueReservedKeys , self :: no_vue_reserved_props :: NoVueReservedProps , self :: use_adjacent_getter_setter :: UseAdjacentGetterSetter , self :: use_anchor_href :: UseAnchorHref , self :: use_consistent_object_definition :: UseConsistentObjectDefinition , self :: use_consistent_response :: UseConsistentResponse , self :: use_consistent_type_definitions :: UseConsistentTypeDefinitions , self :: use_exhaustive_switch_cases :: UseExhaustiveSwitchCases , self :: use_explicit_type :: UseExplicitType , self :: use_exports_last :: UseExportsLast , self :: use_for_component :: UseForComponent , self :: use_google_font_preconnect :: UseGoogleFontPreconnect , self :: use_image_size :: UseImageSize , self :: use_index_of :: UseIndexOf , self :: use_iterable_callback_return :: UseIterableCallbackReturn , self :: use_json_import_attribute :: UseJsonImportAttribute , self :: use_max_params :: UseMaxParams , self :: use_numeric_separators :: UseNumericSeparators , self :: use_object_spread :: UseObjectSpread , self :: use_parse_int_radix :: UseParseIntRadix , self :: use_qwik_classlist :: UseQwikClasslist , self :: use_react_function_components :: UseReactFunctionComponents , self :: use_readonly_class_properties :: UseReadonlyClassProperties , self :: use_single_js_doc_asterisk :: UseSingleJsDocAsterisk , self :: use_sorted_classes :: UseSortedClasses , self :: use_symbol_description :: UseSymbolDescription , self :: use_unified_type_signature :: UseUnifiedTypeSignature , self :: use_unique_element_ids :: UseUniqueElementIds ,] } }
+declare_lint_group! { pub Nursery { name : "nursery" , rules : [self :: no_await_in_loop :: NoAwaitInLoop , self :: no_bitwise_operators :: NoBitwiseOperators , self :: no_constant_binary_expression :: NoConstantBinaryExpression , self :: no_destructured_props :: NoDestructuredProps , self :: no_excessive_lines_per_function :: NoExcessiveLinesPerFunction , self :: no_floating_promises :: NoFloatingPromises , self :: no_global_dirname_filename :: NoGlobalDirnameFilename , self :: no_implicit_coercion :: NoImplicitCoercion , self :: no_import_cycles :: NoImportCycles , self :: no_magic_numbers :: NoMagicNumbers , self :: no_misused_promises :: NoMisusedPromises , self :: no_nested_component_definitions :: NoNestedComponentDefinitions , self :: no_next_async_client_component :: NoNextAsyncClientComponent , self :: no_non_null_asserted_optional_chain :: NoNonNullAssertedOptionalChain , self :: no_noninteractive_element_interactions :: NoNoninteractiveElementInteractions , self :: no_process_global :: NoProcessGlobal , self :: no_qwik_use_visible_task :: NoQwikUseVisibleTask , self :: no_react_prop_assign :: NoReactPropAssign , self :: no_restricted_elements :: NoRestrictedElements , self :: no_secrets :: NoSecrets , self :: no_shadow :: NoShadow , self :: no_ts_ignore :: NoTsIgnore , self :: no_unassigned_variables :: NoUnassignedVariables , self :: no_unnecessary_conditions :: NoUnnecessaryConditions , self :: no_unresolved_imports :: NoUnresolvedImports , self :: no_unwanted_polyfillio :: NoUnwantedPolyfillio , self :: no_useless_backref_in_regex :: NoUselessBackrefInRegex , self :: no_useless_escape_in_string :: NoUselessEscapeInString , self :: no_useless_undefined :: NoUselessUndefined , self :: no_vue_data_object_declaration :: NoVueDataObjectDeclaration , self :: no_vue_reserved_keys :: NoVueReservedKeys , self :: no_vue_reserved_props :: NoVueReservedProps , self :: use_adjacent_getter_setter :: UseAdjacentGetterSetter , self :: use_anchor_href :: UseAnchorHref , self :: use_consistent_object_definition :: UseConsistentObjectDefinition , self :: use_consistent_response :: UseConsistentResponse , self :: use_consistent_type_definitions :: UseConsistentTypeDefinitions , self :: use_exhaustive_switch_cases :: UseExhaustiveSwitchCases , self :: use_explicit_type :: UseExplicitType , self :: use_exports_last :: UseExportsLast , self :: use_for_component :: UseForComponent , self :: use_google_font_preconnect :: UseGoogleFontPreconnect , self :: use_image_size :: UseImageSize , self :: use_index_of :: UseIndexOf , self :: use_iterable_callback_return :: UseIterableCallbackReturn , self :: use_json_import_attribute :: UseJsonImportAttribute , self :: use_max_params :: UseMaxParams , self :: use_numeric_separators :: UseNumericSeparators , self :: use_object_spread :: UseObjectSpread , self :: use_parse_int_radix :: UseParseIntRadix , self :: use_qwik_classlist :: UseQwikClasslist , self :: use_react_function_components :: UseReactFunctionComponents , self :: use_readonly_class_properties :: UseReadonlyClassProperties , self :: use_single_js_doc_asterisk :: UseSingleJsDocAsterisk , self :: use_sorted_classes :: UseSortedClasses , self :: use_symbol_description :: UseSymbolDescription , self :: use_unified_type_signature :: UseUnifiedTypeSignature , self :: use_unique_element_ids :: UseUniqueElementIds ,] } }
diff --git a/crates/biome_js_analyze/src/lint/nursery/no_next_async_client_component.rs b/crates/biome_js_analyze/src/lint/nursery/no_next_async_client_component.rs
new file mode 100644
index 000000000000..67296eb94e2f
--- /dev/null
+++ b/crates/biome_js_analyze/src/lint/nursery/no_next_async_client_component.rs
@@ -0,0 +1,197 @@
+use crate::react::components::{
+ AnyPotentialReactComponentDeclaration, ReactComponentInfo, ReactComponentKind,
+};
+use biome_analyze::{
+ Ast, Rule, RuleDiagnostic, RuleDomain, RuleSource, context::RuleContext, declare_lint_rule,
+};
+use biome_console::markup;
+use biome_diagnostics::Severity;
+use biome_js_syntax::{AnyJsExpression, AnyJsRoot};
+use biome_rowan::{AstNode, TokenText};
+use biome_rule_options::no_next_async_client_component::NoNextAsyncClientComponentOptions;
+
+declare_lint_rule! {
+ /// Prevent client components from being async functions.
+ ///
+ /// This rule prevents the use of async functions for client components in Next.js applications.
+ /// Client components marked with "use client" directive should not be async as this can cause
+ /// hydration mismatches, break component rendering lifecycle, and lead to unexpected behavior
+ /// with React's concurrent features.
+ ///
+ /// ## Examples
+ ///
+ /// ### Invalid
+ ///
+ /// ```jsx,expect_diagnostic
+ /// "use client";
+ ///
+ /// export default async function MyComponent() {
+ /// return Hello
;
+ /// }
+ /// ```
+ ///
+ /// ### Valid
+ ///
+ /// ```jsx
+ /// "use client";
+ ///
+ /// export default function MyComponent() {
+ /// return Hello
;
+ /// }
+ /// ```
+ ///
+ /// ```jsx
+ /// // No "use client" directive - server component can be async
+ /// export default async function ServerComponent() {
+ /// const data = await fetch('/api/data');
+ /// return {data}
;
+ /// }
+ /// ```
+ ///
+ pub NoNextAsyncClientComponent {
+ version: "next",
+ name: "noNextAsyncClientComponent",
+ language: "js",
+ sources: &[RuleSource::EslintNext("no-async-client-component").same()],
+ recommended: false,
+ severity: Severity::Warning,
+ domains: &[RuleDomain::Next],
+ }
+}
+
+impl Rule for NoNextAsyncClientComponent {
+ type Query = Ast;
+ type State = Option;
+ type Signals = Option;
+ type Options = NoNextAsyncClientComponentOptions;
+
+ fn run(ctx: &RuleContext) -> Self::Signals {
+ let declaration = ctx.query();
+ let component = ReactComponentInfo::from_declaration(declaration.syntax())?;
+
+ // Only check function components
+ let ReactComponentKind::Function(_) = component.kind else {
+ return None;
+ };
+
+ // Check if we're in a module with "use client" directive
+ let root = ctx.root();
+ let has_use_client = match root {
+ AnyJsRoot::JsModule(module) => has_use_client_directive(module.directives()),
+ AnyJsRoot::JsScript(script) => has_use_client_directive(script.directives()),
+ _ => false,
+ };
+
+ if !has_use_client {
+ return None;
+ }
+
+ // Check if the component function is async
+ let is_async = match declaration {
+ AnyPotentialReactComponentDeclaration::JsFunctionDeclaration(func) => {
+ func.async_token().is_some()
+ }
+ AnyPotentialReactComponentDeclaration::JsFunctionExportDefaultDeclaration(func) => {
+ func.async_token().is_some()
+ }
+ AnyPotentialReactComponentDeclaration::JsVariableDeclarator(declarator) => declarator
+ .initializer()
+ .and_then(|init| init.expression().ok())
+ .and_then(|expr| match expr {
+ AnyJsExpression::JsArrowFunctionExpression(arrow) => arrow.async_token(),
+ AnyJsExpression::JsFunctionExpression(func) => func.async_token(),
+ _ => None,
+ })
+ .is_some(),
+ AnyPotentialReactComponentDeclaration::JsAssignmentExpression(assignment) => assignment
+ .right()
+ .ok()
+ .and_then(|expr| match expr {
+ AnyJsExpression::JsArrowFunctionExpression(arrow) => arrow.async_token(),
+ AnyJsExpression::JsFunctionExpression(func) => func.async_token(),
+ _ => None,
+ })
+ .is_some(),
+ AnyPotentialReactComponentDeclaration::JsExportDefaultExpressionClause(export) => {
+ export
+ .expression()
+ .ok()
+ .and_then(|expr| match expr {
+ AnyJsExpression::JsArrowFunctionExpression(arrow) => arrow.async_token(),
+ AnyJsExpression::JsFunctionExpression(func) => func.async_token(),
+ _ => None,
+ })
+ .is_some()
+ }
+ AnyPotentialReactComponentDeclaration::JsMethodObjectMember(method) => {
+ method.async_token().is_some()
+ }
+ AnyPotentialReactComponentDeclaration::JsPropertyObjectMember(prop) => prop
+ .value()
+ .ok()
+ .and_then(|expr| match expr {
+ AnyJsExpression::JsArrowFunctionExpression(arrow) => arrow.async_token(),
+ AnyJsExpression::JsFunctionExpression(func) => func.async_token(),
+ _ => None,
+ })
+ .is_some(),
+ AnyPotentialReactComponentDeclaration::JsMethodClassMember(method) => {
+ method.async_token().is_some()
+ }
+ _ => false,
+ };
+
+ if !is_async {
+ return None;
+ }
+
+ let component_name = component
+ .name
+ .or(component.name_hint)
+ .map(|token| token.token_text_trimmed());
+
+ Some(component_name)
+ }
+
+ fn diagnostic(ctx: &RuleContext, state: &Self::State) -> Option {
+ let declaration = ctx.query();
+
+ Some(match state {
+ Some(component_name) => {
+ let name_text = component_name.text();
+ RuleDiagnostic::new(
+ rule_category!(),
+ declaration.range(),
+ markup! {
+ "The component "{name_text}" is an async client component, which is not allowed."
+ },
+ )
+ }
+ None => {
+ RuleDiagnostic::new(
+ rule_category!(),
+ declaration.range(),
+ markup! {
+ "Async client component are not allowed."
+ },
+ )
+ }
+ }
+ .note(markup! {
+ "Client components with \"use client\" directive should not be async functions as this can cause hydration mismatches and break React's rendering lifecycle."
+ })
+ .note(markup! {
+ "Consider using useEffect for async operations inside the component, or remove the \"use client\" directive if this should be a server component."
+ }))
+ }
+}
+
+fn has_use_client_directive(
+ directives: impl IntoIterator- ,
+) -> bool {
+ directives.into_iter().any(|directive| {
+ directive
+ .inner_string_text()
+ .is_ok_and(|text| text.text() == "use client")
+ })
+}
diff --git a/crates/biome_js_analyze/tests/specs/nursery/noNextAsyncClientComponent/invalid.jsx b/crates/biome_js_analyze/tests/specs/nursery/noNextAsyncClientComponent/invalid.jsx
new file mode 100644
index 000000000000..4c1aa89946c8
--- /dev/null
+++ b/crates/biome_js_analyze/tests/specs/nursery/noNextAsyncClientComponent/invalid.jsx
@@ -0,0 +1,51 @@
+"use client";
+
+export default async function MyComponent() {
+ return
Hello
;
+}
+
+async function MyComponent2() {
+ return Hello
;
+}
+
+const MyComponent3 = async () => {
+ return Hello
;
+};
+
+const MyComponent4 = async function() {
+ return Hello
;
+};
+
+let MyComponent5;
+MyComponent5 = async () => {
+ return Hello
;
+};
+
+let MyComponent6;
+MyComponent6 = async function() {
+ return Hello
;
+};
+
+const components = {
+ async MyComponent() {
+ return Hello
;
+ }
+};
+
+const components2 = {
+ MyComponent: async () => {
+ return Hello
;
+ }
+};
+
+const components3 = {
+ MyComponent: async function() {
+ return Hello
;
+ }
+};
+
+class ComponentClass {
+ async MyComponent() {
+ return Hello
;
+ }
+}
diff --git a/crates/biome_js_analyze/tests/specs/nursery/noNextAsyncClientComponent/invalid.jsx.snap b/crates/biome_js_analyze/tests/specs/nursery/noNextAsyncClientComponent/invalid.jsx.snap
new file mode 100644
index 000000000000..c6fbf24d6339
--- /dev/null
+++ b/crates/biome_js_analyze/tests/specs/nursery/noNextAsyncClientComponent/invalid.jsx.snap
@@ -0,0 +1,274 @@
+---
+source: crates/biome_js_analyze/tests/spec_tests.rs
+expression: invalid.jsx
+---
+# Input
+```jsx
+"use client";
+
+export default async function MyComponent() {
+ return Hello
;
+}
+
+async function MyComponent2() {
+ return Hello
;
+}
+
+const MyComponent3 = async () => {
+ return Hello
;
+};
+
+const MyComponent4 = async function() {
+ return Hello
;
+};
+
+let MyComponent5;
+MyComponent5 = async () => {
+ return Hello
;
+};
+
+let MyComponent6;
+MyComponent6 = async function() {
+ return Hello
;
+};
+
+const components = {
+ async MyComponent() {
+ return Hello
;
+ }
+};
+
+const components2 = {
+ MyComponent: async () => {
+ return Hello
;
+ }
+};
+
+const components3 = {
+ MyComponent: async function() {
+ return Hello
;
+ }
+};
+
+class ComponentClass {
+ async MyComponent() {
+ return Hello
;
+ }
+}
+
+```
+
+# Diagnostics
+```
+invalid.jsx:3:16 lint/nursery/noNextAsyncClientComponent ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ ! The component MyComponent is an async client component, which is not allowed.
+
+ 1 │ "use client";
+ 2 │
+ > 3 │ export default async function MyComponent() {
+ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ > 4 │ return Hello
;
+ > 5 │ }
+ │ ^
+ 6 │
+ 7 │ async function MyComponent2() {
+
+ i Client components with "use client" directive should not be async functions as this can cause hydration mismatches and break React's rendering lifecycle.
+
+ i Consider using useEffect for async operations inside the component, or remove the "use client" directive if this should be a server component.
+
+
+```
+
+```
+invalid.jsx:7:1 lint/nursery/noNextAsyncClientComponent ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ ! The component MyComponent2 is an async client component, which is not allowed.
+
+ 5 │ }
+ 6 │
+ > 7 │ async function MyComponent2() {
+ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ > 8 │ return Hello
;
+ > 9 │ }
+ │ ^
+ 10 │
+ 11 │ const MyComponent3 = async () => {
+
+ i Client components with "use client" directive should not be async functions as this can cause hydration mismatches and break React's rendering lifecycle.
+
+ i Consider using useEffect for async operations inside the component, or remove the "use client" directive if this should be a server component.
+
+
+```
+
+```
+invalid.jsx:11:7 lint/nursery/noNextAsyncClientComponent ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ ! The component MyComponent3 is an async client component, which is not allowed.
+
+ 9 │ }
+ 10 │
+ > 11 │ const MyComponent3 = async () => {
+ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ > 12 │ return Hello
;
+ > 13 │ };
+ │ ^
+ 14 │
+ 15 │ const MyComponent4 = async function() {
+
+ i Client components with "use client" directive should not be async functions as this can cause hydration mismatches and break React's rendering lifecycle.
+
+ i Consider using useEffect for async operations inside the component, or remove the "use client" directive if this should be a server component.
+
+
+```
+
+```
+invalid.jsx:15:7 lint/nursery/noNextAsyncClientComponent ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ ! The component MyComponent4 is an async client component, which is not allowed.
+
+ 13 │ };
+ 14 │
+ > 15 │ const MyComponent4 = async function() {
+ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ > 16 │ return Hello
;
+ > 17 │ };
+ │ ^
+ 18 │
+ 19 │ let MyComponent5;
+
+ i Client components with "use client" directive should not be async functions as this can cause hydration mismatches and break React's rendering lifecycle.
+
+ i Consider using useEffect for async operations inside the component, or remove the "use client" directive if this should be a server component.
+
+
+```
+
+```
+invalid.jsx:20:1 lint/nursery/noNextAsyncClientComponent ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ ! The component MyComponent5 is an async client component, which is not allowed.
+
+ 19 │ let MyComponent5;
+ > 20 │ MyComponent5 = async () => {
+ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ > 21 │ return Hello
;
+ > 22 │ };
+ │ ^
+ 23 │
+ 24 │ let MyComponent6;
+
+ i Client components with "use client" directive should not be async functions as this can cause hydration mismatches and break React's rendering lifecycle.
+
+ i Consider using useEffect for async operations inside the component, or remove the "use client" directive if this should be a server component.
+
+
+```
+
+```
+invalid.jsx:25:1 lint/nursery/noNextAsyncClientComponent ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ ! The component MyComponent6 is an async client component, which is not allowed.
+
+ 24 │ let MyComponent6;
+ > 25 │ MyComponent6 = async function() {
+ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ > 26 │ return Hello
;
+ > 27 │ };
+ │ ^
+ 28 │
+ 29 │ const components = {
+
+ i Client components with "use client" directive should not be async functions as this can cause hydration mismatches and break React's rendering lifecycle.
+
+ i Consider using useEffect for async operations inside the component, or remove the "use client" directive if this should be a server component.
+
+
+```
+
+```
+invalid.jsx:30:3 lint/nursery/noNextAsyncClientComponent ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ ! The component MyComponent is an async client component, which is not allowed.
+
+ 29 │ const components = {
+ > 30 │ async MyComponent() {
+ │ ^^^^^^^^^^^^^^^^^^^^^
+ > 31 │ return Hello
;
+ > 32 │ }
+ │ ^
+ 33 │ };
+ 34 │
+
+ i Client components with "use client" directive should not be async functions as this can cause hydration mismatches and break React's rendering lifecycle.
+
+ i Consider using useEffect for async operations inside the component, or remove the "use client" directive if this should be a server component.
+
+
+```
+
+```
+invalid.jsx:36:3 lint/nursery/noNextAsyncClientComponent ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ ! The component MyComponent is an async client component, which is not allowed.
+
+ 35 │ const components2 = {
+ > 36 │ MyComponent: async () => {
+ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ > 37 │ return Hello
;
+ > 38 │ }
+ │ ^
+ 39 │ };
+ 40 │
+
+ i Client components with "use client" directive should not be async functions as this can cause hydration mismatches and break React's rendering lifecycle.
+
+ i Consider using useEffect for async operations inside the component, or remove the "use client" directive if this should be a server component.
+
+
+```
+
+```
+invalid.jsx:42:3 lint/nursery/noNextAsyncClientComponent ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ ! The component MyComponent is an async client component, which is not allowed.
+
+ 41 │ const components3 = {
+ > 42 │ MyComponent: async function() {
+ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ > 43 │ return Hello
;
+ > 44 │ }
+ │ ^
+ 45 │ };
+ 46 │
+
+ i Client components with "use client" directive should not be async functions as this can cause hydration mismatches and break React's rendering lifecycle.
+
+ i Consider using useEffect for async operations inside the component, or remove the "use client" directive if this should be a server component.
+
+
+```
+
+```
+invalid.jsx:48:3 lint/nursery/noNextAsyncClientComponent ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ ! The component MyComponent is an async client component, which is not allowed.
+
+ 47 │ class ComponentClass {
+ > 48 │ async MyComponent() {
+ │ ^^^^^^^^^^^^^^^^^^^^^
+ > 49 │ return Hello
;
+ > 50 │ }
+ │ ^
+ 51 │ }
+ 52 │
+
+ i Client components with "use client" directive should not be async functions as this can cause hydration mismatches and break React's rendering lifecycle.
+
+ i Consider using useEffect for async operations inside the component, or remove the "use client" directive if this should be a server component.
+
+
+```
diff --git a/crates/biome_js_analyze/tests/specs/nursery/noNextAsyncClientComponent/valid.jsx b/crates/biome_js_analyze/tests/specs/nursery/noNextAsyncClientComponent/valid.jsx
new file mode 100644
index 000000000000..453865f48037
--- /dev/null
+++ b/crates/biome_js_analyze/tests/specs/nursery/noNextAsyncClientComponent/valid.jsx
@@ -0,0 +1,31 @@
+/* should not generate diagnostics */
+export default async function ServerComponent() {
+ return Hello
;
+}
+
+const ServerComponent2 = async () => {
+ return Hello
;
+};
+
+let ServerComponent3;
+ServerComponent3 = async function() {
+ return Hello
;
+};
+
+const serverComponents = {
+ async ServerComponent() {
+ return Hello
;
+ }
+};
+
+const serverComponents2 = {
+ ServerComponent: async () => {
+ return Hello
;
+ }
+};
+
+class ServerComponentClass {
+ async ServerComponent() {
+ return Hello
;
+ }
+}
diff --git a/crates/biome_js_analyze/tests/specs/nursery/noNextAsyncClientComponent/valid.jsx.snap b/crates/biome_js_analyze/tests/specs/nursery/noNextAsyncClientComponent/valid.jsx.snap
new file mode 100644
index 000000000000..9e24e9482bad
--- /dev/null
+++ b/crates/biome_js_analyze/tests/specs/nursery/noNextAsyncClientComponent/valid.jsx.snap
@@ -0,0 +1,39 @@
+---
+source: crates/biome_js_analyze/tests/spec_tests.rs
+expression: valid.jsx
+---
+# Input
+```jsx
+/* should not generate diagnostics */
+export default async function ServerComponent() {
+ return Hello
;
+}
+
+const ServerComponent2 = async () => {
+ return Hello
;
+};
+
+let ServerComponent3;
+ServerComponent3 = async function() {
+ return Hello
;
+};
+
+const serverComponents = {
+ async ServerComponent() {
+ return Hello
;
+ }
+};
+
+const serverComponents2 = {
+ ServerComponent: async () => {
+ return Hello
;
+ }
+};
+
+class ServerComponentClass {
+ async ServerComponent() {
+ return Hello
;
+ }
+}
+
+```
diff --git a/crates/biome_js_analyze/tests/specs/nursery/noNextAsyncClientComponent/validClient.jsx b/crates/biome_js_analyze/tests/specs/nursery/noNextAsyncClientComponent/validClient.jsx
new file mode 100644
index 000000000000..28b8d81607df
--- /dev/null
+++ b/crates/biome_js_analyze/tests/specs/nursery/noNextAsyncClientComponent/validClient.jsx
@@ -0,0 +1,51 @@
+/* should not generate diagnostics */
+"use client";
+export default function ClientComponent() {
+ return Hello
;
+}
+
+function ClientComponent2() {
+ return Hello
;
+}
+
+const ClientComponent3 = () => {
+ return Hello
;
+};
+
+const ClientComponent4 = function() {
+ return Hello
;
+};
+
+let ClientComponent5;
+ClientComponent5 = () => {
+ return Hello
;
+};
+
+let ClientComponent6;
+ClientComponent6 = function() {
+ return Hello
;
+};
+
+const components = {
+ MyComponent() {
+ return Hello
;
+ }
+};
+
+const components2 = {
+ MyComponent: () => {
+ return Hello
;
+ }
+};
+
+const components3 = {
+ MyComponent: function() {
+ return Hello
;
+ }
+};
+
+class ComponentClass {
+ MyComponent() {
+ return Hello
;
+ }
+}
diff --git a/crates/biome_js_analyze/tests/specs/nursery/noNextAsyncClientComponent/validClient.jsx.snap b/crates/biome_js_analyze/tests/specs/nursery/noNextAsyncClientComponent/validClient.jsx.snap
new file mode 100644
index 000000000000..1586fc323889
--- /dev/null
+++ b/crates/biome_js_analyze/tests/specs/nursery/noNextAsyncClientComponent/validClient.jsx.snap
@@ -0,0 +1,59 @@
+---
+source: crates/biome_js_analyze/tests/spec_tests.rs
+expression: validClient.jsx
+---
+# Input
+```jsx
+/* should not generate diagnostics */
+"use client";
+export default function ClientComponent() {
+ return Hello
;
+}
+
+function ClientComponent2() {
+ return Hello
;
+}
+
+const ClientComponent3 = () => {
+ return Hello
;
+};
+
+const ClientComponent4 = function() {
+ return Hello
;
+};
+
+let ClientComponent5;
+ClientComponent5 = () => {
+ return Hello
;
+};
+
+let ClientComponent6;
+ClientComponent6 = function() {
+ return Hello
;
+};
+
+const components = {
+ MyComponent() {
+ return Hello
;
+ }
+};
+
+const components2 = {
+ MyComponent: () => {
+ return Hello
;
+ }
+};
+
+const components3 = {
+ MyComponent: function() {
+ return Hello
;
+ }
+};
+
+class ComponentClass {
+ MyComponent() {
+ return Hello
;
+ }
+}
+
+```
diff --git a/crates/biome_rule_options/src/lib.rs b/crates/biome_rule_options/src/lib.rs
index 224bb9d79a8c..2489e956212c 100644
--- a/crates/biome_rule_options/src/lib.rs
+++ b/crates/biome_rule_options/src/lib.rs
@@ -125,6 +125,7 @@ pub mod no_namespace_import;
pub mod no_negation_else;
pub mod no_nested_component_definitions;
pub mod no_nested_ternary;
+pub mod no_next_async_client_component;
pub mod no_nodejs_modules;
pub mod no_non_null_asserted_optional_chain;
pub mod no_non_null_assertion;
diff --git a/crates/biome_rule_options/src/no_next_async_client_component.rs b/crates/biome_rule_options/src/no_next_async_client_component.rs
new file mode 100644
index 000000000000..67d41e833544
--- /dev/null
+++ b/crates/biome_rule_options/src/no_next_async_client_component.rs
@@ -0,0 +1,6 @@
+use biome_deserialize_macros::Deserializable;
+use serde::{Deserialize, Serialize};
+#[derive(Default, Clone, Debug, Deserialize, Deserializable, Eq, PartialEq, Serialize)]
+#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
+#[serde(rename_all = "camelCase", deny_unknown_fields, default)]
+pub struct NoNextAsyncClientComponentOptions {}
diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts
index 98a65a09f847..1211f13ff28b 100644
--- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts
+++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts
@@ -1603,6 +1603,10 @@ export interface Nursery {
* Disallows defining React components inside other components.
*/
noNestedComponentDefinitions?: RuleConfiguration_for_NoNestedComponentDefinitionsOptions;
+ /**
+ * Prevent client components from being async functions.
+ */
+ noNextAsyncClientComponent?: RuleConfiguration_for_NoNextAsyncClientComponentOptions;
/**
* Disallow non-null assertions after optional chaining expressions.
*/
@@ -2927,6 +2931,9 @@ export type RuleFixConfiguration_for_NoMisusedPromisesOptions =
export type RuleConfiguration_for_NoNestedComponentDefinitionsOptions =
| RulePlainConfiguration
| RuleWithOptions_for_NoNestedComponentDefinitionsOptions;
+export type RuleConfiguration_for_NoNextAsyncClientComponentOptions =
+ | RulePlainConfiguration
+ | RuleWithOptions_for_NoNextAsyncClientComponentOptions;
export type RuleConfiguration_for_NoNonNullAssertedOptionalChainOptions =
| RulePlainConfiguration
| RuleWithOptions_for_NoNonNullAssertedOptionalChainOptions;
@@ -5230,6 +5237,16 @@ export interface RuleWithOptions_for_NoNestedComponentDefinitionsOptions {
*/
options: NoNestedComponentDefinitionsOptions;
}
+export interface RuleWithOptions_for_NoNextAsyncClientComponentOptions {
+ /**
+ * The severity of the emitted diagnostics by the rule
+ */
+ level: RulePlainConfiguration;
+ /**
+ * Rule's options
+ */
+ options: NoNextAsyncClientComponentOptions;
+}
export interface RuleWithOptions_for_NoNonNullAssertedOptionalChainOptions {
/**
* The severity of the emitted diagnostics by the rule
@@ -7887,6 +7904,7 @@ export interface NoImportantStylesOptions {}
export interface NoMagicNumbersOptions {}
export interface NoMisusedPromisesOptions {}
export interface NoNestedComponentDefinitionsOptions {}
+export interface NoNextAsyncClientComponentOptions {}
export interface NoNonNullAssertedOptionalChainOptions {}
export interface NoNoninteractiveElementInteractionsOptions {}
export interface NoProcessGlobalOptions {}
@@ -8576,6 +8594,7 @@ export type Category =
| "lint/correctness/useValidTypeof"
| "lint/correctness/useYield"
| "lint/nursery/colorNoInvalidHex"
+ | "lint/nursery/noNextAsyncClientComponent"
| "lint/nursery/noAwaitInLoop"
| "lint/nursery/noBitwiseOperators"
| "lint/nursery/noColorInvalidHex"
diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json
index 0f9c2eed6e98..169791e4b02b 100644
--- a/packages/@biomejs/biome/configuration_schema.json
+++ b/packages/@biomejs/biome/configuration_schema.json
@@ -3629,6 +3629,16 @@
"type": "object",
"additionalProperties": false
},
+ "NoNextAsyncClientComponentConfiguration": {
+ "anyOf": [
+ { "$ref": "#/definitions/RulePlainConfiguration" },
+ { "$ref": "#/definitions/RuleWithNoNextAsyncClientComponentOptions" }
+ ]
+ },
+ "NoNextAsyncClientComponentOptions": {
+ "type": "object",
+ "additionalProperties": false
+ },
"NoNodejsModulesConfiguration": {
"anyOf": [
{ "$ref": "#/definitions/RulePlainConfiguration" },
@@ -4869,6 +4879,13 @@
{ "type": "null" }
]
},
+ "noNextAsyncClientComponent": {
+ "description": "Prevent client components from being async functions.",
+ "anyOf": [
+ { "$ref": "#/definitions/NoNextAsyncClientComponentConfiguration" },
+ { "type": "null" }
+ ]
+ },
"noNonNullAssertedOptionalChain": {
"description": "Disallow non-null assertions after optional chaining expressions.",
"anyOf": [
@@ -7776,6 +7793,23 @@
},
"additionalProperties": false
},
+ "RuleWithNoNextAsyncClientComponentOptions": {
+ "type": "object",
+ "required": ["level"],
+ "properties": {
+ "level": {
+ "description": "The severity of the emitted diagnostics by the rule",
+ "allOf": [{ "$ref": "#/definitions/RulePlainConfiguration" }]
+ },
+ "options": {
+ "description": "Rule's options",
+ "allOf": [
+ { "$ref": "#/definitions/NoNextAsyncClientComponentOptions" }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
"RuleWithNoNodejsModulesOptions": {
"type": "object",
"required": ["level"],