diff --git a/.changeset/brave-scripts-dance.md b/.changeset/brave-scripts-dance.md new file mode 100644 index 000000000000..d9b73ff29277 --- /dev/null +++ b/.changeset/brave-scripts-dance.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Added the rule [`useRequiredScripts`](https://biomejs.dev/linter/rules/use-required-scripts/), which enforces presence of configurable entries in the `scripts` section of `package.json` files. diff --git a/.changeset/green-humans-try.md b/.changeset/green-humans-try.md new file mode 100644 index 000000000000..ab90d5ed20f7 --- /dev/null +++ b/.changeset/green-humans-try.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fixed [`noDuplicateDependencies`](https://biomejs.dev/linter/rules/no-duplicate-dependencies/) incorrectly triggering on files like `_package.json`. diff --git a/crates/biome_configuration/src/analyzer/linter/rules.rs b/crates/biome_configuration/src/analyzer/linter/rules.rs index 40a8480c0ba2..df1c71e3bb6f 100644 --- a/crates/biome_configuration/src/analyzer/linter/rules.rs +++ b/crates/biome_configuration/src/analyzer/linter/rules.rs @@ -436,6 +436,7 @@ pub enum RuleName { UseReadonlyClassProperties, UseRegexLiterals, UseRegexpExec, + UseRequiredScripts, UseSelfClosingElements, UseSemanticElements, UseShorthandAssign, @@ -833,6 +834,7 @@ impl RuleName { Self::UseReadonlyClassProperties => "useReadonlyClassProperties", Self::UseRegexLiterals => "useRegexLiterals", Self::UseRegexpExec => "useRegexpExec", + Self::UseRequiredScripts => "useRequiredScripts", Self::UseSelfClosingElements => "useSelfClosingElements", Self::UseSemanticElements => "useSemanticElements", Self::UseShorthandAssign => "useShorthandAssign", @@ -1226,6 +1228,7 @@ impl RuleName { Self::UseReadonlyClassProperties => RuleGroup::Style, Self::UseRegexLiterals => RuleGroup::Complexity, Self::UseRegexpExec => RuleGroup::Nursery, + Self::UseRequiredScripts => RuleGroup::Nursery, Self::UseSelfClosingElements => RuleGroup::Style, Self::UseSemanticElements => RuleGroup::A11y, Self::UseShorthandAssign => RuleGroup::Style, @@ -1628,6 +1631,7 @@ impl std::str::FromStr for RuleName { "useReadonlyClassProperties" => Ok(Self::UseReadonlyClassProperties), "useRegexLiterals" => Ok(Self::UseRegexLiterals), "useRegexpExec" => Ok(Self::UseRegexpExec), + "useRequiredScripts" => Ok(Self::UseRequiredScripts), "useSelfClosingElements" => Ok(Self::UseSelfClosingElements), "useSemanticElements" => Ok(Self::UseSemanticElements), "useShorthandAssign" => Ok(Self::UseShorthandAssign), @@ -4804,7 +4808,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" Enables the recommended rules for this group"] # [serde (skip_serializing_if = "Option::is_none")] pub recommended : Option < bool > , # [doc = "Disallow continue statements.\nSee https://biomejs.dev/linter/rules/no-continue"] # [serde (skip_serializing_if = "Option::is_none")] pub no_continue : Option < RuleConfiguration < biome_rule_options :: no_continue :: NoContinueOptions >> , # [doc = "Restrict imports of deprecated exports.\nSee https://biomejs.dev/linter/rules/no-deprecated-imports"] # [serde (skip_serializing_if = "Option::is_none")] pub no_deprecated_imports : Option < RuleConfiguration < biome_rule_options :: no_deprecated_imports :: NoDeprecatedImportsOptions >> , # [doc = "Prevent the listing of duplicate dependencies. The rule supports the following dependency groups: \"bundledDependencies\", \"bundleDependencies\", \"dependencies\", \"devDependencies\", \"overrides\", \"optionalDependencies\", and \"peerDependencies\".\nSee https://biomejs.dev/linter/rules/no-duplicate-dependencies"] # [serde (skip_serializing_if = "Option::is_none")] pub no_duplicate_dependencies : Option < RuleConfiguration < biome_rule_options :: no_duplicate_dependencies :: NoDuplicateDependenciesOptions >> , # [doc = "Disallow JSX prop spreading the same identifier multiple times.\nSee https://biomejs.dev/linter/rules/no-duplicated-spread-props"] # [serde (skip_serializing_if = "Option::is_none")] pub no_duplicated_spread_props : Option < RuleConfiguration < biome_rule_options :: no_duplicated_spread_props :: NoDuplicatedSpreadPropsOptions >> , # [doc = "Disallow empty sources.\nSee https://biomejs.dev/linter/rules/no-empty-source"] # [serde (skip_serializing_if = "Option::is_none")] pub no_empty_source : Option < RuleConfiguration < biome_rule_options :: no_empty_source :: NoEmptySourceOptions >> , # [doc = "Require the use of === or !== for comparison with null.\nSee https://biomejs.dev/linter/rules/no-equals-to-null"] # [serde (skip_serializing_if = "Option::is_none")] pub no_equals_to_null : Option < RuleFixConfiguration < biome_rule_options :: no_equals_to_null :: NoEqualsToNullOptions >> , # [doc = "Require Promise-like statements to be handled appropriately.\nSee https://biomejs.dev/linter/rules/no-floating-promises"] # [serde (skip_serializing_if = "Option::is_none")] pub no_floating_promises : Option < RuleFixConfiguration < biome_rule_options :: no_floating_promises :: NoFloatingPromisesOptions >> , # [doc = "Disallow iterating using a for-in loop.\nSee https://biomejs.dev/linter/rules/no-for-in"] # [serde (skip_serializing_if = "Option::is_none")] pub no_for_in : Option < RuleConfiguration < biome_rule_options :: no_for_in :: NoForInOptions >> , # [doc = "Prevent import cycles.\nSee https://biomejs.dev/linter/rules/no-import-cycles"] # [serde (skip_serializing_if = "Option::is_none")] pub no_import_cycles : Option < RuleConfiguration < biome_rule_options :: no_import_cycles :: NoImportCyclesOptions >> , # [doc = "Disallows the usage of the unary operators ++ and --.\nSee https://biomejs.dev/linter/rules/no-increment-decrement"] # [serde (skip_serializing_if = "Option::is_none")] pub no_increment_decrement : Option < RuleConfiguration < biome_rule_options :: no_increment_decrement :: NoIncrementDecrementOptions >> , # [doc = "Disallow string literals inside JSX elements.\nSee https://biomejs.dev/linter/rules/no-jsx-literals"] # [serde (skip_serializing_if = "Option::is_none")] pub no_jsx_literals : Option < RuleConfiguration < biome_rule_options :: no_jsx_literals :: NoJsxLiteralsOptions >> , # [doc = "Prevent problematic leaked values from being rendered.\nSee https://biomejs.dev/linter/rules/no-leaked-render"] # [serde (skip_serializing_if = "Option::is_none")] pub no_leaked_render : Option < RuleConfiguration < biome_rule_options :: no_leaked_render :: NoLeakedRenderOptions >> , # [doc = "Disallow Promises to be used in places where they are almost certainly a mistake.\nSee https://biomejs.dev/linter/rules/no-misused-promises"] # [serde (skip_serializing_if = "Option::is_none")] pub no_misused_promises : Option < RuleFixConfiguration < biome_rule_options :: no_misused_promises :: NoMisusedPromisesOptions >> , # [doc = "Disallow creating multiline strings by escaping newlines.\nSee https://biomejs.dev/linter/rules/no-multi-str"] # [serde (skip_serializing_if = "Option::is_none")] pub no_multi_str : Option < RuleConfiguration < biome_rule_options :: no_multi_str :: NoMultiStrOptions >> , # [doc = "Prevent client components from being async functions.\nSee https://biomejs.dev/linter/rules/no-next-async-client-component"] # [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 function parameters that are only used in recursive calls.\nSee https://biomejs.dev/linter/rules/no-parameters-only-used-in-recursion"] # [serde (skip_serializing_if = "Option::is_none")] pub no_parameters_only_used_in_recursion : Option < RuleFixConfiguration < biome_rule_options :: no_parameters_only_used_in_recursion :: NoParametersOnlyUsedInRecursionOptions >> , # [doc = "Disallow the use of the __proto__ property.\nSee https://biomejs.dev/linter/rules/no-proto"] # [serde (skip_serializing_if = "Option::is_none")] pub no_proto : Option < RuleConfiguration < biome_rule_options :: no_proto :: NoProtoOptions >> , # [doc = "Replaces usages of forwardRef with passing ref as a prop.\nSee https://biomejs.dev/linter/rules/no-react-forward-ref"] # [serde (skip_serializing_if = "Option::is_none")] pub no_react_forward_ref : Option < RuleFixConfiguration < biome_rule_options :: no_react_forward_ref :: NoReactForwardRefOptions >> , # [doc = "Disallow variable declarations from shadowing variables declared in the outer scope.\nSee https://biomejs.dev/linter/rules/no-shadow"] # [serde (skip_serializing_if = "Option::is_none")] pub no_shadow : Option < RuleConfiguration < biome_rule_options :: no_shadow :: NoShadowOptions >> , # [doc = "Prevent the usage of synchronous scripts.\nSee https://biomejs.dev/linter/rules/no-sync-scripts"] # [serde (skip_serializing_if = "Option::is_none")] pub no_sync_scripts : Option < RuleConfiguration < biome_rule_options :: no_sync_scripts :: NoSyncScriptsOptions >> , # [doc = "Disallow ternary operators.\nSee https://biomejs.dev/linter/rules/no-ternary"] # [serde (skip_serializing_if = "Option::is_none")] pub no_ternary : Option < RuleConfiguration < biome_rule_options :: no_ternary :: NoTernaryOptions >> , # [doc = "Disallow unknown DOM properties.\nSee https://biomejs.dev/linter/rules/no-unknown-attribute"] # [serde (skip_serializing_if = "Option::is_none")] pub no_unknown_attribute : Option < RuleConfiguration < biome_rule_options :: no_unknown_attribute :: NoUnknownAttributeOptions >> , # [doc = "Disallow unnecessary type-based conditions that can be statically determined as redundant.\nSee https://biomejs.dev/linter/rules/no-unnecessary-conditions"] # [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.\nSee https://biomejs.dev/linter/rules/no-unresolved-imports"] # [serde (skip_serializing_if = "Option::is_none")] pub no_unresolved_imports : Option < RuleConfiguration < biome_rule_options :: no_unresolved_imports :: NoUnresolvedImportsOptions >> , # [doc = "Disallow expression statements that are neither a function call nor an assignment.\nSee https://biomejs.dev/linter/rules/no-unused-expressions"] # [serde (skip_serializing_if = "Option::is_none")] pub no_unused_expressions : Option < RuleConfiguration < biome_rule_options :: no_unused_expressions :: NoUnusedExpressionsOptions >> , # [doc = "Disallow unused catch bindings.\nSee https://biomejs.dev/linter/rules/no-useless-catch-binding"] # [serde (skip_serializing_if = "Option::is_none")] pub no_useless_catch_binding : Option < RuleFixConfiguration < biome_rule_options :: no_useless_catch_binding :: NoUselessCatchBindingOptions >> , # [doc = "Disallow the use of useless undefined.\nSee https://biomejs.dev/linter/rules/no-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.\nSee https://biomejs.dev/linter/rules/no-vue-data-object-declaration"] # [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 duplicate keys in Vue component data, methods, computed properties, and other options.\nSee https://biomejs.dev/linter/rules/no-vue-duplicate-keys"] # [serde (skip_serializing_if = "Option::is_none")] pub no_vue_duplicate_keys : Option < RuleConfiguration < biome_rule_options :: no_vue_duplicate_keys :: NoVueDuplicateKeysOptions >> , # [doc = "Disallow reserved keys in Vue component data and computed properties.\nSee https://biomejs.dev/linter/rules/no-vue-reserved-keys"] # [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.\nSee https://biomejs.dev/linter/rules/no-vue-reserved-props"] # [serde (skip_serializing_if = "Option::is_none")] pub no_vue_reserved_props : Option < RuleConfiguration < biome_rule_options :: no_vue_reserved_props :: NoVueReservedPropsOptions >> , # [doc = "Disallow using v-if and v-for directives on the same element.\nSee https://biomejs.dev/linter/rules/no-vue-v-if-with-v-for"] # [serde (skip_serializing_if = "Option::is_none")] pub no_vue_v_if_with_v_for : Option < RuleConfiguration < biome_rule_options :: no_vue_v_if_with_v_for :: NoVueVIfWithVForOptions >> , # [doc = "Require Array#sort and Array#toSorted calls to always provide a compareFunction.\nSee https://biomejs.dev/linter/rules/use-array-sort-compare"] # [serde (skip_serializing_if = "Option::is_none")] pub use_array_sort_compare : Option < RuleConfiguration < biome_rule_options :: use_array_sort_compare :: UseArraySortCompareOptions >> , # [doc = "Enforce consistent arrow function bodies.\nSee https://biomejs.dev/linter/rules/use-consistent-arrow-return"] # [serde (skip_serializing_if = "Option::is_none")] pub use_consistent_arrow_return : Option < RuleFixConfiguration < biome_rule_options :: use_consistent_arrow_return :: UseConsistentArrowReturnOptions >> , # [doc = "Require all descriptions to follow the same style (either block or inline) to maintain consistency and improve readability across the schema.\nSee https://biomejs.dev/linter/rules/use-consistent-graphql-descriptions"] # [serde (skip_serializing_if = "Option::is_none")] pub use_consistent_graphql_descriptions : Option < RuleConfiguration < biome_rule_options :: use_consistent_graphql_descriptions :: UseConsistentGraphqlDescriptionsOptions >> , # [doc = "Require the @deprecated directive to specify a deletion date.\nSee https://biomejs.dev/linter/rules/use-deprecated-date"] # [serde (skip_serializing_if = "Option::is_none")] pub use_deprecated_date : Option < RuleConfiguration < biome_rule_options :: use_deprecated_date :: UseDeprecatedDateOptions >> , # [doc = "Require switch-case statements to be exhaustive.\nSee https://biomejs.dev/linter/rules/use-exhaustive-switch-cases"] # [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.\nSee https://biomejs.dev/linter/rules/use-explicit-type"] # [serde (skip_serializing_if = "Option::is_none")] pub use_explicit_type : Option < RuleConfiguration < biome_rule_options :: use_explicit_type :: UseExplicitTypeOptions >> , # [doc = "Enforce the use of Array.prototype.find() over Array.prototype.filter() followed by [0] when looking for a single result.\nSee https://biomejs.dev/linter/rules/use-find"] # [serde (skip_serializing_if = "Option::is_none")] pub use_find : Option < RuleConfiguration < biome_rule_options :: use_find :: UseFindOptions >> , # [doc = "Enforce a maximum number of parameters in function definitions.\nSee https://biomejs.dev/linter/rules/use-max-params"] # [serde (skip_serializing_if = "Option::is_none")] pub use_max_params : Option < RuleConfiguration < biome_rule_options :: use_max_params :: UseMaxParamsOptions >> , # [doc = "Disallow use* hooks outside of component$ or other use* hooks in Qwik applications.\nSee https://biomejs.dev/linter/rules/use-qwik-method-usage"] # [serde (skip_serializing_if = "Option::is_none")] pub use_qwik_method_usage : Option < RuleConfiguration < biome_rule_options :: use_qwik_method_usage :: UseQwikMethodUsageOptions >> , # [doc = "Disallow unserializable expressions in Qwik dollar ($) scopes.\nSee https://biomejs.dev/linter/rules/use-qwik-valid-lexical-scope"] # [serde (skip_serializing_if = "Option::is_none")] pub use_qwik_valid_lexical_scope : Option < RuleConfiguration < biome_rule_options :: use_qwik_valid_lexical_scope :: UseQwikValidLexicalScopeOptions >> , # [doc = "Enforce RegExp#exec over String#match if no global flag is provided.\nSee https://biomejs.dev/linter/rules/use-regexp-exec"] # [serde (skip_serializing_if = "Option::is_none")] pub use_regexp_exec : Option < RuleConfiguration < biome_rule_options :: use_regexp_exec :: UseRegexpExecOptions >> , # [doc = "Enforce the sorting of CSS utility classes.\nSee https://biomejs.dev/linter/rules/use-sorted-classes"] # [serde (skip_serializing_if = "Option::is_none")] pub use_sorted_classes : Option < RuleFixConfiguration < biome_rule_options :: use_sorted_classes :: UseSortedClassesOptions >> , # [doc = "Enforce the use of the spread operator over .apply().\nSee https://biomejs.dev/linter/rules/use-spread"] # [serde (skip_serializing_if = "Option::is_none")] pub use_spread : Option < RuleFixConfiguration < biome_rule_options :: use_spread :: UseSpreadOptions >> , # [doc = "Enforce unique operation names across a GraphQL document.\nSee https://biomejs.dev/linter/rules/use-unique-graphql-operation-name"] # [serde (skip_serializing_if = "Option::is_none")] pub use_unique_graphql_operation_name : Option < RuleConfiguration < biome_rule_options :: use_unique_graphql_operation_name :: UseUniqueGraphqlOperationNameOptions >> , # [doc = "Enforce specific order of Vue compiler macros.\nSee https://biomejs.dev/linter/rules/use-vue-define-macros-order"] # [serde (skip_serializing_if = "Option::is_none")] pub use_vue_define_macros_order : Option < RuleFixConfiguration < biome_rule_options :: use_vue_define_macros_order :: UseVueDefineMacrosOrderOptions >> , # [doc = "Enforce hyphenated (kebab-case) attribute names in Vue templates.\nSee https://biomejs.dev/linter/rules/use-vue-hyphenated-attributes"] # [serde (skip_serializing_if = "Option::is_none")] pub use_vue_hyphenated_attributes : Option < RuleFixConfiguration < biome_rule_options :: use_vue_hyphenated_attributes :: UseVueHyphenatedAttributesOptions >> , # [doc = "Enforce multi-word component names in Vue components.\nSee https://biomejs.dev/linter/rules/use-vue-multi-word-component-names"] # [serde (skip_serializing_if = "Option::is_none")] pub use_vue_multi_word_component_names : Option < RuleConfiguration < biome_rule_options :: use_vue_multi_word_component_names :: UseVueMultiWordComponentNamesOptions >> , # [doc = "Forbids v-bind directives with missing arguments or invalid modifiers.\nSee https://biomejs.dev/linter/rules/use-vue-valid-v-bind"] # [serde (skip_serializing_if = "Option::is_none")] pub use_vue_valid_v_bind : Option < RuleConfiguration < biome_rule_options :: use_vue_valid_v_bind :: UseVueValidVBindOptions >> , # [doc = "Enforce valid usage of v-else.\nSee https://biomejs.dev/linter/rules/use-vue-valid-v-else"] # [serde (skip_serializing_if = "Option::is_none")] pub use_vue_valid_v_else : Option < RuleConfiguration < biome_rule_options :: use_vue_valid_v_else :: UseVueValidVElseOptions >> , # [doc = "Enforce valid v-else-if directives.\nSee https://biomejs.dev/linter/rules/use-vue-valid-v-else-if"] # [serde (skip_serializing_if = "Option::is_none")] pub use_vue_valid_v_else_if : Option < RuleConfiguration < biome_rule_options :: use_vue_valid_v_else_if :: UseVueValidVElseIfOptions >> , # [doc = "Enforce valid v-html directives.\nSee https://biomejs.dev/linter/rules/use-vue-valid-v-html"] # [serde (skip_serializing_if = "Option::is_none")] pub use_vue_valid_v_html : Option < RuleConfiguration < biome_rule_options :: use_vue_valid_v_html :: UseVueValidVHtmlOptions >> , # [doc = "Enforces valid v-if usage for Vue templates.\nSee https://biomejs.dev/linter/rules/use-vue-valid-v-if"] # [serde (skip_serializing_if = "Option::is_none")] pub use_vue_valid_v_if : Option < RuleConfiguration < biome_rule_options :: use_vue_valid_v_if :: UseVueValidVIfOptions >> , # [doc = "Enforce valid v-on directives with proper arguments, modifiers, and handlers.\nSee https://biomejs.dev/linter/rules/use-vue-valid-v-on"] # [serde (skip_serializing_if = "Option::is_none")] pub use_vue_valid_v_on : Option < RuleConfiguration < biome_rule_options :: use_vue_valid_v_on :: UseVueValidVOnOptions >> , # [doc = "Enforce valid v-text Vue directives.\nSee https://biomejs.dev/linter/rules/use-vue-valid-v-text"] # [serde (skip_serializing_if = "Option::is_none")] pub use_vue_valid_v_text : Option < RuleConfiguration < biome_rule_options :: use_vue_valid_v_text :: UseVueValidVTextOptions >> } +pub struct Nursery { # [doc = r" Enables the recommended rules for this group"] # [serde (skip_serializing_if = "Option::is_none")] pub recommended : Option < bool > , # [doc = "Disallow continue statements.\nSee https://biomejs.dev/linter/rules/no-continue"] # [serde (skip_serializing_if = "Option::is_none")] pub no_continue : Option < RuleConfiguration < biome_rule_options :: no_continue :: NoContinueOptions >> , # [doc = "Restrict imports of deprecated exports.\nSee https://biomejs.dev/linter/rules/no-deprecated-imports"] # [serde (skip_serializing_if = "Option::is_none")] pub no_deprecated_imports : Option < RuleConfiguration < biome_rule_options :: no_deprecated_imports :: NoDeprecatedImportsOptions >> , # [doc = "Prevent the listing of duplicate dependencies. The rule supports the following dependency groups: \"bundledDependencies\", \"bundleDependencies\", \"dependencies\", \"devDependencies\", \"overrides\", \"optionalDependencies\", and \"peerDependencies\".\nSee https://biomejs.dev/linter/rules/no-duplicate-dependencies"] # [serde (skip_serializing_if = "Option::is_none")] pub no_duplicate_dependencies : Option < RuleConfiguration < biome_rule_options :: no_duplicate_dependencies :: NoDuplicateDependenciesOptions >> , # [doc = "Disallow JSX prop spreading the same identifier multiple times.\nSee https://biomejs.dev/linter/rules/no-duplicated-spread-props"] # [serde (skip_serializing_if = "Option::is_none")] pub no_duplicated_spread_props : Option < RuleConfiguration < biome_rule_options :: no_duplicated_spread_props :: NoDuplicatedSpreadPropsOptions >> , # [doc = "Disallow empty sources.\nSee https://biomejs.dev/linter/rules/no-empty-source"] # [serde (skip_serializing_if = "Option::is_none")] pub no_empty_source : Option < RuleConfiguration < biome_rule_options :: no_empty_source :: NoEmptySourceOptions >> , # [doc = "Require the use of === or !== for comparison with null.\nSee https://biomejs.dev/linter/rules/no-equals-to-null"] # [serde (skip_serializing_if = "Option::is_none")] pub no_equals_to_null : Option < RuleFixConfiguration < biome_rule_options :: no_equals_to_null :: NoEqualsToNullOptions >> , # [doc = "Require Promise-like statements to be handled appropriately.\nSee https://biomejs.dev/linter/rules/no-floating-promises"] # [serde (skip_serializing_if = "Option::is_none")] pub no_floating_promises : Option < RuleFixConfiguration < biome_rule_options :: no_floating_promises :: NoFloatingPromisesOptions >> , # [doc = "Disallow iterating using a for-in loop.\nSee https://biomejs.dev/linter/rules/no-for-in"] # [serde (skip_serializing_if = "Option::is_none")] pub no_for_in : Option < RuleConfiguration < biome_rule_options :: no_for_in :: NoForInOptions >> , # [doc = "Prevent import cycles.\nSee https://biomejs.dev/linter/rules/no-import-cycles"] # [serde (skip_serializing_if = "Option::is_none")] pub no_import_cycles : Option < RuleConfiguration < biome_rule_options :: no_import_cycles :: NoImportCyclesOptions >> , # [doc = "Disallows the usage of the unary operators ++ and --.\nSee https://biomejs.dev/linter/rules/no-increment-decrement"] # [serde (skip_serializing_if = "Option::is_none")] pub no_increment_decrement : Option < RuleConfiguration < biome_rule_options :: no_increment_decrement :: NoIncrementDecrementOptions >> , # [doc = "Disallow string literals inside JSX elements.\nSee https://biomejs.dev/linter/rules/no-jsx-literals"] # [serde (skip_serializing_if = "Option::is_none")] pub no_jsx_literals : Option < RuleConfiguration < biome_rule_options :: no_jsx_literals :: NoJsxLiteralsOptions >> , # [doc = "Prevent problematic leaked values from being rendered.\nSee https://biomejs.dev/linter/rules/no-leaked-render"] # [serde (skip_serializing_if = "Option::is_none")] pub no_leaked_render : Option < RuleConfiguration < biome_rule_options :: no_leaked_render :: NoLeakedRenderOptions >> , # [doc = "Disallow Promises to be used in places where they are almost certainly a mistake.\nSee https://biomejs.dev/linter/rules/no-misused-promises"] # [serde (skip_serializing_if = "Option::is_none")] pub no_misused_promises : Option < RuleFixConfiguration < biome_rule_options :: no_misused_promises :: NoMisusedPromisesOptions >> , # [doc = "Disallow creating multiline strings by escaping newlines.\nSee https://biomejs.dev/linter/rules/no-multi-str"] # [serde (skip_serializing_if = "Option::is_none")] pub no_multi_str : Option < RuleConfiguration < biome_rule_options :: no_multi_str :: NoMultiStrOptions >> , # [doc = "Prevent client components from being async functions.\nSee https://biomejs.dev/linter/rules/no-next-async-client-component"] # [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 function parameters that are only used in recursive calls.\nSee https://biomejs.dev/linter/rules/no-parameters-only-used-in-recursion"] # [serde (skip_serializing_if = "Option::is_none")] pub no_parameters_only_used_in_recursion : Option < RuleFixConfiguration < biome_rule_options :: no_parameters_only_used_in_recursion :: NoParametersOnlyUsedInRecursionOptions >> , # [doc = "Disallow the use of the __proto__ property.\nSee https://biomejs.dev/linter/rules/no-proto"] # [serde (skip_serializing_if = "Option::is_none")] pub no_proto : Option < RuleConfiguration < biome_rule_options :: no_proto :: NoProtoOptions >> , # [doc = "Replaces usages of forwardRef with passing ref as a prop.\nSee https://biomejs.dev/linter/rules/no-react-forward-ref"] # [serde (skip_serializing_if = "Option::is_none")] pub no_react_forward_ref : Option < RuleFixConfiguration < biome_rule_options :: no_react_forward_ref :: NoReactForwardRefOptions >> , # [doc = "Disallow variable declarations from shadowing variables declared in the outer scope.\nSee https://biomejs.dev/linter/rules/no-shadow"] # [serde (skip_serializing_if = "Option::is_none")] pub no_shadow : Option < RuleConfiguration < biome_rule_options :: no_shadow :: NoShadowOptions >> , # [doc = "Prevent the usage of synchronous scripts.\nSee https://biomejs.dev/linter/rules/no-sync-scripts"] # [serde (skip_serializing_if = "Option::is_none")] pub no_sync_scripts : Option < RuleConfiguration < biome_rule_options :: no_sync_scripts :: NoSyncScriptsOptions >> , # [doc = "Disallow ternary operators.\nSee https://biomejs.dev/linter/rules/no-ternary"] # [serde (skip_serializing_if = "Option::is_none")] pub no_ternary : Option < RuleConfiguration < biome_rule_options :: no_ternary :: NoTernaryOptions >> , # [doc = "Disallow unknown DOM properties.\nSee https://biomejs.dev/linter/rules/no-unknown-attribute"] # [serde (skip_serializing_if = "Option::is_none")] pub no_unknown_attribute : Option < RuleConfiguration < biome_rule_options :: no_unknown_attribute :: NoUnknownAttributeOptions >> , # [doc = "Disallow unnecessary type-based conditions that can be statically determined as redundant.\nSee https://biomejs.dev/linter/rules/no-unnecessary-conditions"] # [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.\nSee https://biomejs.dev/linter/rules/no-unresolved-imports"] # [serde (skip_serializing_if = "Option::is_none")] pub no_unresolved_imports : Option < RuleConfiguration < biome_rule_options :: no_unresolved_imports :: NoUnresolvedImportsOptions >> , # [doc = "Disallow expression statements that are neither a function call nor an assignment.\nSee https://biomejs.dev/linter/rules/no-unused-expressions"] # [serde (skip_serializing_if = "Option::is_none")] pub no_unused_expressions : Option < RuleConfiguration < biome_rule_options :: no_unused_expressions :: NoUnusedExpressionsOptions >> , # [doc = "Disallow unused catch bindings.\nSee https://biomejs.dev/linter/rules/no-useless-catch-binding"] # [serde (skip_serializing_if = "Option::is_none")] pub no_useless_catch_binding : Option < RuleFixConfiguration < biome_rule_options :: no_useless_catch_binding :: NoUselessCatchBindingOptions >> , # [doc = "Disallow the use of useless undefined.\nSee https://biomejs.dev/linter/rules/no-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.\nSee https://biomejs.dev/linter/rules/no-vue-data-object-declaration"] # [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 duplicate keys in Vue component data, methods, computed properties, and other options.\nSee https://biomejs.dev/linter/rules/no-vue-duplicate-keys"] # [serde (skip_serializing_if = "Option::is_none")] pub no_vue_duplicate_keys : Option < RuleConfiguration < biome_rule_options :: no_vue_duplicate_keys :: NoVueDuplicateKeysOptions >> , # [doc = "Disallow reserved keys in Vue component data and computed properties.\nSee https://biomejs.dev/linter/rules/no-vue-reserved-keys"] # [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.\nSee https://biomejs.dev/linter/rules/no-vue-reserved-props"] # [serde (skip_serializing_if = "Option::is_none")] pub no_vue_reserved_props : Option < RuleConfiguration < biome_rule_options :: no_vue_reserved_props :: NoVueReservedPropsOptions >> , # [doc = "Disallow using v-if and v-for directives on the same element.\nSee https://biomejs.dev/linter/rules/no-vue-v-if-with-v-for"] # [serde (skip_serializing_if = "Option::is_none")] pub no_vue_v_if_with_v_for : Option < RuleConfiguration < biome_rule_options :: no_vue_v_if_with_v_for :: NoVueVIfWithVForOptions >> , # [doc = "Require Array#sort and Array#toSorted calls to always provide a compareFunction.\nSee https://biomejs.dev/linter/rules/use-array-sort-compare"] # [serde (skip_serializing_if = "Option::is_none")] pub use_array_sort_compare : Option < RuleConfiguration < biome_rule_options :: use_array_sort_compare :: UseArraySortCompareOptions >> , # [doc = "Enforce consistent arrow function bodies.\nSee https://biomejs.dev/linter/rules/use-consistent-arrow-return"] # [serde (skip_serializing_if = "Option::is_none")] pub use_consistent_arrow_return : Option < RuleFixConfiguration < biome_rule_options :: use_consistent_arrow_return :: UseConsistentArrowReturnOptions >> , # [doc = "Require all descriptions to follow the same style (either block or inline) to maintain consistency and improve readability across the schema.\nSee https://biomejs.dev/linter/rules/use-consistent-graphql-descriptions"] # [serde (skip_serializing_if = "Option::is_none")] pub use_consistent_graphql_descriptions : Option < RuleConfiguration < biome_rule_options :: use_consistent_graphql_descriptions :: UseConsistentGraphqlDescriptionsOptions >> , # [doc = "Require the @deprecated directive to specify a deletion date.\nSee https://biomejs.dev/linter/rules/use-deprecated-date"] # [serde (skip_serializing_if = "Option::is_none")] pub use_deprecated_date : Option < RuleConfiguration < biome_rule_options :: use_deprecated_date :: UseDeprecatedDateOptions >> , # [doc = "Require switch-case statements to be exhaustive.\nSee https://biomejs.dev/linter/rules/use-exhaustive-switch-cases"] # [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.\nSee https://biomejs.dev/linter/rules/use-explicit-type"] # [serde (skip_serializing_if = "Option::is_none")] pub use_explicit_type : Option < RuleConfiguration < biome_rule_options :: use_explicit_type :: UseExplicitTypeOptions >> , # [doc = "Enforce the use of Array.prototype.find() over Array.prototype.filter() followed by [0] when looking for a single result.\nSee https://biomejs.dev/linter/rules/use-find"] # [serde (skip_serializing_if = "Option::is_none")] pub use_find : Option < RuleConfiguration < biome_rule_options :: use_find :: UseFindOptions >> , # [doc = "Enforce a maximum number of parameters in function definitions.\nSee https://biomejs.dev/linter/rules/use-max-params"] # [serde (skip_serializing_if = "Option::is_none")] pub use_max_params : Option < RuleConfiguration < biome_rule_options :: use_max_params :: UseMaxParamsOptions >> , # [doc = "Disallow use* hooks outside of component$ or other use* hooks in Qwik applications.\nSee https://biomejs.dev/linter/rules/use-qwik-method-usage"] # [serde (skip_serializing_if = "Option::is_none")] pub use_qwik_method_usage : Option < RuleConfiguration < biome_rule_options :: use_qwik_method_usage :: UseQwikMethodUsageOptions >> , # [doc = "Disallow unserializable expressions in Qwik dollar ($) scopes.\nSee https://biomejs.dev/linter/rules/use-qwik-valid-lexical-scope"] # [serde (skip_serializing_if = "Option::is_none")] pub use_qwik_valid_lexical_scope : Option < RuleConfiguration < biome_rule_options :: use_qwik_valid_lexical_scope :: UseQwikValidLexicalScopeOptions >> , # [doc = "Enforce RegExp#exec over String#match if no global flag is provided.\nSee https://biomejs.dev/linter/rules/use-regexp-exec"] # [serde (skip_serializing_if = "Option::is_none")] pub use_regexp_exec : Option < RuleConfiguration < biome_rule_options :: use_regexp_exec :: UseRegexpExecOptions >> , # [doc = "Enforce the presence of required scripts in package.json.\nSee https://biomejs.dev/linter/rules/use-required-scripts"] # [serde (skip_serializing_if = "Option::is_none")] pub use_required_scripts : Option < RuleConfiguration < biome_rule_options :: use_required_scripts :: UseRequiredScriptsOptions >> , # [doc = "Enforce the sorting of CSS utility classes.\nSee https://biomejs.dev/linter/rules/use-sorted-classes"] # [serde (skip_serializing_if = "Option::is_none")] pub use_sorted_classes : Option < RuleFixConfiguration < biome_rule_options :: use_sorted_classes :: UseSortedClassesOptions >> , # [doc = "Enforce the use of the spread operator over .apply().\nSee https://biomejs.dev/linter/rules/use-spread"] # [serde (skip_serializing_if = "Option::is_none")] pub use_spread : Option < RuleFixConfiguration < biome_rule_options :: use_spread :: UseSpreadOptions >> , # [doc = "Enforce unique operation names across a GraphQL document.\nSee https://biomejs.dev/linter/rules/use-unique-graphql-operation-name"] # [serde (skip_serializing_if = "Option::is_none")] pub use_unique_graphql_operation_name : Option < RuleConfiguration < biome_rule_options :: use_unique_graphql_operation_name :: UseUniqueGraphqlOperationNameOptions >> , # [doc = "Enforce specific order of Vue compiler macros.\nSee https://biomejs.dev/linter/rules/use-vue-define-macros-order"] # [serde (skip_serializing_if = "Option::is_none")] pub use_vue_define_macros_order : Option < RuleFixConfiguration < biome_rule_options :: use_vue_define_macros_order :: UseVueDefineMacrosOrderOptions >> , # [doc = "Enforce hyphenated (kebab-case) attribute names in Vue templates.\nSee https://biomejs.dev/linter/rules/use-vue-hyphenated-attributes"] # [serde (skip_serializing_if = "Option::is_none")] pub use_vue_hyphenated_attributes : Option < RuleFixConfiguration < biome_rule_options :: use_vue_hyphenated_attributes :: UseVueHyphenatedAttributesOptions >> , # [doc = "Enforce multi-word component names in Vue components.\nSee https://biomejs.dev/linter/rules/use-vue-multi-word-component-names"] # [serde (skip_serializing_if = "Option::is_none")] pub use_vue_multi_word_component_names : Option < RuleConfiguration < biome_rule_options :: use_vue_multi_word_component_names :: UseVueMultiWordComponentNamesOptions >> , # [doc = "Forbids v-bind directives with missing arguments or invalid modifiers.\nSee https://biomejs.dev/linter/rules/use-vue-valid-v-bind"] # [serde (skip_serializing_if = "Option::is_none")] pub use_vue_valid_v_bind : Option < RuleConfiguration < biome_rule_options :: use_vue_valid_v_bind :: UseVueValidVBindOptions >> , # [doc = "Enforce valid usage of v-else.\nSee https://biomejs.dev/linter/rules/use-vue-valid-v-else"] # [serde (skip_serializing_if = "Option::is_none")] pub use_vue_valid_v_else : Option < RuleConfiguration < biome_rule_options :: use_vue_valid_v_else :: UseVueValidVElseOptions >> , # [doc = "Enforce valid v-else-if directives.\nSee https://biomejs.dev/linter/rules/use-vue-valid-v-else-if"] # [serde (skip_serializing_if = "Option::is_none")] pub use_vue_valid_v_else_if : Option < RuleConfiguration < biome_rule_options :: use_vue_valid_v_else_if :: UseVueValidVElseIfOptions >> , # [doc = "Enforce valid v-html directives.\nSee https://biomejs.dev/linter/rules/use-vue-valid-v-html"] # [serde (skip_serializing_if = "Option::is_none")] pub use_vue_valid_v_html : Option < RuleConfiguration < biome_rule_options :: use_vue_valid_v_html :: UseVueValidVHtmlOptions >> , # [doc = "Enforces valid v-if usage for Vue templates.\nSee https://biomejs.dev/linter/rules/use-vue-valid-v-if"] # [serde (skip_serializing_if = "Option::is_none")] pub use_vue_valid_v_if : Option < RuleConfiguration < biome_rule_options :: use_vue_valid_v_if :: UseVueValidVIfOptions >> , # [doc = "Enforce valid v-on directives with proper arguments, modifiers, and handlers.\nSee https://biomejs.dev/linter/rules/use-vue-valid-v-on"] # [serde (skip_serializing_if = "Option::is_none")] pub use_vue_valid_v_on : Option < RuleConfiguration < biome_rule_options :: use_vue_valid_v_on :: UseVueValidVOnOptions >> , # [doc = "Enforce valid v-text Vue directives.\nSee https://biomejs.dev/linter/rules/use-vue-valid-v-text"] # [serde (skip_serializing_if = "Option::is_none")] pub use_vue_valid_v_text : Option < RuleConfiguration < biome_rule_options :: use_vue_valid_v_text :: UseVueValidVTextOptions >> } impl Nursery { const GROUP_NAME: &'static str = "nursery"; pub(crate) const GROUP_RULES: &'static [&'static str] = &[ @@ -4851,6 +4855,7 @@ impl Nursery { "useQwikMethodUsage", "useQwikValidLexicalScope", "useRegexpExec", + "useRequiredScripts", "useSortedClasses", "useSpread", "useUniqueGraphqlOperationName", @@ -4867,7 +4872,7 @@ impl Nursery { ]; const RECOMMENDED_RULES_AS_FILTERS: &'static [RuleFilter<'static>] = &[ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45]), ]; const ALL_RULES_AS_FILTERS: &'static [RuleFilter<'static>] = &[ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[0]), @@ -4926,6 +4931,7 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[53]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[54]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[55]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[56]), ]; } impl RuleGroupExt for Nursery { @@ -5152,71 +5158,76 @@ impl RuleGroupExt for Nursery { { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42])); } - if let Some(rule) = self.use_sorted_classes.as_ref() + if let Some(rule) = self.use_required_scripts.as_ref() && rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); } - if let Some(rule) = self.use_spread.as_ref() + if let Some(rule) = self.use_sorted_classes.as_ref() && rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44])); } - if let Some(rule) = self.use_unique_graphql_operation_name.as_ref() + if let Some(rule) = self.use_spread.as_ref() && rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45])); } - if let Some(rule) = self.use_vue_define_macros_order.as_ref() + if let Some(rule) = self.use_unique_graphql_operation_name.as_ref() && rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46])); } - if let Some(rule) = self.use_vue_hyphenated_attributes.as_ref() + if let Some(rule) = self.use_vue_define_macros_order.as_ref() && rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); } - if let Some(rule) = self.use_vue_multi_word_component_names.as_ref() + if let Some(rule) = self.use_vue_hyphenated_attributes.as_ref() && rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48])); } - if let Some(rule) = self.use_vue_valid_v_bind.as_ref() + if let Some(rule) = self.use_vue_multi_word_component_names.as_ref() && rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49])); } - if let Some(rule) = self.use_vue_valid_v_else.as_ref() + if let Some(rule) = self.use_vue_valid_v_bind.as_ref() && rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50])); } - if let Some(rule) = self.use_vue_valid_v_else_if.as_ref() + if let Some(rule) = self.use_vue_valid_v_else.as_ref() && rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[51])); } - if let Some(rule) = self.use_vue_valid_v_html.as_ref() + if let Some(rule) = self.use_vue_valid_v_else_if.as_ref() && rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[52])); } - if let Some(rule) = self.use_vue_valid_v_if.as_ref() + if let Some(rule) = self.use_vue_valid_v_html.as_ref() && rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[53])); } - if let Some(rule) = self.use_vue_valid_v_on.as_ref() + if let Some(rule) = self.use_vue_valid_v_if.as_ref() && rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[54])); } - if let Some(rule) = self.use_vue_valid_v_text.as_ref() + if let Some(rule) = self.use_vue_valid_v_on.as_ref() && rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[55])); } + if let Some(rule) = self.use_vue_valid_v_text.as_ref() + && rule.is_enabled() + { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[56])); + } index_set } fn get_disabled_rules(&self) -> FxHashSet> { @@ -5436,71 +5447,76 @@ impl RuleGroupExt for Nursery { { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42])); } - if let Some(rule) = self.use_sorted_classes.as_ref() + if let Some(rule) = self.use_required_scripts.as_ref() && rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); } - if let Some(rule) = self.use_spread.as_ref() + if let Some(rule) = self.use_sorted_classes.as_ref() && rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44])); } - if let Some(rule) = self.use_unique_graphql_operation_name.as_ref() + if let Some(rule) = self.use_spread.as_ref() && rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45])); } - if let Some(rule) = self.use_vue_define_macros_order.as_ref() + if let Some(rule) = self.use_unique_graphql_operation_name.as_ref() && rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46])); } - if let Some(rule) = self.use_vue_hyphenated_attributes.as_ref() + if let Some(rule) = self.use_vue_define_macros_order.as_ref() && rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); } - if let Some(rule) = self.use_vue_multi_word_component_names.as_ref() + if let Some(rule) = self.use_vue_hyphenated_attributes.as_ref() && rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48])); } - if let Some(rule) = self.use_vue_valid_v_bind.as_ref() + if let Some(rule) = self.use_vue_multi_word_component_names.as_ref() && rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49])); } - if let Some(rule) = self.use_vue_valid_v_else.as_ref() + if let Some(rule) = self.use_vue_valid_v_bind.as_ref() && rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50])); } - if let Some(rule) = self.use_vue_valid_v_else_if.as_ref() + if let Some(rule) = self.use_vue_valid_v_else.as_ref() && rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[51])); } - if let Some(rule) = self.use_vue_valid_v_html.as_ref() + if let Some(rule) = self.use_vue_valid_v_else_if.as_ref() && rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[52])); } - if let Some(rule) = self.use_vue_valid_v_if.as_ref() + if let Some(rule) = self.use_vue_valid_v_html.as_ref() && rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[53])); } - if let Some(rule) = self.use_vue_valid_v_on.as_ref() + if let Some(rule) = self.use_vue_valid_v_if.as_ref() && rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[54])); } - if let Some(rule) = self.use_vue_valid_v_text.as_ref() + if let Some(rule) = self.use_vue_valid_v_on.as_ref() && rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[55])); } + if let Some(rule) = self.use_vue_valid_v_text.as_ref() + && rule.is_disabled() + { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[56])); + } index_set } #[doc = r" Checks if, given a rule name, matches one of the rules contained in this category"] @@ -5703,6 +5719,10 @@ impl RuleGroupExt for Nursery { .use_regexp_exec .as_ref() .map(|conf| (conf.level(), conf.get_options())), + "useRequiredScripts" => self + .use_required_scripts + .as_ref() + .map(|conf| (conf.level(), conf.get_options())), "useSortedClasses" => self .use_sorted_classes .as_ref() @@ -5806,6 +5826,7 @@ impl From for Nursery { use_qwik_method_usage: Some(value.into()), use_qwik_valid_lexical_scope: Some(value.into()), use_regexp_exec: Some(value.into()), + use_required_scripts: Some(value.into()), use_sorted_classes: Some(value.into()), use_spread: Some(value.into()), use_unique_graphql_operation_name: Some(value.into()), diff --git a/crates/biome_diagnostics_categories/src/categories.rs b/crates/biome_diagnostics_categories/src/categories.rs index f999c47eee17..7b7ee2fb7137 100644 --- a/crates/biome_diagnostics_categories/src/categories.rs +++ b/crates/biome_diagnostics_categories/src/categories.rs @@ -217,6 +217,7 @@ define_categories! { "lint/nursery/useMaxParams": "https://biomejs.dev/linter/rules/use-max-params", "lint/nursery/useQwikMethodUsage": "https://biomejs.dev/linter/rules/use-qwik-method-usage", "lint/nursery/useQwikValidLexicalScope": "https://biomejs.dev/linter/rules/use-qwik-valid-lexical-scope", + "lint/nursery/useRequiredScripts": "https://biomejs.dev/linter/rules/use-required-scripts", "lint/nursery/useRegexpExec": "https://biomejs.dev/linter/rules/use-regexp-exec", "lint/nursery/useSortedClasses": "https://biomejs.dev/linter/rules/use-sorted-classes", "lint/nursery/useSpread": "https://biomejs.dev/linter/rules/no-spread", diff --git a/crates/biome_json_analyze/Cargo.toml b/crates/biome_json_analyze/Cargo.toml index 9132d85c57c9..dc30f454155b 100644 --- a/crates/biome_json_analyze/Cargo.toml +++ b/crates/biome_json_analyze/Cargo.toml @@ -23,12 +23,12 @@ biome_rowan = { workspace = true } biome_rule_options = { workspace = true } biome_string_case = { workspace = true } biome_suppression = { workspace = true } +camino = { workspace = true } rustc-hash = { workspace = true } [dev-dependencies] biome_json_parser = { path = "../biome_json_parser" } biome_test_utils = { path = "../biome_test_utils" } -camino = { workspace = true } criterion = { package = "codspeed-criterion-compat", version = "=3.0.5" } insta = { workspace = true, features = ["glob"] } tests_macros = { path = "../tests_macros" } diff --git a/crates/biome_json_analyze/src/lint/nursery.rs b/crates/biome_json_analyze/src/lint/nursery.rs index 7be313cae830..88b398f3766c 100644 --- a/crates/biome_json_analyze/src/lint/nursery.rs +++ b/crates/biome_json_analyze/src/lint/nursery.rs @@ -4,4 +4,5 @@ use biome_analyze::declare_lint_group; pub mod no_duplicate_dependencies; -declare_lint_group! { pub Nursery { name : "nursery" , rules : [self :: no_duplicate_dependencies :: NoDuplicateDependencies ,] } } +pub mod use_required_scripts; +declare_lint_group! { pub Nursery { name : "nursery" , rules : [self :: no_duplicate_dependencies :: NoDuplicateDependencies , self :: use_required_scripts :: UseRequiredScripts ,] } } diff --git a/crates/biome_json_analyze/src/lint/nursery/no_duplicate_dependencies.rs b/crates/biome_json_analyze/src/lint/nursery/no_duplicate_dependencies.rs index db72a9d006ec..423acd12b894 100644 --- a/crates/biome_json_analyze/src/lint/nursery/no_duplicate_dependencies.rs +++ b/crates/biome_json_analyze/src/lint/nursery/no_duplicate_dependencies.rs @@ -7,6 +7,8 @@ use biome_rowan::{AstNode, AstSeparatedList}; use biome_rule_options::no_duplicate_dependencies::NoDuplicateDependenciesOptions; use rustc_hash::FxHashMap; +use crate::utils::is_package_json; + declare_lint_rule! { /// Prevent the listing of duplicate dependencies. /// The rule supports the following dependency groups: "bundledDependencies", "bundleDependencies", "dependencies", "devDependencies", "overrides", "optionalDependencies", and "peerDependencies". @@ -89,8 +91,6 @@ declare_lint_rule! { } } -const PACKAGE_JSON: &str = "package.json"; - // dependencies <-> devDependencies / optionalDependencies / peerDependencies // peerDependencies <-> optionalDependencies const UNIQUE_PROPERTY_KEYS: [(&str, &[&str]); 2] = [ @@ -133,7 +133,7 @@ impl Rule for NoDuplicateDependencies { let value = query.value().ok()?; let object_value = value.as_json_object_value()?; - if !path.ends_with(PACKAGE_JSON) { + if !is_package_json(path) { return None; } diff --git a/crates/biome_json_analyze/src/lint/nursery/use_required_scripts.rs b/crates/biome_json_analyze/src/lint/nursery/use_required_scripts.rs new file mode 100644 index 000000000000..335e438b8cc8 --- /dev/null +++ b/crates/biome_json_analyze/src/lint/nursery/use_required_scripts.rs @@ -0,0 +1,155 @@ +use biome_analyze::{Ast, Rule, RuleDiagnostic, context::RuleContext, declare_lint_rule}; +use biome_console::markup; +use biome_json_syntax::{JsonRoot, TextRange}; +use biome_rowan::{AstNode, AstSeparatedList}; +use biome_rule_options::use_required_scripts::UseRequiredScriptsOptions; + +use crate::utils::is_package_json; + +/// State containing the missing scripts and the range to highlight +pub struct UseRequiredScriptsState { + /// The list of missing script names + pub missing_scripts: Vec, + /// The range to highlight in the diagnostic (scripts object or root object) + pub range: TextRange, +} + +declare_lint_rule! { + /// Enforce the presence of required scripts in package.json. + /// + /// This rule ensures that specified scripts are defined in the `scripts` section of a `package.json` file. + /// It's particularly useful in monorepo environments where consistency across workspaces is important. + /// + /// Without required scripts configured, this rule doesn't do anything. + /// + /// ## Examples + /// + /// ### Invalid + /// + /// ```json,options + /// { + /// "options": { + /// "requiredScripts": ["test", "build"] + /// } + /// } + /// ``` + /// + /// ```json,use_options + /// { + /// "scripts": { + /// "test": "vitest" + /// } + /// } + /// ``` + /// + /// ### Valid + /// + /// ```json,use_options + /// { + /// "scripts": { + /// "test": "vitest", + /// "build": "tsc" + /// } + /// } + /// ``` + /// + /// ## Options + /// + /// ### `requiredScripts` + /// + /// An array of script names that must be present in the `scripts` section of `package.json`. + /// Default: `[]` (no scripts required) + /// + pub UseRequiredScripts { + version: "next", + name: "useRequiredScripts", + language: "json", + recommended: false, + } +} + +impl Rule for UseRequiredScripts { + type Query = Ast; + type State = UseRequiredScriptsState; + type Signals = Option; + type Options = UseRequiredScriptsOptions; + + fn run(ctx: &RuleContext) -> Self::Signals { + let query = ctx.query(); + let path = ctx.file_path(); + let options = ctx.options(); + + if !is_package_json(path) { + return None; + } + if options.required_scripts.is_empty() { + return None; + } + + let value = query.value().ok()?; + let object_value = value.as_json_object_value()?; + + let scripts_member = object_value.find_member("scripts"); + + // If there's no scripts section, all required scripts are missing + // Point to the root object in this case + let Some(scripts_member) = scripts_member else { + return Some(UseRequiredScriptsState { + missing_scripts: options.required_scripts.clone(), + range: object_value.range(), + }); + }; + + let scripts_value = scripts_member.value().ok()?; + let scripts_object = scripts_value.as_json_object_value()?; + + let existing_scripts: Vec = scripts_object + .json_member_list() + .iter() + .flatten() + .filter_map(|member| { + let name = member.name().ok()?; + let text = name.inner_string_text().ok()?; + Some(text.to_string()) + }) + .collect(); + + let missing_scripts: Vec = options + .required_scripts + .iter() + .filter(|script| !existing_scripts.iter().any(|s| s == *script)) + .cloned() + .collect(); + + if missing_scripts.is_empty() { + None + } else { + // Point to the scripts member when scripts exist but some are missing + Some(UseRequiredScriptsState { + missing_scripts, + range: scripts_member.range(), + }) + } + } + + fn diagnostic(_ctx: &RuleContext, state: &Self::State) -> Option { + let missing_count = state.missing_scripts.len(); + let missing_list = state.missing_scripts.join(", "); + + let message = if missing_count == 1 { + markup! { + "The required script "{missing_list}" is missing from package.json." + } + } else { + markup! { + "The required scripts "{missing_list}" are missing from package.json." + } + }; + + Some( + RuleDiagnostic::new(rule_category!(), state.range, message).note(markup! { + "Consistent scripts across packages ensure that each can be run reliably from the root of our project. Add the missing script"{{if missing_count > 1 { "s" } else { "" }}}" to your package.json." + }), + ) + } +} diff --git a/crates/biome_json_analyze/src/utils.rs b/crates/biome_json_analyze/src/utils.rs index aa25f8fb24d9..8124d1bc87c2 100644 --- a/crates/biome_json_analyze/src/utils.rs +++ b/crates/biome_json_analyze/src/utils.rs @@ -1,5 +1,6 @@ use biome_json_syntax::JsonMember; use biome_rowan::AstNode; +use camino::Utf8Path; /// Finds the first ancestor [JsonMember], and returns [true] if it's name matches the given input pub(crate) fn matches_parent_object(node: &JsonMember, name: &str) -> bool { @@ -11,3 +12,11 @@ pub(crate) fn matches_parent_object(node: &JsonMember, name: &str) -> bool { .and_then(|member| member.inner_string_text().ok()) .is_some_and(|text| text.text() == name) } + +/// Returns `true` if the given path has a filename of `package.json`. +pub(crate) fn is_package_json(path: &Utf8Path) -> bool { + match path.file_name() { + Some(name) => name == "package.json", + None => false, + } +} diff --git a/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-all-scripts/package.json b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-all-scripts/package.json new file mode 100644 index 000000000000..72a2a23e8fdf --- /dev/null +++ b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-all-scripts/package.json @@ -0,0 +1,6 @@ +{ + "name": "test-package", + "scripts": { + "start": "node index.js" + } +} diff --git a/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-all-scripts/package.json.snap b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-all-scripts/package.json.snap new file mode 100644 index 000000000000..3fac73824aa0 --- /dev/null +++ b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-all-scripts/package.json.snap @@ -0,0 +1,35 @@ +--- +source: crates/biome_json_analyze/tests/spec_tests.rs +expression: package.json +--- +# Input +```json +{ + "name": "test-package", + "scripts": { + "start": "node index.js" + } +} + +``` + +# Diagnostics +``` +package.json:3:3 lint/nursery/useRequiredScripts ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + i The required scripts test, build are missing from package.json. + + 1 │ { + 2 │ "name": "test-package", + > 3 │ "scripts": { + │ ^^^^^^^^^^^^ + > 4 │ "start": "node index.js" + > 5 │ } + │ ^ + 6 │ } + 7 │ + + i Consistent scripts across packages ensure that each can be run reliably from the root of our project. Add the missing scripts to your package.json. + + +``` diff --git a/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-all-scripts/package.options.json b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-all-scripts/package.options.json new file mode 100644 index 000000000000..eece544d5ced --- /dev/null +++ b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-all-scripts/package.options.json @@ -0,0 +1,14 @@ +{ + "linter": { + "rules": { + "nursery": { + "useRequiredScripts": { + "level": "error", + "options": { + "requiredScripts": ["test", "build"] + } + } + } + } + } +} diff --git a/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-multiple-scripts/package.json b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-multiple-scripts/package.json new file mode 100644 index 000000000000..1afaba17dffc --- /dev/null +++ b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-multiple-scripts/package.json @@ -0,0 +1,6 @@ +{ + "name": "test-package", + "scripts": { + "test": "vitest" + } +} diff --git a/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-multiple-scripts/package.json.snap b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-multiple-scripts/package.json.snap new file mode 100644 index 000000000000..30516f0e73cc --- /dev/null +++ b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-multiple-scripts/package.json.snap @@ -0,0 +1,35 @@ +--- +source: crates/biome_json_analyze/tests/spec_tests.rs +expression: package.json +--- +# Input +```json +{ + "name": "test-package", + "scripts": { + "test": "vitest" + } +} + +``` + +# Diagnostics +``` +package.json:3:3 lint/nursery/useRequiredScripts ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + i The required scripts build, lint are missing from package.json. + + 1 │ { + 2 │ "name": "test-package", + > 3 │ "scripts": { + │ ^^^^^^^^^^^^ + > 4 │ "test": "vitest" + > 5 │ } + │ ^ + 6 │ } + 7 │ + + i Consistent scripts across packages ensure that each can be run reliably from the root of our project. Add the missing scripts to your package.json. + + +``` diff --git a/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-multiple-scripts/package.options.json b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-multiple-scripts/package.options.json new file mode 100644 index 000000000000..38d4dd96b208 --- /dev/null +++ b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-multiple-scripts/package.options.json @@ -0,0 +1,14 @@ +{ + "linter": { + "rules": { + "nursery": { + "useRequiredScripts": { + "level": "error", + "options": { + "requiredScripts": ["test", "build", "lint"] + } + } + } + } + } +} diff --git a/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-single-script/package.json b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-single-script/package.json new file mode 100644 index 000000000000..1afaba17dffc --- /dev/null +++ b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-single-script/package.json @@ -0,0 +1,6 @@ +{ + "name": "test-package", + "scripts": { + "test": "vitest" + } +} diff --git a/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-single-script/package.json.snap b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-single-script/package.json.snap new file mode 100644 index 000000000000..bc43e59bfbbb --- /dev/null +++ b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-single-script/package.json.snap @@ -0,0 +1,35 @@ +--- +source: crates/biome_json_analyze/tests/spec_tests.rs +expression: package.json +--- +# Input +```json +{ + "name": "test-package", + "scripts": { + "test": "vitest" + } +} + +``` + +# Diagnostics +``` +package.json:3:3 lint/nursery/useRequiredScripts ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + i The required script build is missing from package.json. + + 1 │ { + 2 │ "name": "test-package", + > 3 │ "scripts": { + │ ^^^^^^^^^^^^ + > 4 │ "test": "vitest" + > 5 │ } + │ ^ + 6 │ } + 7 │ + + i Consistent scripts across packages ensure that each can be run reliably from the root of our project. Add the missing script to your package.json. + + +``` diff --git a/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-single-script/package.options.json b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-single-script/package.options.json new file mode 100644 index 000000000000..eece544d5ced --- /dev/null +++ b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/missing-single-script/package.options.json @@ -0,0 +1,14 @@ +{ + "linter": { + "rules": { + "nursery": { + "useRequiredScripts": { + "level": "error", + "options": { + "requiredScripts": ["test", "build"] + } + } + } + } + } +} diff --git a/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/no-scripts-section/package.json b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/no-scripts-section/package.json new file mode 100644 index 000000000000..d25122b80077 --- /dev/null +++ b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/no-scripts-section/package.json @@ -0,0 +1,4 @@ +{ + "name": "test-package", + "version": "1.0.0" +} diff --git a/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/no-scripts-section/package.json.snap b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/no-scripts-section/package.json.snap new file mode 100644 index 000000000000..63b4383f45bb --- /dev/null +++ b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/no-scripts-section/package.json.snap @@ -0,0 +1,31 @@ +--- +source: crates/biome_json_analyze/tests/spec_tests.rs +expression: package.json +--- +# Input +```json +{ + "name": "test-package", + "version": "1.0.0" +} + +``` + +# Diagnostics +``` +package.json:1:1 lint/nursery/useRequiredScripts ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + i The required scripts test, build are missing from package.json. + + > 1 │ { + │ ^ + > 2 │ "name": "test-package", + > 3 │ "version": "1.0.0" + > 4 │ } + │ ^ + 5 │ + + i Consistent scripts across packages ensure that each can be run reliably from the root of our project. Add the missing scripts to your package.json. + + +``` diff --git a/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/no-scripts-section/package.options.json b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/no-scripts-section/package.options.json new file mode 100644 index 000000000000..eece544d5ced --- /dev/null +++ b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/invalid/no-scripts-section/package.options.json @@ -0,0 +1,14 @@ +{ + "linter": { + "rules": { + "nursery": { + "useRequiredScripts": { + "level": "error", + "options": { + "requiredScripts": ["test", "build"] + } + } + } + } + } +} diff --git a/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/valid/all-required-scripts-present/package.json b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/valid/all-required-scripts-present/package.json new file mode 100644 index 000000000000..12504db3d8d4 --- /dev/null +++ b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/valid/all-required-scripts-present/package.json @@ -0,0 +1,8 @@ +{ + "name": "test-package", + "scripts": { + "test": "vitest", + "build": "tsc", + "lint": "biome lint" + } +} diff --git a/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/valid/all-required-scripts-present/package.json.snap b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/valid/all-required-scripts-present/package.json.snap new file mode 100644 index 000000000000..7cf03ff16536 --- /dev/null +++ b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/valid/all-required-scripts-present/package.json.snap @@ -0,0 +1,16 @@ +--- +source: crates/biome_json_analyze/tests/spec_tests.rs +expression: package.json +--- +# Input +```json +{ + "name": "test-package", + "scripts": { + "test": "vitest", + "build": "tsc", + "lint": "biome lint" + } +} + +``` diff --git a/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/valid/all-required-scripts-present/package.options.json b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/valid/all-required-scripts-present/package.options.json new file mode 100644 index 000000000000..eece544d5ced --- /dev/null +++ b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/valid/all-required-scripts-present/package.options.json @@ -0,0 +1,14 @@ +{ + "linter": { + "rules": { + "nursery": { + "useRequiredScripts": { + "level": "error", + "options": { + "requiredScripts": ["test", "build"] + } + } + } + } + } +} diff --git a/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/valid/extra-scripts-present/package.json b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/valid/extra-scripts-present/package.json new file mode 100644 index 000000000000..d06c545eb4a9 --- /dev/null +++ b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/valid/extra-scripts-present/package.json @@ -0,0 +1,10 @@ +{ + "name": "test-package", + "scripts": { + "test": "vitest", + "build": "tsc", + "lint": "biome lint", + "dev": "vite", + "preview": "vite preview" + } +} diff --git a/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/valid/extra-scripts-present/package.json.snap b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/valid/extra-scripts-present/package.json.snap new file mode 100644 index 000000000000..d8a63acb6478 --- /dev/null +++ b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/valid/extra-scripts-present/package.json.snap @@ -0,0 +1,18 @@ +--- +source: crates/biome_json_analyze/tests/spec_tests.rs +expression: package.json +--- +# Input +```json +{ + "name": "test-package", + "scripts": { + "test": "vitest", + "build": "tsc", + "lint": "biome lint", + "dev": "vite", + "preview": "vite preview" + } +} + +``` diff --git a/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/valid/extra-scripts-present/package.options.json b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/valid/extra-scripts-present/package.options.json new file mode 100644 index 000000000000..eece544d5ced --- /dev/null +++ b/crates/biome_json_analyze/tests/specs/nursery/useRequiredScripts/valid/extra-scripts-present/package.options.json @@ -0,0 +1,14 @@ +{ + "linter": { + "rules": { + "nursery": { + "useRequiredScripts": { + "level": "error", + "options": { + "requiredScripts": ["test", "build"] + } + } + } + } + } +} diff --git a/crates/biome_rule_options/src/lib.rs b/crates/biome_rule_options/src/lib.rs index f6a5a8b66881..b00880b2ba0f 100644 --- a/crates/biome_rule_options/src/lib.rs +++ b/crates/biome_rule_options/src/lib.rs @@ -350,6 +350,7 @@ pub mod use_react_function_components; pub mod use_readonly_class_properties; pub mod use_regex_literals; pub mod use_regexp_exec; +pub mod use_required_scripts; pub mod use_self_closing_elements; pub mod use_semantic_elements; pub mod use_shorthand_assign; diff --git a/crates/biome_rule_options/src/use_required_scripts.rs b/crates/biome_rule_options/src/use_required_scripts.rs new file mode 100644 index 000000000000..426d1279eae6 --- /dev/null +++ b/crates/biome_rule_options/src/use_required_scripts.rs @@ -0,0 +1,11 @@ +use biome_deserialize_macros::{Deserializable, Merge}; +use serde::{Deserialize, Serialize}; + +#[derive(Default, Clone, Debug, Deserialize, Deserializable, Merge, Eq, PartialEq, Serialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields, default)] +pub struct UseRequiredScriptsOptions { + /// List of script names that must be present in package.json + #[serde(default)] + pub required_scripts: Vec, +} diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index 1b7998fc8ab0..f40df49f7b23 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -2075,6 +2075,11 @@ See https://biomejs.dev/linter/rules/use-regexp-exec */ useRegexpExec?: UseRegexpExecConfiguration; /** + * Enforce the presence of required scripts in package.json. +See https://biomejs.dev/linter/rules/use-required-scripts + */ + useRequiredScripts?: UseRequiredScriptsConfiguration; + /** * Enforce the sorting of CSS utility classes. See https://biomejs.dev/linter/rules/use-sorted-classes */ @@ -3692,6 +3697,9 @@ export type UseQwikValidLexicalScopeConfiguration = export type UseRegexpExecConfiguration = | RulePlainConfiguration | RuleWithUseRegexpExecOptions; +export type UseRequiredScriptsConfiguration = + | RulePlainConfiguration + | RuleWithUseRequiredScriptsOptions; export type UseSortedClassesConfiguration = | RulePlainConfiguration | RuleWithUseSortedClassesOptions; @@ -5140,6 +5148,10 @@ export interface RuleWithUseRegexpExecOptions { level: RulePlainConfiguration; options?: UseRegexpExecOptions; } +export interface RuleWithUseRequiredScriptsOptions { + level: RulePlainConfiguration; + options?: UseRequiredScriptsOptions; +} export interface RuleWithUseSortedClassesOptions { fix?: FixKind; level: RulePlainConfiguration; @@ -6397,6 +6409,12 @@ export interface UseMaxParamsOptions { export type UseQwikMethodUsageOptions = {}; export type UseQwikValidLexicalScopeOptions = {}; export type UseRegexpExecOptions = {}; +export interface UseRequiredScriptsOptions { + /** + * List of script names that must be present in package.json + */ + requiredScripts?: string[]; +} export interface UseSortedClassesOptions { /** * Additional attributes that will be sorted. @@ -7209,6 +7227,7 @@ export type Category = | "lint/nursery/useMaxParams" | "lint/nursery/useQwikMethodUsage" | "lint/nursery/useQwikValidLexicalScope" + | "lint/nursery/useRequiredScripts" | "lint/nursery/useRegexpExec" | "lint/nursery/useSortedClasses" | "lint/nursery/useSpread" diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index e18b8736ff11..42abb2b70052 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -5390,6 +5390,13 @@ { "type": "null" } ] }, + "useRequiredScripts": { + "description": "Enforce the presence of required scripts in package.json.\nSee https://biomejs.dev/linter/rules/use-required-scripts", + "anyOf": [ + { "$ref": "#/$defs/UseRequiredScriptsConfiguration" }, + { "type": "null" } + ] + }, "useSortedClasses": { "description": "Enforce the sorting of CSS utility classes.\nSee https://biomejs.dev/linter/rules/use-sorted-classes", "anyOf": [ @@ -9274,6 +9281,15 @@ "additionalProperties": false, "required": ["level"] }, + "RuleWithUseRequiredScriptsOptions": { + "type": "object", + "properties": { + "level": { "$ref": "#/$defs/RulePlainConfiguration" }, + "options": { "$ref": "#/$defs/UseRequiredScriptsOptions" } + }, + "additionalProperties": false, + "required": ["level"] + }, "RuleWithUseSelfClosingElementsOptions": { "type": "object", "properties": { @@ -12191,6 +12207,24 @@ ] }, "UseRegexpExecOptions": { "type": "object", "additionalProperties": false }, + "UseRequiredScriptsConfiguration": { + "oneOf": [ + { "$ref": "#/$defs/RulePlainConfiguration" }, + { "$ref": "#/$defs/RuleWithUseRequiredScriptsOptions" } + ] + }, + "UseRequiredScriptsOptions": { + "type": "object", + "properties": { + "requiredScripts": { + "description": "List of script names that must be present in package.json", + "type": "array", + "default": [], + "items": { "type": "string" } + } + }, + "additionalProperties": false + }, "UseSelfClosingElementsConfiguration": { "oneOf": [ { "$ref": "#/$defs/RulePlainConfiguration" },