From 273e406fb29fe417bcad48d9b5c930a36e58d999 Mon Sep 17 00:00:00 2001 From: dy0gu Date: Thu, 6 Feb 2025 00:36:19 +0000 Subject: [PATCH 01/10] feat(biome_js_analyze): add `useConsistentObjectLiterals` rule --- .changeset/famous-ravens-cross.md | 5 + .../migrate/eslint_any_rule_to_biome.rs | 15 ++ .../src/analyzer/linter/rules.rs | 80 +++++--- .../src/categories.rs | 1 + crates/biome_js_analyze/src/lint/nursery.rs | 3 +- .../nursery/use_consistent_object_literals.rs | 189 ++++++++++++++++++ crates/biome_js_analyze/src/options.rs | 1 + .../invalidExplicit.js | 4 + .../invalidExplicit.js.snap | 46 +++++ .../invalidExplicit.options.json | 16 ++ .../invalidShorthand.js | 4 + .../invalidShorthand.js.snap | 46 +++++ .../invalidShorthand.options.json | 16 ++ .../validExplicit.js | 7 + .../validExplicit.js.snap | 15 ++ .../validExplicit.options.json | 16 ++ .../validShorthand.js | 7 + .../validShorthand.js.snap | 15 ++ .../validShorthand.options.json | 16 ++ .../@biomejs/backend-jsonrpc/src/workspace.ts | 22 ++ .../@biomejs/biome/configuration_schema.json | 46 +++++ 21 files changed, 539 insertions(+), 31 deletions(-) create mode 100644 .changeset/famous-ravens-cross.md create mode 100644 crates/biome_js_analyze/src/lint/nursery/use_consistent_object_literals.rs create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidExplicit.js create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidExplicit.js.snap create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidExplicit.options.json create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidShorthand.js create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidShorthand.js.snap create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidShorthand.options.json create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validExplicit.js create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validExplicit.js.snap create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validExplicit.options.json create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validShorthand.js create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validShorthand.js.snap create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validShorthand.options.json diff --git a/.changeset/famous-ravens-cross.md b/.changeset/famous-ravens-cross.md new file mode 100644 index 000000000000..732b536a0ed3 --- /dev/null +++ b/.changeset/famous-ravens-cross.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": minor +--- + +Add the useConsistentObjectLiterals rule. 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 0fd2eda9e9e2..e1e2309c4527 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 @@ -1787,6 +1787,21 @@ pub(crate) fn migrate_eslint_any_rule( .get_or_insert(Default::default()); rule.set_level(rule.level().max(rule_severity.into())); } + "object-shorthand" => { + if !options.include_inspired { + results.has_inspired_rules = true; + return false; + } + if !options.include_nursery { + return false; + } + let group = rules.nursery.get_or_insert_with(Default::default); + let rule = group + .unwrap_group_as_mut() + .use_consistent_object_literals + .get_or_insert(Default::default()); + rule.set_level(rule.level().max(rule_severity.into())); + } "one-var" => { let group = rules.style.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 9bcf2c01a92c..053eb0f7d31c 100644 --- a/crates/biome_configuration/src/analyzer/linter/rules.rs +++ b/crates/biome_configuration/src/analyzer/linter/rules.rs @@ -3202,6 +3202,10 @@ pub struct Nursery { #[serde(skip_serializing_if = "Option::is_none")] pub use_consistent_member_accessibility: Option>, + #[doc = "Require consistently using either explicit object property assignment or the newer shorthand syntax, when possible."] + #[serde(skip_serializing_if = "Option::is_none")] + pub use_consistent_object_literals: + Option>, #[doc = "Require specifying the reason argument when using @deprecated directive"] #[serde(skip_serializing_if = "Option::is_none")] pub use_deprecated_reason: @@ -3302,6 +3306,7 @@ impl Nursery { "useComponentExportOnlyModules", "useConsistentCurlyBraces", "useConsistentMemberAccessibility", + "useConsistentObjectLiterals", "useDeprecatedReason", "useExplicitType", "useExportsLast", @@ -3331,10 +3336,10 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[37]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[54]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[56]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[58]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[55]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[57]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[59]), ]; const ALL_RULES_AS_FILTERS: &'static [RuleFilter<'static>] = &[ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[0]), @@ -3398,6 +3403,7 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[58]), 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]), ]; } impl RuleGroupExt for Nursery { @@ -3649,71 +3655,76 @@ impl RuleGroupExt for Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); } } - if let Some(rule) = self.use_deprecated_reason.as_ref() { + if let Some(rule) = self.use_consistent_object_literals.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48])); } } - if let Some(rule) = self.use_explicit_type.as_ref() { + if let Some(rule) = self.use_deprecated_reason.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49])); } } - 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[50])); } } - if let Some(rule) = self.use_google_font_display.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[51])); } } - if let Some(rule) = self.use_google_font_preconnect.as_ref() { + if let Some(rule) = self.use_google_font_display.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[52])); } } - if let Some(rule) = self.use_guard_for_in.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[53])); } } - if let Some(rule) = self.use_named_operation.as_ref() { + if let Some(rule) = self.use_guard_for_in.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[54])); } } - 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[55])); } } - if let Some(rule) = self.use_parse_int_radix.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[56])); } } - if let Some(rule) = self.use_sorted_classes.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[57])); } } - if let Some(rule) = self.use_strict_mode.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[58])); } } - if let Some(rule) = self.use_trim_start_end.as_ref() { + if let Some(rule) = self.use_strict_mode.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[59])); } } - if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if let Some(rule) = self.use_trim_start_end.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[60])); } } + if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[61])); + } + } index_set } fn get_disabled_rules(&self) -> FxHashSet> { @@ -3958,71 +3969,76 @@ impl RuleGroupExt for Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); } } - if let Some(rule) = self.use_deprecated_reason.as_ref() { + if let Some(rule) = self.use_consistent_object_literals.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48])); } } - if let Some(rule) = self.use_explicit_type.as_ref() { + if let Some(rule) = self.use_deprecated_reason.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49])); } } - 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[50])); } } - if let Some(rule) = self.use_google_font_display.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[51])); } } - if let Some(rule) = self.use_google_font_preconnect.as_ref() { + if let Some(rule) = self.use_google_font_display.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[52])); } } - if let Some(rule) = self.use_guard_for_in.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[53])); } } - if let Some(rule) = self.use_named_operation.as_ref() { + if let Some(rule) = self.use_guard_for_in.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[54])); } } - 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[55])); } } - if let Some(rule) = self.use_parse_int_radix.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[56])); } } - if let Some(rule) = self.use_sorted_classes.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[57])); } } - if let Some(rule) = self.use_strict_mode.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[58])); } } - if let Some(rule) = self.use_trim_start_end.as_ref() { + if let Some(rule) = self.use_strict_mode.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[59])); } } - if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if let Some(rule) = self.use_trim_start_end.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[60])); } } + if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[61])); + } + } index_set } #[doc = r" Checks if, given a rule name, matches one of the rules contained in this category"] @@ -4245,6 +4261,10 @@ impl RuleGroupExt for Nursery { .use_consistent_member_accessibility .as_ref() .map(|conf| (conf.level(), conf.get_options())), + "useConsistentObjectLiterals" => self + .use_consistent_object_literals + .as_ref() + .map(|conf| (conf.level(), conf.get_options())), "useDeprecatedReason" => self .use_deprecated_reason .as_ref() diff --git a/crates/biome_diagnostics_categories/src/categories.rs b/crates/biome_diagnostics_categories/src/categories.rs index 5d24d1296a72..7207a0a024de 100644 --- a/crates/biome_diagnostics_categories/src/categories.rs +++ b/crates/biome_diagnostics_categories/src/categories.rs @@ -203,6 +203,7 @@ define_categories! { "lint/nursery/useComponentExportOnlyModules": "https://biomejs.dev/linter/rules/use-components-only-module", "lint/nursery/useConsistentCurlyBraces": "https://biomejs.dev/linter/rules/use-consistent-curly-braces", "lint/nursery/useConsistentMemberAccessibility": "https://biomejs.dev/linter/rules/use-consistent-member-accessibility", + "lint/nursery/useConsistentObjectLiterals": "https://biomejs.dev/linter/rules/use-consistent-object-literals", "lint/nursery/useDeprecatedReason": "https://biomejs.dev/linter/rules/use-deprecated-reason", "lint/nursery/useExplicitFunctionReturnType": "https://biomejs.dev/linter/rules/use-explicit-type", "lint/nursery/useExplicitType": "https://biomejs.dev/linter/rules/use-explicit-type", diff --git a/crates/biome_js_analyze/src/lint/nursery.rs b/crates/biome_js_analyze/src/lint/nursery.rs index 43da73dfb3e9..84589e14d514 100644 --- a/crates/biome_js_analyze/src/lint/nursery.rs +++ b/crates/biome_js_analyze/src/lint/nursery.rs @@ -41,6 +41,7 @@ pub mod use_collapsed_if; pub mod use_component_export_only_modules; pub mod use_consistent_curly_braces; pub mod use_consistent_member_accessibility; +pub mod use_consistent_object_literals; pub mod use_explicit_type; pub mod use_exports_last; pub mod use_google_font_display; @@ -51,4 +52,4 @@ pub mod use_sorted_classes; pub mod use_strict_mode; pub mod use_trim_start_end; pub mod use_valid_autocomplete; -declare_lint_group! { pub Nursery { name : "nursery" , rules : [self :: no_common_js :: NoCommonJs , self :: no_document_cookie :: NoDocumentCookie , self :: no_document_import_in_page :: NoDocumentImportInPage , self :: no_duplicate_else_if :: NoDuplicateElseIf , self :: no_dynamic_namespace_import_access :: NoDynamicNamespaceImportAccess , self :: no_enum :: NoEnum , self :: no_exported_imports :: NoExportedImports , self :: no_floating_promises :: NoFloatingPromises , self :: no_global_dirname_filename :: NoGlobalDirnameFilename , self :: no_head_element :: NoHeadElement , self :: no_head_import_in_document :: NoHeadImportInDocument , self :: no_img_element :: NoImgElement , self :: no_import_cycles :: NoImportCycles , self :: no_irregular_whitespace :: NoIrregularWhitespace , self :: no_nested_ternary :: NoNestedTernary , self :: no_noninteractive_element_interactions :: NoNoninteractiveElementInteractions , self :: no_octal_escape :: NoOctalEscape , self :: no_package_private_imports :: NoPackagePrivateImports , self :: no_process_env :: NoProcessEnv , self :: no_process_global :: NoProcessGlobal , self :: no_restricted_imports :: NoRestrictedImports , self :: no_restricted_types :: NoRestrictedTypes , self :: no_secrets :: NoSecrets , self :: no_static_element_interactions :: NoStaticElementInteractions , self :: no_substr :: NoSubstr , self :: no_template_curly_in_string :: NoTemplateCurlyInString , self :: no_ts_ignore :: NoTsIgnore , self :: no_unwanted_polyfillio :: NoUnwantedPolyfillio , self :: no_useless_escape_in_regex :: NoUselessEscapeInRegex , self :: no_useless_string_raw :: NoUselessStringRaw , self :: no_useless_undefined :: NoUselessUndefined , self :: use_adjacent_overload_signatures :: UseAdjacentOverloadSignatures , self :: use_aria_props_supported_by_role :: UseAriaPropsSupportedByRole , self :: use_at_index :: UseAtIndex , self :: use_collapsed_if :: UseCollapsedIf , self :: use_component_export_only_modules :: UseComponentExportOnlyModules , self :: use_consistent_curly_braces :: UseConsistentCurlyBraces , self :: use_consistent_member_accessibility :: UseConsistentMemberAccessibility , self :: use_explicit_type :: UseExplicitType , self :: use_exports_last :: UseExportsLast , self :: use_google_font_display :: UseGoogleFontDisplay , self :: use_google_font_preconnect :: UseGoogleFontPreconnect , self :: use_guard_for_in :: UseGuardForIn , self :: use_parse_int_radix :: UseParseIntRadix , self :: use_sorted_classes :: UseSortedClasses , self :: use_strict_mode :: UseStrictMode , self :: use_trim_start_end :: UseTrimStartEnd , self :: use_valid_autocomplete :: UseValidAutocomplete ,] } } +declare_lint_group! { pub Nursery { name : "nursery" , rules : [self :: no_common_js :: NoCommonJs , self :: no_document_cookie :: NoDocumentCookie , self :: no_document_import_in_page :: NoDocumentImportInPage , self :: no_duplicate_else_if :: NoDuplicateElseIf , self :: no_dynamic_namespace_import_access :: NoDynamicNamespaceImportAccess , self :: no_enum :: NoEnum , self :: no_exported_imports :: NoExportedImports , self :: no_floating_promises :: NoFloatingPromises , self :: no_global_dirname_filename :: NoGlobalDirnameFilename , self :: no_head_element :: NoHeadElement , self :: no_head_import_in_document :: NoHeadImportInDocument , self :: no_img_element :: NoImgElement , self :: no_import_cycles :: NoImportCycles , self :: no_irregular_whitespace :: NoIrregularWhitespace , self :: no_nested_ternary :: NoNestedTernary , self :: no_noninteractive_element_interactions :: NoNoninteractiveElementInteractions , self :: no_octal_escape :: NoOctalEscape , self :: no_package_private_imports :: NoPackagePrivateImports , self :: no_process_env :: NoProcessEnv , self :: no_process_global :: NoProcessGlobal , self :: no_restricted_imports :: NoRestrictedImports , self :: no_restricted_types :: NoRestrictedTypes , self :: no_secrets :: NoSecrets , self :: no_static_element_interactions :: NoStaticElementInteractions , self :: no_substr :: NoSubstr , self :: no_template_curly_in_string :: NoTemplateCurlyInString , self :: no_ts_ignore :: NoTsIgnore , self :: no_unwanted_polyfillio :: NoUnwantedPolyfillio , self :: no_useless_escape_in_regex :: NoUselessEscapeInRegex , self :: no_useless_string_raw :: NoUselessStringRaw , self :: no_useless_undefined :: NoUselessUndefined , self :: use_adjacent_overload_signatures :: UseAdjacentOverloadSignatures , self :: use_aria_props_supported_by_role :: UseAriaPropsSupportedByRole , self :: use_at_index :: UseAtIndex , self :: use_collapsed_if :: UseCollapsedIf , self :: use_component_export_only_modules :: UseComponentExportOnlyModules , self :: use_consistent_curly_braces :: UseConsistentCurlyBraces , self :: use_consistent_member_accessibility :: UseConsistentMemberAccessibility , self :: use_consistent_object_literals :: UseConsistentObjectLiterals , self :: use_explicit_type :: UseExplicitType , self :: use_exports_last :: UseExportsLast , self :: use_google_font_display :: UseGoogleFontDisplay , self :: use_google_font_preconnect :: UseGoogleFontPreconnect , self :: use_guard_for_in :: UseGuardForIn , self :: use_parse_int_radix :: UseParseIntRadix , self :: use_sorted_classes :: UseSortedClasses , self :: use_strict_mode :: UseStrictMode , self :: use_trim_start_end :: UseTrimStartEnd , self :: use_valid_autocomplete :: UseValidAutocomplete ,] } } diff --git a/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_literals.rs b/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_literals.rs new file mode 100644 index 000000000000..763fd3a4eff9 --- /dev/null +++ b/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_literals.rs @@ -0,0 +1,189 @@ +use biome_analyze::{ + context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, RuleSourceKind, +}; +use biome_console::markup; +use biome_deserialize_macros::Deserializable; +use biome_js_syntax::{inner_string_text, AnyJsExpression, AnyJsObjectMember}; +use biome_rowan::AstNode; +use serde::{Deserialize, Serialize}; + +declare_lint_rule! { + /// Require consistently using either explicit object property assignment or the newer shorthand syntax, when possible. + /// + /// ECMAScript 6 provides two ways to define an object literal: `{foo: foo}` and `{foo}`. + /// The two styles are functionally equivalent. + /// Using the same style consistently across your codebase makes it easier to quickly read and understand object definitions. + /// + /// ## Example + /// + /// ### Invalid + /// ```js,expect_diagnostic + /// let foo = 1; + /// let invalid = { + /// foo + /// }; + /// ``` + /// + /// ```js,expect_diagnostic + /// let invalid = { + /// bar() { return "bar"; }, + /// }; + /// ``` + /// + /// ### Valid + /// ```js + /// let foo = 1; + /// let valid = { + /// foo: foo, + /// bar: function() { return "bar"; }, + /// arrow: () => { "arrow" }, + /// get getter() { return "getter"; }, + /// set setter(value) { this._setter = value; } + /// }; + /// ``` + /// + /// ## Options + /// + /// Use the options to specify the syntax of object literals to enforce. + /// + /// ```json,options + /// { + /// "options": { + /// "syntax": "explicit" + /// } + /// } + /// ``` + /// + /// ### syntax + /// + /// The syntax to use: + /// - `explicit`: enforces the use of explicit object property syntax in every case + /// - `shorthand`: enforces the use of shorthand object property syntax when possible + /// + /// Default: `explicit` + pub UseConsistentObjectLiterals { + version: "next", + name: "useConsistentObjectLiterals", + language: "js", + recommended: false, + sources: &[RuleSource::Eslint("object-shorthand")], + source_kind: RuleSourceKind::Inspired, + } +} + +#[derive(Clone, Debug, Default, Deserialize, Deserializable, Eq, PartialEq, Serialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields, default)] +pub struct UseConsistentObjectLiteralsOptions { + syntax: ObjectLiteralSyntax, +} + +#[derive(Clone, Debug, Default, Deserialize, Deserializable, Eq, PartialEq, Serialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub enum ObjectLiteralSyntax { + #[default] + Explicit, + Shorthand, +} + +impl Rule for UseConsistentObjectLiterals { + type Query = Ast; + type State = String; + type Signals = Option; + type Options = UseConsistentObjectLiteralsOptions; + + fn run(ctx: &RuleContext) -> Self::Signals { + let binding = ctx.query(); + let options = ctx.options(); + match binding { + AnyJsObjectMember::JsShorthandPropertyObjectMember(source) => { + let member_token = source.name().ok()?.value_token().ok()?; + let member_id = inner_string_text(&member_token); + + match options.syntax { + ObjectLiteralSyntax::Shorthand => None, + ObjectLiteralSyntax::Explicit => Some(member_id.to_string()), + } + } + AnyJsObjectMember::JsPropertyObjectMember(source) => { + let member_token = source + .name() + .ok()? + .as_js_literal_member_name()? + .value() + .ok()?; + let member_id = inner_string_text(&member_token); + let reference_token = source.value().ok()?; + let reference_id = match reference_token { + AnyJsExpression::JsIdentifierExpression(identifier) => { + let variable_token = identifier.name().ok()?.value_token().ok()?; + inner_string_text(&variable_token) + } + AnyJsExpression::JsCallExpression(call) => { + let callee_token = call + .callee() + .ok()? + .as_js_identifier_expression()? + .name() + .ok()? + .value_token() + .ok()?; + inner_string_text(&callee_token) + } + AnyJsExpression::JsFunctionExpression(function) => { + let function_token = function.function_token().ok()?; + inner_string_text(&function_token) + } + _ => return None, + }; + + match options.syntax { + ObjectLiteralSyntax::Shorthand => { + if member_id == reference_id || "function" == reference_id { + Some(member_id.to_string()) + } else { + None + } + } + ObjectLiteralSyntax::Explicit => None, + } + } + AnyJsObjectMember::JsMethodObjectMember(source) => { + let member_token = source + .name() + .ok()? + .as_js_literal_member_name()? + .value() + .ok()?; + let member_id = inner_string_text(&member_token); + + match options.syntax { + ObjectLiteralSyntax::Shorthand => None, + ObjectLiteralSyntax::Explicit => Some(member_id.to_string()), + } + } + _ => None, + } + } + + fn diagnostic(ctx: &RuleContext, _state: &Self::State) -> Option { + let node = ctx.query(); + let options = ctx.options(); + + let title = match options.syntax { + ObjectLiteralSyntax::Shorthand => { + "Use shorthand object property syntax whenever possible." + } + ObjectLiteralSyntax::Explicit => { + "Always use explicit object property assignment syntax." + } + }; + + Some( + RuleDiagnostic::new(rule_category!(), node.range(), markup! {{title}}).note( + markup! { "Using a consistent object literal syntax makes it easier to anticipate the structure of an object." }, + ), + ) + } +} diff --git a/crates/biome_js_analyze/src/options.rs b/crates/biome_js_analyze/src/options.rs index 700eb09976b8..4f6d03347047 100644 --- a/crates/biome_js_analyze/src/options.rs +++ b/crates/biome_js_analyze/src/options.rs @@ -320,6 +320,7 @@ pub type UseConsistentArrayType = < lint :: style :: use_consistent_array_type : pub type UseConsistentBuiltinInstantiation = < lint :: style :: use_consistent_builtin_instantiation :: UseConsistentBuiltinInstantiation as biome_analyze :: Rule > :: Options ; pub type UseConsistentCurlyBraces = < lint :: nursery :: use_consistent_curly_braces :: UseConsistentCurlyBraces as biome_analyze :: Rule > :: Options ; pub type UseConsistentMemberAccessibility = < lint :: nursery :: use_consistent_member_accessibility :: UseConsistentMemberAccessibility as biome_analyze :: Rule > :: Options ; +pub type UseConsistentObjectLiterals = < lint :: nursery :: use_consistent_object_literals :: UseConsistentObjectLiterals as biome_analyze :: Rule > :: Options ; pub type UseConst = ::Options; pub type UseDateNow = ::Options; pub type UseDefaultParameterLast = < lint :: style :: use_default_parameter_last :: UseDefaultParameterLast as biome_analyze :: Rule > :: Options ; diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidExplicit.js b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidExplicit.js new file mode 100644 index 000000000000..d57b13581623 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidExplicit.js @@ -0,0 +1,4 @@ +const obj = { + foo: foo, + bar: function () { return "bar"; }, +}; diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidExplicit.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidExplicit.js.snap new file mode 100644 index 000000000000..59e678408a69 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidExplicit.js.snap @@ -0,0 +1,46 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: invalidExplicit.js +--- +# Input +```js +const obj = { + foo: foo, + bar: function () { return "bar"; }, +}; + +``` + +# Diagnostics +``` +invalidExplicit.js:2:5 lint/nursery/useConsistentObjectLiterals ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use shorthand object property syntax whenever possible. + + 1 │ const obj = { + > 2 │ foo: foo, + │ ^^^^^^^^ + 3 │ bar: function () { return "bar"; }, + 4 │ }; + + i Using a consistent object literal syntax makes it easier to anticipate the structure of an object. + + +``` + +``` +invalidExplicit.js:3:5 lint/nursery/useConsistentObjectLiterals ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use shorthand object property syntax whenever possible. + + 1 │ const obj = { + 2 │ foo: foo, + > 3 │ bar: function () { return "bar"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 4 │ }; + 5 │ + + i Using a consistent object literal syntax makes it easier to anticipate the structure of an object. + + +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidExplicit.options.json b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidExplicit.options.json new file mode 100644 index 000000000000..3d801acbb133 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidExplicit.options.json @@ -0,0 +1,16 @@ +{ + "$schema": "../../../../../../packages/@biomejs/biome/configuration_schema.json", + "linter": { + "enabled": true, + "rules": { + "nursery": { + "useConsistentObjectLiterals": { + "level": "error", + "options": { + "syntax": "shorthand" + } + } + } + } + } +} diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidShorthand.js b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidShorthand.js new file mode 100644 index 000000000000..5665ad898d4e --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidShorthand.js @@ -0,0 +1,4 @@ +const obj = { + foo, + bar() { return "bar"; }, +}; diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidShorthand.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidShorthand.js.snap new file mode 100644 index 000000000000..9bc71aaa8062 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidShorthand.js.snap @@ -0,0 +1,46 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: invalidShorthand.js +--- +# Input +```js +const obj = { + foo, + bar() { return "bar"; }, +}; + +``` + +# Diagnostics +``` +invalidShorthand.js:2:5 lint/nursery/useConsistentObjectLiterals ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Always use explicit object property assignment syntax. + + 1 │ const obj = { + > 2 │ foo, + │ ^^^ + 3 │ bar() { return "bar"; }, + 4 │ }; + + i Using a consistent object literal syntax makes it easier to anticipate the structure of an object. + + +``` + +``` +invalidShorthand.js:3:5 lint/nursery/useConsistentObjectLiterals ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Always use explicit object property assignment syntax. + + 1 │ const obj = { + 2 │ foo, + > 3 │ bar() { return "bar"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^ + 4 │ }; + 5 │ + + i Using a consistent object literal syntax makes it easier to anticipate the structure of an object. + + +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidShorthand.options.json b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidShorthand.options.json new file mode 100644 index 000000000000..98e85ffd53b0 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidShorthand.options.json @@ -0,0 +1,16 @@ +{ + "$schema": "../../../../../../packages/@biomejs/biome/configuration_schema.json", + "linter": { + "enabled": true, + "rules": { + "nursery": { + "useConsistentObjectLiterals": { + "level": "error", + "options": { + "syntax": "explicit" + } + } + } + } + } +} diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validExplicit.js b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validExplicit.js new file mode 100644 index 000000000000..b63b03eb99cc --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validExplicit.js @@ -0,0 +1,7 @@ +const obj = { + foo: foo, + bar: function () { return "bar"; }, + arrow: () => { "arrow" }, + get getter() { return "getter"; }, + set setter(value) { this._setter = value; } +}; diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validExplicit.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validExplicit.js.snap new file mode 100644 index 000000000000..2dbcc89d8260 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validExplicit.js.snap @@ -0,0 +1,15 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: validExplicit.js +--- +# Input +```js +const obj = { + foo: foo, + bar: function () { return "bar"; }, + arrow: () => { "arrow" }, + get getter() { return "getter"; }, + set setter(value) { this._setter = value; } +}; + +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validExplicit.options.json b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validExplicit.options.json new file mode 100644 index 000000000000..c827cd6f73a7 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validExplicit.options.json @@ -0,0 +1,16 @@ +{ + "$schema": "../../../../../../packages/@biomejs/biome/configuration_schema.json", + "linter": { + "enabled": true, + "rules": { + "nursery": { + "useConsistentObjectLiterals": { + "level": "error", + "options": { + "syntax": "explicit" + } + } + } + } + } +} diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validShorthand.js b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validShorthand.js new file mode 100644 index 000000000000..6e87e93f09d9 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validShorthand.js @@ -0,0 +1,7 @@ +const obj = { + foo, + bar() { return "bar"; }, + arrow: () => { "arrow" }, + get getter() { return "getter"; }, + set setter(value) { this._setter = value; } +}; diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validShorthand.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validShorthand.js.snap new file mode 100644 index 000000000000..7a280c74a3b6 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validShorthand.js.snap @@ -0,0 +1,15 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: validShorthand.js +--- +# Input +```js +const obj = { + foo, + bar() { return "bar"; }, + arrow: () => { "arrow" }, + get getter() { return "getter"; }, + set setter(value) { this._setter = value; } +}; + +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validShorthand.options.json b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validShorthand.options.json new file mode 100644 index 000000000000..ea112ca69429 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validShorthand.options.json @@ -0,0 +1,16 @@ +{ + "$schema": "../../../../../../packages/@biomejs/biome/configuration_schema.json", + "linter": { + "enabled": true, + "rules": { + "nursery": { + "useConsistentObjectLiterals": { + "level": "error", + "options": { + "syntax": "shorthand" + } + } + } + } + } +} diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index cdc7cb87fb35..3cc0b76d16b6 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -1659,6 +1659,10 @@ export interface Nursery { * Require consistent accessibility modifiers on class properties and methods. */ useConsistentMemberAccessibility?: RuleConfiguration_for_ConsistentMemberAccessibilityOptions; + /** + * Require consistently using either explicit object property assignment or the newer shorthand syntax, when possible. + */ + useConsistentObjectLiterals?: RuleConfiguration_for_UseConsistentObjectLiteralsOptions; /** * Require specifying the reason argument when using @deprecated directive */ @@ -2312,6 +2316,9 @@ export type RuleConfiguration_for_UseComponentExportOnlyModulesOptions = export type RuleConfiguration_for_ConsistentMemberAccessibilityOptions = | RulePlainConfiguration | RuleWithOptions_for_ConsistentMemberAccessibilityOptions; +export type RuleConfiguration_for_UseConsistentObjectLiteralsOptions = + | RulePlainConfiguration + | RuleWithOptions_for_UseConsistentObjectLiteralsOptions; export type RuleFixConfiguration_for_UtilityClassSortingOptions = | RulePlainConfiguration | RuleWithFixOptions_for_UtilityClassSortingOptions; @@ -2534,6 +2541,16 @@ export interface RuleWithOptions_for_ConsistentMemberAccessibilityOptions { */ options: ConsistentMemberAccessibilityOptions; } +export interface RuleWithOptions_for_UseConsistentObjectLiteralsOptions { + /** + * The severity of the emitted diagnostics by the rule + */ + level: RulePlainConfiguration; + /** + * Rule's options + */ + options: UseConsistentObjectLiteralsOptions; +} export interface RuleWithFixOptions_for_UtilityClassSortingOptions { /** * The kind of the code actions emitted by the rule @@ -2773,6 +2790,9 @@ export interface UseComponentExportOnlyModulesOptions { export interface ConsistentMemberAccessibilityOptions { accessibility?: Accessibility; } +export interface UseConsistentObjectLiteralsOptions { + syntax?: ObjectLiteralSyntax; +} export interface UtilityClassSortingOptions { /** * Additional attributes that will be sorted. @@ -2899,6 +2919,7 @@ For example, for React's `useRef()` hook the value would be `true`, while for `u stableResult?: StableHookResult; } export type Accessibility = "noPublic" | "explicit" | "none"; +export type ObjectLiteralSyntax = "explicit" | "shorthand"; export type ConsistentArrayType = "shorthand" | "generic"; export type FilenameCases = FilenameCase[]; export type Regex = string; @@ -3359,6 +3380,7 @@ export type Category = | "lint/nursery/useComponentExportOnlyModules" | "lint/nursery/useConsistentCurlyBraces" | "lint/nursery/useConsistentMemberAccessibility" + | "lint/nursery/useConsistentObjectLiterals" | "lint/nursery/useDeprecatedReason" | "lint/nursery/useExplicitFunctionReturnType" | "lint/nursery/useExplicitType" diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index c3c38210af2f..0fbf3d1959d8 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -2821,6 +2821,15 @@ { "type": "null" } ] }, + "useConsistentObjectLiterals": { + "description": "Require consistently using either explicit object property assignment or the newer shorthand syntax, when possible.", + "anyOf": [ + { + "$ref": "#/definitions/UseConsistentObjectLiteralsConfiguration" + }, + { "type": "null" } + ] + }, "useDeprecatedReason": { "description": "Require specifying the reason argument when using @deprecated directive", "anyOf": [ @@ -2915,6 +2924,10 @@ }, "additionalProperties": false }, + "ObjectLiteralSyntax": { + "type": "string", + "enum": ["explicit", "shorthand"] + }, "Options": { "type": "object", "properties": { @@ -3622,6 +3635,23 @@ }, "additionalProperties": false }, + "RuleWithUseConsistentObjectLiteralsOptions": { + "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/UseConsistentObjectLiteralsOptions" } + ] + } + }, + "additionalProperties": false + }, "RuleWithUseExhaustiveDependenciesOptions": { "type": "object", "required": ["level"], @@ -4861,6 +4891,22 @@ }, "additionalProperties": false }, + "UseConsistentObjectLiteralsConfiguration": { + "anyOf": [ + { "$ref": "#/definitions/RulePlainConfiguration" }, + { "$ref": "#/definitions/RuleWithUseConsistentObjectLiteralsOptions" } + ] + }, + "UseConsistentObjectLiteralsOptions": { + "type": "object", + "properties": { + "syntax": { + "default": "explicit", + "allOf": [{ "$ref": "#/definitions/ObjectLiteralSyntax" }] + } + }, + "additionalProperties": false + }, "UseExhaustiveDependenciesConfiguration": { "anyOf": [ { "$ref": "#/definitions/RulePlainConfiguration" }, From eca0c31694590b135711e6262cac881aef64059b Mon Sep 17 00:00:00 2001 From: dy0gu Date: Fri, 7 Feb 2025 17:34:35 +0000 Subject: [PATCH 02/10] chore: improve best practices --- .changeset/famous-ravens-cross.md | 2 +- .../migrate/eslint_any_rule_to_biome.rs | 2 +- .../src/analyzer/linter/rules.rs | 16 +++--- .../src/categories.rs | 2 +- crates/biome_js_analyze/src/lint/nursery.rs | 4 +- ...rs => use_consistent_object_definition.rs} | 52 +++++++------------ crates/biome_js_analyze/src/options.rs | 2 +- .../invalidExplicit.js | 0 .../invalidExplicit.js.snap | 8 +-- .../invalidExplicit.options.json | 2 +- .../invalidShorthand.js | 0 .../invalidShorthand.js.snap | 8 +-- .../invalidShorthand.options.json | 2 +- .../validExplicit.js | 0 .../validExplicit.js.snap | 0 .../validExplicit.options.json | 2 +- .../validShorthand.js | 0 .../validShorthand.js.snap | 0 .../validShorthand.options.json | 2 +- .../@biomejs/backend-jsonrpc/src/workspace.ts | 16 +++--- .../@biomejs/biome/configuration_schema.json | 16 +++--- 21 files changed, 61 insertions(+), 75 deletions(-) rename crates/biome_js_analyze/src/lint/nursery/{use_consistent_object_literals.rs => use_consistent_object_definition.rs} (76%) rename crates/biome_js_analyze/tests/specs/nursery/{useConsistentObjectLiterals => useConsistentObjectDefinition}/invalidExplicit.js (100%) rename crates/biome_js_analyze/tests/specs/nursery/{useConsistentObjectLiterals => useConsistentObjectDefinition}/invalidExplicit.js.snap (54%) rename crates/biome_js_analyze/tests/specs/nursery/{useConsistentObjectLiterals => useConsistentObjectDefinition}/invalidExplicit.options.json (86%) rename crates/biome_js_analyze/tests/specs/nursery/{useConsistentObjectLiterals => useConsistentObjectDefinition}/invalidShorthand.js (100%) rename crates/biome_js_analyze/tests/specs/nursery/{useConsistentObjectLiterals => useConsistentObjectDefinition}/invalidShorthand.js.snap (52%) rename crates/biome_js_analyze/tests/specs/nursery/{useConsistentObjectLiterals => useConsistentObjectDefinition}/invalidShorthand.options.json (86%) rename crates/biome_js_analyze/tests/specs/nursery/{useConsistentObjectLiterals => useConsistentObjectDefinition}/validExplicit.js (100%) rename crates/biome_js_analyze/tests/specs/nursery/{useConsistentObjectLiterals => useConsistentObjectDefinition}/validExplicit.js.snap (100%) rename crates/biome_js_analyze/tests/specs/nursery/{useConsistentObjectLiterals => useConsistentObjectDefinition}/validExplicit.options.json (87%) rename crates/biome_js_analyze/tests/specs/nursery/{useConsistentObjectLiterals => useConsistentObjectDefinition}/validShorthand.js (100%) rename crates/biome_js_analyze/tests/specs/nursery/{useConsistentObjectLiterals => useConsistentObjectDefinition}/validShorthand.js.snap (100%) rename crates/biome_js_analyze/tests/specs/nursery/{useConsistentObjectLiterals => useConsistentObjectDefinition}/validShorthand.options.json (87%) diff --git a/.changeset/famous-ravens-cross.md b/.changeset/famous-ravens-cross.md index 732b536a0ed3..727d061f4c39 100644 --- a/.changeset/famous-ravens-cross.md +++ b/.changeset/famous-ravens-cross.md @@ -2,4 +2,4 @@ "@biomejs/biome": minor --- -Add the useConsistentObjectLiterals rule. +Add the useConsistentObjectDefinition rule. 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 e1e2309c4527..09c32cfe9a9f 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 @@ -1798,7 +1798,7 @@ pub(crate) fn migrate_eslint_any_rule( let group = rules.nursery.get_or_insert_with(Default::default); let rule = group .unwrap_group_as_mut() - .use_consistent_object_literals + .use_consistent_object_definition .get_or_insert(Default::default()); rule.set_level(rule.level().max(rule_severity.into())); } diff --git a/crates/biome_configuration/src/analyzer/linter/rules.rs b/crates/biome_configuration/src/analyzer/linter/rules.rs index 053eb0f7d31c..25b1859f2091 100644 --- a/crates/biome_configuration/src/analyzer/linter/rules.rs +++ b/crates/biome_configuration/src/analyzer/linter/rules.rs @@ -3202,10 +3202,10 @@ pub struct Nursery { #[serde(skip_serializing_if = "Option::is_none")] pub use_consistent_member_accessibility: Option>, - #[doc = "Require consistently using either explicit object property assignment or the newer shorthand syntax, when possible."] + #[doc = "Require the consistent declaration of object literals. Defaults to explicit definitions."] #[serde(skip_serializing_if = "Option::is_none")] - pub use_consistent_object_literals: - Option>, + pub use_consistent_object_definition: + Option>, #[doc = "Require specifying the reason argument when using @deprecated directive"] #[serde(skip_serializing_if = "Option::is_none")] pub use_deprecated_reason: @@ -3306,7 +3306,7 @@ impl Nursery { "useComponentExportOnlyModules", "useConsistentCurlyBraces", "useConsistentMemberAccessibility", - "useConsistentObjectLiterals", + "useConsistentObjectDefinition", "useDeprecatedReason", "useExplicitType", "useExportsLast", @@ -3655,7 +3655,7 @@ impl RuleGroupExt for Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); } } - if let Some(rule) = self.use_consistent_object_literals.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[48])); } @@ -3969,7 +3969,7 @@ impl RuleGroupExt for Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); } } - if let Some(rule) = self.use_consistent_object_literals.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[48])); } @@ -4261,8 +4261,8 @@ impl RuleGroupExt for Nursery { .use_consistent_member_accessibility .as_ref() .map(|conf| (conf.level(), conf.get_options())), - "useConsistentObjectLiterals" => self - .use_consistent_object_literals + "useConsistentObjectDefinition" => self + .use_consistent_object_definition .as_ref() .map(|conf| (conf.level(), conf.get_options())), "useDeprecatedReason" => self diff --git a/crates/biome_diagnostics_categories/src/categories.rs b/crates/biome_diagnostics_categories/src/categories.rs index 7207a0a024de..8ee7628303ce 100644 --- a/crates/biome_diagnostics_categories/src/categories.rs +++ b/crates/biome_diagnostics_categories/src/categories.rs @@ -203,7 +203,7 @@ define_categories! { "lint/nursery/useComponentExportOnlyModules": "https://biomejs.dev/linter/rules/use-components-only-module", "lint/nursery/useConsistentCurlyBraces": "https://biomejs.dev/linter/rules/use-consistent-curly-braces", "lint/nursery/useConsistentMemberAccessibility": "https://biomejs.dev/linter/rules/use-consistent-member-accessibility", - "lint/nursery/useConsistentObjectLiterals": "https://biomejs.dev/linter/rules/use-consistent-object-literals", + "lint/nursery/useConsistentObjectDefinition": "https://biomejs.dev/linter/rules/use-consistent-object-literals", "lint/nursery/useDeprecatedReason": "https://biomejs.dev/linter/rules/use-deprecated-reason", "lint/nursery/useExplicitFunctionReturnType": "https://biomejs.dev/linter/rules/use-explicit-type", "lint/nursery/useExplicitType": "https://biomejs.dev/linter/rules/use-explicit-type", diff --git a/crates/biome_js_analyze/src/lint/nursery.rs b/crates/biome_js_analyze/src/lint/nursery.rs index 84589e14d514..cd6a36a31898 100644 --- a/crates/biome_js_analyze/src/lint/nursery.rs +++ b/crates/biome_js_analyze/src/lint/nursery.rs @@ -41,7 +41,7 @@ pub mod use_collapsed_if; pub mod use_component_export_only_modules; pub mod use_consistent_curly_braces; pub mod use_consistent_member_accessibility; -pub mod use_consistent_object_literals; +pub mod use_consistent_object_definition; pub mod use_explicit_type; pub mod use_exports_last; pub mod use_google_font_display; @@ -52,4 +52,4 @@ pub mod use_sorted_classes; pub mod use_strict_mode; pub mod use_trim_start_end; pub mod use_valid_autocomplete; -declare_lint_group! { pub Nursery { name : "nursery" , rules : [self :: no_common_js :: NoCommonJs , self :: no_document_cookie :: NoDocumentCookie , self :: no_document_import_in_page :: NoDocumentImportInPage , self :: no_duplicate_else_if :: NoDuplicateElseIf , self :: no_dynamic_namespace_import_access :: NoDynamicNamespaceImportAccess , self :: no_enum :: NoEnum , self :: no_exported_imports :: NoExportedImports , self :: no_floating_promises :: NoFloatingPromises , self :: no_global_dirname_filename :: NoGlobalDirnameFilename , self :: no_head_element :: NoHeadElement , self :: no_head_import_in_document :: NoHeadImportInDocument , self :: no_img_element :: NoImgElement , self :: no_import_cycles :: NoImportCycles , self :: no_irregular_whitespace :: NoIrregularWhitespace , self :: no_nested_ternary :: NoNestedTernary , self :: no_noninteractive_element_interactions :: NoNoninteractiveElementInteractions , self :: no_octal_escape :: NoOctalEscape , self :: no_package_private_imports :: NoPackagePrivateImports , self :: no_process_env :: NoProcessEnv , self :: no_process_global :: NoProcessGlobal , self :: no_restricted_imports :: NoRestrictedImports , self :: no_restricted_types :: NoRestrictedTypes , self :: no_secrets :: NoSecrets , self :: no_static_element_interactions :: NoStaticElementInteractions , self :: no_substr :: NoSubstr , self :: no_template_curly_in_string :: NoTemplateCurlyInString , self :: no_ts_ignore :: NoTsIgnore , self :: no_unwanted_polyfillio :: NoUnwantedPolyfillio , self :: no_useless_escape_in_regex :: NoUselessEscapeInRegex , self :: no_useless_string_raw :: NoUselessStringRaw , self :: no_useless_undefined :: NoUselessUndefined , self :: use_adjacent_overload_signatures :: UseAdjacentOverloadSignatures , self :: use_aria_props_supported_by_role :: UseAriaPropsSupportedByRole , self :: use_at_index :: UseAtIndex , self :: use_collapsed_if :: UseCollapsedIf , self :: use_component_export_only_modules :: UseComponentExportOnlyModules , self :: use_consistent_curly_braces :: UseConsistentCurlyBraces , self :: use_consistent_member_accessibility :: UseConsistentMemberAccessibility , self :: use_consistent_object_literals :: UseConsistentObjectLiterals , self :: use_explicit_type :: UseExplicitType , self :: use_exports_last :: UseExportsLast , self :: use_google_font_display :: UseGoogleFontDisplay , self :: use_google_font_preconnect :: UseGoogleFontPreconnect , self :: use_guard_for_in :: UseGuardForIn , self :: use_parse_int_radix :: UseParseIntRadix , self :: use_sorted_classes :: UseSortedClasses , self :: use_strict_mode :: UseStrictMode , self :: use_trim_start_end :: UseTrimStartEnd , self :: use_valid_autocomplete :: UseValidAutocomplete ,] } } +declare_lint_group! { pub Nursery { name : "nursery" , rules : [self :: no_common_js :: NoCommonJs , self :: no_document_cookie :: NoDocumentCookie , self :: no_document_import_in_page :: NoDocumentImportInPage , self :: no_duplicate_else_if :: NoDuplicateElseIf , self :: no_dynamic_namespace_import_access :: NoDynamicNamespaceImportAccess , self :: no_enum :: NoEnum , self :: no_exported_imports :: NoExportedImports , self :: no_floating_promises :: NoFloatingPromises , self :: no_global_dirname_filename :: NoGlobalDirnameFilename , self :: no_head_element :: NoHeadElement , self :: no_head_import_in_document :: NoHeadImportInDocument , self :: no_img_element :: NoImgElement , self :: no_import_cycles :: NoImportCycles , self :: no_irregular_whitespace :: NoIrregularWhitespace , self :: no_nested_ternary :: NoNestedTernary , self :: no_noninteractive_element_interactions :: NoNoninteractiveElementInteractions , self :: no_octal_escape :: NoOctalEscape , self :: no_package_private_imports :: NoPackagePrivateImports , self :: no_process_env :: NoProcessEnv , self :: no_process_global :: NoProcessGlobal , self :: no_restricted_imports :: NoRestrictedImports , self :: no_restricted_types :: NoRestrictedTypes , self :: no_secrets :: NoSecrets , self :: no_static_element_interactions :: NoStaticElementInteractions , self :: no_substr :: NoSubstr , self :: no_template_curly_in_string :: NoTemplateCurlyInString , self :: no_ts_ignore :: NoTsIgnore , self :: no_unwanted_polyfillio :: NoUnwantedPolyfillio , self :: no_useless_escape_in_regex :: NoUselessEscapeInRegex , self :: no_useless_string_raw :: NoUselessStringRaw , self :: no_useless_undefined :: NoUselessUndefined , self :: use_adjacent_overload_signatures :: UseAdjacentOverloadSignatures , self :: use_aria_props_supported_by_role :: UseAriaPropsSupportedByRole , self :: use_at_index :: UseAtIndex , self :: use_collapsed_if :: UseCollapsedIf , self :: use_component_export_only_modules :: UseComponentExportOnlyModules , self :: use_consistent_curly_braces :: UseConsistentCurlyBraces , self :: use_consistent_member_accessibility :: UseConsistentMemberAccessibility , self :: use_consistent_object_definition :: UseConsistentObjectDefinition , self :: use_explicit_type :: UseExplicitType , self :: use_exports_last :: UseExportsLast , self :: use_google_font_display :: UseGoogleFontDisplay , self :: use_google_font_preconnect :: UseGoogleFontPreconnect , self :: use_guard_for_in :: UseGuardForIn , self :: use_parse_int_radix :: UseParseIntRadix , self :: use_sorted_classes :: UseSortedClasses , self :: use_strict_mode :: UseStrictMode , self :: use_trim_start_end :: UseTrimStartEnd , self :: use_valid_autocomplete :: UseValidAutocomplete ,] } } diff --git a/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_literals.rs b/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs similarity index 76% rename from crates/biome_js_analyze/src/lint/nursery/use_consistent_object_literals.rs rename to crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs index 763fd3a4eff9..643e37883c76 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_literals.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs @@ -8,7 +8,7 @@ use biome_rowan::AstNode; use serde::{Deserialize, Serialize}; declare_lint_rule! { - /// Require consistently using either explicit object property assignment or the newer shorthand syntax, when possible. + /// Require the consistent declaration of object literals. Defaults to explicit definitions. /// /// ECMAScript 6 provides two ways to define an object literal: `{foo: foo}` and `{foo}`. /// The two styles are functionally equivalent. @@ -60,10 +60,11 @@ declare_lint_rule! { /// - `explicit`: enforces the use of explicit object property syntax in every case /// - `shorthand`: enforces the use of shorthand object property syntax when possible /// - /// Default: `explicit` - pub UseConsistentObjectLiterals { + /// **Default:** `explicit` + /// + pub UseConsistentObjectDefinition { version: "next", - name: "useConsistentObjectLiterals", + name: "useConsistentObjectDefinition", language: "js", recommended: false, sources: &[RuleSource::Eslint("object-shorthand")], @@ -74,7 +75,7 @@ declare_lint_rule! { #[derive(Clone, Debug, Default, Deserialize, Deserializable, Eq, PartialEq, Serialize)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[serde(rename_all = "camelCase", deny_unknown_fields, default)] -pub struct UseConsistentObjectLiteralsOptions { +pub struct UseConsistentObjectDefinitionOptions { syntax: ObjectLiteralSyntax, } @@ -87,25 +88,20 @@ pub enum ObjectLiteralSyntax { Shorthand, } -impl Rule for UseConsistentObjectLiterals { +impl Rule for UseConsistentObjectDefinition { type Query = Ast; - type State = String; + type State = (); type Signals = Option; - type Options = UseConsistentObjectLiteralsOptions; + type Options = UseConsistentObjectDefinitionOptions; fn run(ctx: &RuleContext) -> Self::Signals { let binding = ctx.query(); let options = ctx.options(); match binding { - AnyJsObjectMember::JsShorthandPropertyObjectMember(source) => { - let member_token = source.name().ok()?.value_token().ok()?; - let member_id = inner_string_text(&member_token); - - match options.syntax { - ObjectLiteralSyntax::Shorthand => None, - ObjectLiteralSyntax::Explicit => Some(member_id.to_string()), - } - } + AnyJsObjectMember::JsShorthandPropertyObjectMember(_) => match options.syntax { + ObjectLiteralSyntax::Shorthand => None, + ObjectLiteralSyntax::Explicit => Some(()), + }, AnyJsObjectMember::JsPropertyObjectMember(source) => { let member_token = source .name() @@ -141,7 +137,7 @@ impl Rule for UseConsistentObjectLiterals { match options.syntax { ObjectLiteralSyntax::Shorthand => { if member_id == reference_id || "function" == reference_id { - Some(member_id.to_string()) + Some(()) } else { None } @@ -149,20 +145,10 @@ impl Rule for UseConsistentObjectLiterals { ObjectLiteralSyntax::Explicit => None, } } - AnyJsObjectMember::JsMethodObjectMember(source) => { - let member_token = source - .name() - .ok()? - .as_js_literal_member_name()? - .value() - .ok()?; - let member_id = inner_string_text(&member_token); - - match options.syntax { - ObjectLiteralSyntax::Shorthand => None, - ObjectLiteralSyntax::Explicit => Some(member_id.to_string()), - } - } + AnyJsObjectMember::JsMethodObjectMember(_) => match options.syntax { + ObjectLiteralSyntax::Shorthand => None, + ObjectLiteralSyntax::Explicit => Some(()), + }, _ => None, } } @@ -182,7 +168,7 @@ impl Rule for UseConsistentObjectLiterals { Some( RuleDiagnostic::new(rule_category!(), node.range(), markup! {{title}}).note( - markup! { "Using a consistent object literal syntax makes it easier to anticipate the structure of an object." }, + markup! { "Using consistent object definitions makes it easier to anticipate the structure of an object." }, ), ) } diff --git a/crates/biome_js_analyze/src/options.rs b/crates/biome_js_analyze/src/options.rs index 4f6d03347047..fddc71eecf67 100644 --- a/crates/biome_js_analyze/src/options.rs +++ b/crates/biome_js_analyze/src/options.rs @@ -320,7 +320,7 @@ pub type UseConsistentArrayType = < lint :: style :: use_consistent_array_type : pub type UseConsistentBuiltinInstantiation = < lint :: style :: use_consistent_builtin_instantiation :: UseConsistentBuiltinInstantiation as biome_analyze :: Rule > :: Options ; pub type UseConsistentCurlyBraces = < lint :: nursery :: use_consistent_curly_braces :: UseConsistentCurlyBraces as biome_analyze :: Rule > :: Options ; pub type UseConsistentMemberAccessibility = < lint :: nursery :: use_consistent_member_accessibility :: UseConsistentMemberAccessibility as biome_analyze :: Rule > :: Options ; -pub type UseConsistentObjectLiterals = < lint :: nursery :: use_consistent_object_literals :: UseConsistentObjectLiterals as biome_analyze :: Rule > :: Options ; +pub type UseConsistentObjectDefinition = < lint :: nursery :: use_consistent_object_definition :: UseConsistentObjectDefinition as biome_analyze :: Rule > :: Options ; pub type UseConst = ::Options; pub type UseDateNow = ::Options; pub type UseDefaultParameterLast = < lint :: style :: use_default_parameter_last :: UseDefaultParameterLast as biome_analyze :: Rule > :: Options ; diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidExplicit.js b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidExplicit.js similarity index 100% rename from crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidExplicit.js rename to crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidExplicit.js diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidExplicit.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidExplicit.js.snap similarity index 54% rename from crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidExplicit.js.snap rename to crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidExplicit.js.snap index 59e678408a69..67bf5ff4d11f 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidExplicit.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidExplicit.js.snap @@ -13,7 +13,7 @@ const obj = { # Diagnostics ``` -invalidExplicit.js:2:5 lint/nursery/useConsistentObjectLiterals ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalidExplicit.js:2:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use shorthand object property syntax whenever possible. @@ -23,13 +23,13 @@ invalidExplicit.js:2:5 lint/nursery/useConsistentObjectLiterals ━━━━━ 3 │ bar: function () { return "bar"; }, 4 │ }; - i Using a consistent object literal syntax makes it easier to anticipate the structure of an object. + i Using consistent object definitions makes it easier to anticipate the structure of an object. ``` ``` -invalidExplicit.js:3:5 lint/nursery/useConsistentObjectLiterals ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalidExplicit.js:3:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use shorthand object property syntax whenever possible. @@ -40,7 +40,7 @@ invalidExplicit.js:3:5 lint/nursery/useConsistentObjectLiterals ━━━━━ 4 │ }; 5 │ - i Using a consistent object literal syntax makes it easier to anticipate the structure of an object. + i Using consistent object definitions makes it easier to anticipate the structure of an object. ``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidExplicit.options.json b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidExplicit.options.json similarity index 86% rename from crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidExplicit.options.json rename to crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidExplicit.options.json index 3d801acbb133..746d3b469c7f 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidExplicit.options.json +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidExplicit.options.json @@ -4,7 +4,7 @@ "enabled": true, "rules": { "nursery": { - "useConsistentObjectLiterals": { + "useConsistentObjectDefinition": { "level": "error", "options": { "syntax": "shorthand" diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidShorthand.js b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidShorthand.js similarity index 100% rename from crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidShorthand.js rename to crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidShorthand.js diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidShorthand.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidShorthand.js.snap similarity index 52% rename from crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidShorthand.js.snap rename to crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidShorthand.js.snap index 9bc71aaa8062..71bf10034fe2 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidShorthand.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidShorthand.js.snap @@ -13,7 +13,7 @@ const obj = { # Diagnostics ``` -invalidShorthand.js:2:5 lint/nursery/useConsistentObjectLiterals ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalidShorthand.js:2:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Always use explicit object property assignment syntax. @@ -23,13 +23,13 @@ invalidShorthand.js:2:5 lint/nursery/useConsistentObjectLiterals ━━━━━ 3 │ bar() { return "bar"; }, 4 │ }; - i Using a consistent object literal syntax makes it easier to anticipate the structure of an object. + i Using consistent object definitions makes it easier to anticipate the structure of an object. ``` ``` -invalidShorthand.js:3:5 lint/nursery/useConsistentObjectLiterals ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalidShorthand.js:3:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Always use explicit object property assignment syntax. @@ -40,7 +40,7 @@ invalidShorthand.js:3:5 lint/nursery/useConsistentObjectLiterals ━━━━━ 4 │ }; 5 │ - i Using a consistent object literal syntax makes it easier to anticipate the structure of an object. + i Using consistent object definitions makes it easier to anticipate the structure of an object. ``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidShorthand.options.json b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidShorthand.options.json similarity index 86% rename from crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidShorthand.options.json rename to crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidShorthand.options.json index 98e85ffd53b0..c4c21e3b4e8f 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/invalidShorthand.options.json +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidShorthand.options.json @@ -4,7 +4,7 @@ "enabled": true, "rules": { "nursery": { - "useConsistentObjectLiterals": { + "useConsistentObjectDefinition": { "level": "error", "options": { "syntax": "explicit" diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validExplicit.js b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validExplicit.js similarity index 100% rename from crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validExplicit.js rename to crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validExplicit.js diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validExplicit.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validExplicit.js.snap similarity index 100% rename from crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validExplicit.js.snap rename to crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validExplicit.js.snap diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validExplicit.options.json b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validExplicit.options.json similarity index 87% rename from crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validExplicit.options.json rename to crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validExplicit.options.json index c827cd6f73a7..0e77667554a3 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validExplicit.options.json +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validExplicit.options.json @@ -4,7 +4,7 @@ "enabled": true, "rules": { "nursery": { - "useConsistentObjectLiterals": { + "useConsistentObjectDefinition": { "level": "error", "options": { "syntax": "explicit" diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validShorthand.js b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validShorthand.js similarity index 100% rename from crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validShorthand.js rename to crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validShorthand.js diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validShorthand.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validShorthand.js.snap similarity index 100% rename from crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validShorthand.js.snap rename to crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validShorthand.js.snap diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validShorthand.options.json b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validShorthand.options.json similarity index 87% rename from crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validShorthand.options.json rename to crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validShorthand.options.json index ea112ca69429..5ca7d68645b1 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectLiterals/validShorthand.options.json +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validShorthand.options.json @@ -4,7 +4,7 @@ "enabled": true, "rules": { "nursery": { - "useConsistentObjectLiterals": { + "useConsistentObjectDefinition": { "level": "error", "options": { "syntax": "shorthand" diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index 3cc0b76d16b6..55e7fc7cc892 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -1660,9 +1660,9 @@ export interface Nursery { */ useConsistentMemberAccessibility?: RuleConfiguration_for_ConsistentMemberAccessibilityOptions; /** - * Require consistently using either explicit object property assignment or the newer shorthand syntax, when possible. + * Require the consistent declaration of object literals. Defaults to explicit definitions. */ - useConsistentObjectLiterals?: RuleConfiguration_for_UseConsistentObjectLiteralsOptions; + useConsistentObjectDefinition?: RuleConfiguration_for_UseConsistentObjectDefinitionOptions; /** * Require specifying the reason argument when using @deprecated directive */ @@ -2316,9 +2316,9 @@ export type RuleConfiguration_for_UseComponentExportOnlyModulesOptions = export type RuleConfiguration_for_ConsistentMemberAccessibilityOptions = | RulePlainConfiguration | RuleWithOptions_for_ConsistentMemberAccessibilityOptions; -export type RuleConfiguration_for_UseConsistentObjectLiteralsOptions = +export type RuleConfiguration_for_UseConsistentObjectDefinitionOptions = | RulePlainConfiguration - | RuleWithOptions_for_UseConsistentObjectLiteralsOptions; + | RuleWithOptions_for_UseConsistentObjectDefinitionOptions; export type RuleFixConfiguration_for_UtilityClassSortingOptions = | RulePlainConfiguration | RuleWithFixOptions_for_UtilityClassSortingOptions; @@ -2541,7 +2541,7 @@ export interface RuleWithOptions_for_ConsistentMemberAccessibilityOptions { */ options: ConsistentMemberAccessibilityOptions; } -export interface RuleWithOptions_for_UseConsistentObjectLiteralsOptions { +export interface RuleWithOptions_for_UseConsistentObjectDefinitionOptions { /** * The severity of the emitted diagnostics by the rule */ @@ -2549,7 +2549,7 @@ export interface RuleWithOptions_for_UseConsistentObjectLiteralsOptions { /** * Rule's options */ - options: UseConsistentObjectLiteralsOptions; + options: UseConsistentObjectDefinitionOptions; } export interface RuleWithFixOptions_for_UtilityClassSortingOptions { /** @@ -2790,7 +2790,7 @@ export interface UseComponentExportOnlyModulesOptions { export interface ConsistentMemberAccessibilityOptions { accessibility?: Accessibility; } -export interface UseConsistentObjectLiteralsOptions { +export interface UseConsistentObjectDefinitionOptions { syntax?: ObjectLiteralSyntax; } export interface UtilityClassSortingOptions { @@ -3380,7 +3380,7 @@ export type Category = | "lint/nursery/useComponentExportOnlyModules" | "lint/nursery/useConsistentCurlyBraces" | "lint/nursery/useConsistentMemberAccessibility" - | "lint/nursery/useConsistentObjectLiterals" + | "lint/nursery/useConsistentObjectDefinition" | "lint/nursery/useDeprecatedReason" | "lint/nursery/useExplicitFunctionReturnType" | "lint/nursery/useExplicitType" diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index 0fbf3d1959d8..6a2ea11d6a78 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -2821,11 +2821,11 @@ { "type": "null" } ] }, - "useConsistentObjectLiterals": { - "description": "Require consistently using either explicit object property assignment or the newer shorthand syntax, when possible.", + "useConsistentObjectDefinition": { + "description": "Require the consistent declaration of object literals. Defaults to explicit definitions.", "anyOf": [ { - "$ref": "#/definitions/UseConsistentObjectLiteralsConfiguration" + "$ref": "#/definitions/UseConsistentObjectDefinitionConfiguration" }, { "type": "null" } ] @@ -3635,7 +3635,7 @@ }, "additionalProperties": false }, - "RuleWithUseConsistentObjectLiteralsOptions": { + "RuleWithUseConsistentObjectDefinitionOptions": { "type": "object", "required": ["level"], "properties": { @@ -3646,7 +3646,7 @@ "options": { "description": "Rule's options", "allOf": [ - { "$ref": "#/definitions/UseConsistentObjectLiteralsOptions" } + { "$ref": "#/definitions/UseConsistentObjectDefinitionOptions" } ] } }, @@ -4891,13 +4891,13 @@ }, "additionalProperties": false }, - "UseConsistentObjectLiteralsConfiguration": { + "UseConsistentObjectDefinitionConfiguration": { "anyOf": [ { "$ref": "#/definitions/RulePlainConfiguration" }, - { "$ref": "#/definitions/RuleWithUseConsistentObjectLiteralsOptions" } + { "$ref": "#/definitions/RuleWithUseConsistentObjectDefinitionOptions" } ] }, - "UseConsistentObjectLiteralsOptions": { + "UseConsistentObjectDefinitionOptions": { "type": "object", "properties": { "syntax": { From a881776ecaee8f86ebcdd2258bc999d22ae3b8cc Mon Sep 17 00:00:00 2001 From: dy0gu Date: Fri, 7 Feb 2025 18:03:44 +0000 Subject: [PATCH 03/10] chore: update files broken by rule rename --- .../src/categories.rs | 2 +- .../use_consistent_object_definition.rs | 20 +++++++++---------- .../@biomejs/backend-jsonrpc/src/workspace.ts | 4 ++-- .../@biomejs/biome/configuration_schema.json | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/crates/biome_diagnostics_categories/src/categories.rs b/crates/biome_diagnostics_categories/src/categories.rs index 8ee7628303ce..d50077da61d2 100644 --- a/crates/biome_diagnostics_categories/src/categories.rs +++ b/crates/biome_diagnostics_categories/src/categories.rs @@ -203,7 +203,7 @@ define_categories! { "lint/nursery/useComponentExportOnlyModules": "https://biomejs.dev/linter/rules/use-components-only-module", "lint/nursery/useConsistentCurlyBraces": "https://biomejs.dev/linter/rules/use-consistent-curly-braces", "lint/nursery/useConsistentMemberAccessibility": "https://biomejs.dev/linter/rules/use-consistent-member-accessibility", - "lint/nursery/useConsistentObjectDefinition": "https://biomejs.dev/linter/rules/use-consistent-object-literals", + "lint/nursery/useConsistentObjectDefinition": "https://biomejs.dev/linter/rules/use-consistent-object-definition", "lint/nursery/useDeprecatedReason": "https://biomejs.dev/linter/rules/use-deprecated-reason", "lint/nursery/useExplicitFunctionReturnType": "https://biomejs.dev/linter/rules/use-explicit-type", "lint/nursery/useExplicitType": "https://biomejs.dev/linter/rules/use-explicit-type", diff --git a/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs b/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs index 643e37883c76..2c514ed34221 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs @@ -76,13 +76,13 @@ declare_lint_rule! { #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[serde(rename_all = "camelCase", deny_unknown_fields, default)] pub struct UseConsistentObjectDefinitionOptions { - syntax: ObjectLiteralSyntax, + syntax: ObjectPropertySyntax, } #[derive(Clone, Debug, Default, Deserialize, Deserializable, Eq, PartialEq, Serialize)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[serde(rename_all = "camelCase", deny_unknown_fields)] -pub enum ObjectLiteralSyntax { +pub enum ObjectPropertySyntax { #[default] Explicit, Shorthand, @@ -99,8 +99,8 @@ impl Rule for UseConsistentObjectDefinition { let options = ctx.options(); match binding { AnyJsObjectMember::JsShorthandPropertyObjectMember(_) => match options.syntax { - ObjectLiteralSyntax::Shorthand => None, - ObjectLiteralSyntax::Explicit => Some(()), + ObjectPropertySyntax::Shorthand => None, + ObjectPropertySyntax::Explicit => Some(()), }, AnyJsObjectMember::JsPropertyObjectMember(source) => { let member_token = source @@ -135,19 +135,19 @@ impl Rule for UseConsistentObjectDefinition { }; match options.syntax { - ObjectLiteralSyntax::Shorthand => { + ObjectPropertySyntax::Shorthand => { if member_id == reference_id || "function" == reference_id { Some(()) } else { None } } - ObjectLiteralSyntax::Explicit => None, + ObjectPropertySyntax::Explicit => None, } } AnyJsObjectMember::JsMethodObjectMember(_) => match options.syntax { - ObjectLiteralSyntax::Shorthand => None, - ObjectLiteralSyntax::Explicit => Some(()), + ObjectPropertySyntax::Shorthand => None, + ObjectPropertySyntax::Explicit => Some(()), }, _ => None, } @@ -158,10 +158,10 @@ impl Rule for UseConsistentObjectDefinition { let options = ctx.options(); let title = match options.syntax { - ObjectLiteralSyntax::Shorthand => { + ObjectPropertySyntax::Shorthand => { "Use shorthand object property syntax whenever possible." } - ObjectLiteralSyntax::Explicit => { + ObjectPropertySyntax::Explicit => { "Always use explicit object property assignment syntax." } }; diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index 55e7fc7cc892..f384f155eba0 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -2791,7 +2791,7 @@ export interface ConsistentMemberAccessibilityOptions { accessibility?: Accessibility; } export interface UseConsistentObjectDefinitionOptions { - syntax?: ObjectLiteralSyntax; + syntax?: ObjectPropertySyntax; } export interface UtilityClassSortingOptions { /** @@ -2919,7 +2919,7 @@ For example, for React's `useRef()` hook the value would be `true`, while for `u stableResult?: StableHookResult; } export type Accessibility = "noPublic" | "explicit" | "none"; -export type ObjectLiteralSyntax = "explicit" | "shorthand"; +export type ObjectPropertySyntax = "explicit" | "shorthand"; export type ConsistentArrayType = "shorthand" | "generic"; export type FilenameCases = FilenameCase[]; export type Regex = string; diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index 6a2ea11d6a78..a25252b0337a 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -2924,7 +2924,7 @@ }, "additionalProperties": false }, - "ObjectLiteralSyntax": { + "ObjectPropertySyntax": { "type": "string", "enum": ["explicit", "shorthand"] }, @@ -4902,7 +4902,7 @@ "properties": { "syntax": { "default": "explicit", - "allOf": [{ "$ref": "#/definitions/ObjectLiteralSyntax" }] + "allOf": [{ "$ref": "#/definitions/ObjectPropertySyntax" }] } }, "additionalProperties": false From 624c4e3a612033cb2ce6dcb5a56e464469257eff Mon Sep 17 00:00:00 2001 From: dy0gu Date: Fri, 7 Feb 2025 18:39:19 +0000 Subject: [PATCH 04/10] chore: improve error messages --- .../nursery/use_consistent_object_definition.rs | 16 +++++++++++----- .../invalidExplicit.js.snap | 8 ++++---- .../invalidShorthand.js.snap | 8 ++++---- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs b/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs index 2c514ed34221..4166e63435ff 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs @@ -159,17 +159,23 @@ impl Rule for UseConsistentObjectDefinition { let title = match options.syntax { ObjectPropertySyntax::Shorthand => { - "Use shorthand object property syntax whenever possible." + "Do not use explicit object property syntax when shorthand syntax is possible." + } + ObjectPropertySyntax::Explicit => "Do not use shorthand object property syntax.", + }; + + let note = match options.syntax { + ObjectPropertySyntax::Shorthand => { + "Using shorthand object property syntax makes object definitions more concise." } ObjectPropertySyntax::Explicit => { - "Always use explicit object property assignment syntax." + "Using explicit object property syntax makes object definitions more readable and consistent." } }; Some( - RuleDiagnostic::new(rule_category!(), node.range(), markup! {{title}}).note( - markup! { "Using consistent object definitions makes it easier to anticipate the structure of an object." }, - ), + RuleDiagnostic::new(rule_category!(), node.range(), markup! {{title}}) + .note(markup! {{note}}), ) } } diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidExplicit.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidExplicit.js.snap index 67bf5ff4d11f..025328af8eef 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidExplicit.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidExplicit.js.snap @@ -15,7 +15,7 @@ const obj = { ``` invalidExplicit.js:2:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Use shorthand object property syntax whenever possible. + ! Do not use explicit object property syntax when shorthand syntax is possible. 1 │ const obj = { > 2 │ foo: foo, @@ -23,7 +23,7 @@ invalidExplicit.js:2:5 lint/nursery/useConsistentObjectDefinition ━━━━ 3 │ bar: function () { return "bar"; }, 4 │ }; - i Using consistent object definitions makes it easier to anticipate the structure of an object. + i Using shorthand object property syntax makes object definitions more concise. ``` @@ -31,7 +31,7 @@ invalidExplicit.js:2:5 lint/nursery/useConsistentObjectDefinition ━━━━ ``` invalidExplicit.js:3:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Use shorthand object property syntax whenever possible. + ! Do not use explicit object property syntax when shorthand syntax is possible. 1 │ const obj = { 2 │ foo: foo, @@ -40,7 +40,7 @@ invalidExplicit.js:3:5 lint/nursery/useConsistentObjectDefinition ━━━━ 4 │ }; 5 │ - i Using consistent object definitions makes it easier to anticipate the structure of an object. + i Using shorthand object property syntax makes object definitions more concise. ``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidShorthand.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidShorthand.js.snap index 71bf10034fe2..9d838a326aab 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidShorthand.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidShorthand.js.snap @@ -15,7 +15,7 @@ const obj = { ``` invalidShorthand.js:2:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Always use explicit object property assignment syntax. + ! Do not use shorthand object property syntax. 1 │ const obj = { > 2 │ foo, @@ -23,7 +23,7 @@ invalidShorthand.js:2:5 lint/nursery/useConsistentObjectDefinition ━━━━ 3 │ bar() { return "bar"; }, 4 │ }; - i Using consistent object definitions makes it easier to anticipate the structure of an object. + i Using explicit object property syntax makes object definitions more readable and consistent. ``` @@ -31,7 +31,7 @@ invalidShorthand.js:2:5 lint/nursery/useConsistentObjectDefinition ━━━━ ``` invalidShorthand.js:3:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - ! Always use explicit object property assignment syntax. + ! Do not use shorthand object property syntax. 1 │ const obj = { 2 │ foo, @@ -40,7 +40,7 @@ invalidShorthand.js:3:5 lint/nursery/useConsistentObjectDefinition ━━━━ 4 │ }; 5 │ - i Using consistent object definitions makes it easier to anticipate the structure of an object. + i Using explicit object property syntax makes object definitions more readable and consistent. ``` From 4cfa65ef39a8bda6c1c43b57a77b936c9b176fa2 Mon Sep 17 00:00:00 2001 From: dy0gu Date: Wed, 12 Feb 2025 12:58:56 +0000 Subject: [PATCH 05/10] fix: catch cases that were missing --- .../use_consistent_object_definition.rs | 47 +-- .../invalidExplicit.js | 20 +- .../invalidExplicit.js.snap | 230 ++++++++++++++- .../invalidShorthand.js | 26 +- .../invalidShorthand.js.snap | 269 +++++++++++++++++- .../validExplicit.js | 45 ++- .../validExplicit.js.snap | 45 ++- .../validShorthand.js | 46 ++- .../validShorthand.js.snap | 46 ++- .../@biomejs/biome/configuration_schema.json | 6 +- 10 files changed, 706 insertions(+), 74 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs b/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs index 4166e63435ff..55d379b288ab 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs @@ -3,7 +3,10 @@ use biome_analyze::{ }; use biome_console::markup; use biome_deserialize_macros::Deserializable; -use biome_js_syntax::{inner_string_text, AnyJsExpression, AnyJsObjectMember}; +use biome_diagnostics::Severity; +use biome_js_syntax::{ + inner_string_text, AnyJsExpression, AnyJsObjectMember, AnyJsObjectMemberName, +}; use biome_rowan::AstNode; use serde::{Deserialize, Serialize}; @@ -36,9 +39,6 @@ declare_lint_rule! { /// let valid = { /// foo: foo, /// bar: function() { return "bar"; }, - /// arrow: () => { "arrow" }, - /// get getter() { return "getter"; }, - /// set setter(value) { this._setter = value; } /// }; /// ``` /// @@ -67,6 +67,7 @@ declare_lint_rule! { name: "useConsistentObjectDefinition", language: "js", recommended: false, + severity: Severity::Error, sources: &[RuleSource::Eslint("object-shorthand")], source_kind: RuleSourceKind::Inspired, } @@ -83,8 +84,10 @@ pub struct UseConsistentObjectDefinitionOptions { #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub enum ObjectPropertySyntax { + /// {foo: foo} #[default] Explicit, + /// {foo} Shorthand, } @@ -103,30 +106,30 @@ impl Rule for UseConsistentObjectDefinition { ObjectPropertySyntax::Explicit => Some(()), }, AnyJsObjectMember::JsPropertyObjectMember(source) => { - let member_token = source - .name() - .ok()? - .as_js_literal_member_name()? - .value() - .ok()?; - let member_id = inner_string_text(&member_token); + let member_token = source.name().ok()?; + let member_id = match member_token { + AnyJsObjectMemberName::JsLiteralMemberName(literal) => { + let literal_token = literal.value().ok()?; + inner_string_text(&literal_token) + } + AnyJsObjectMemberName::JsComputedMemberName(_computed) => { + let reference_token = source.value().ok()?; + match reference_token { + AnyJsExpression::JsFunctionExpression(function) => { + let function_token = function.function_token().ok()?; + inner_string_text(&function_token) + } + _ => return None, + } + } + _ => return None, + }; let reference_token = source.value().ok()?; let reference_id = match reference_token { AnyJsExpression::JsIdentifierExpression(identifier) => { let variable_token = identifier.name().ok()?.value_token().ok()?; inner_string_text(&variable_token) } - AnyJsExpression::JsCallExpression(call) => { - let callee_token = call - .callee() - .ok()? - .as_js_identifier_expression()? - .name() - .ok()? - .value_token() - .ok()?; - inner_string_text(&callee_token) - } AnyJsExpression::JsFunctionExpression(function) => { let function_token = function.function_token().ok()?; inner_string_text(&function_token) diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidExplicit.js b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidExplicit.js index d57b13581623..ea192cc46897 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidExplicit.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidExplicit.js @@ -1,4 +1,20 @@ -const obj = { +const invalidExplicit = { + // Basic property shorthand violations foo: foo, - bar: function () { return "bar"; }, + bar: bar, + baz: baz, + + // Method shorthand violations + method: function () { return "method"; }, + async: async function () { return "async"; }, + generator: function* () { yield "gen"; }, + asyncGenerator: async function* () { yield "async gen"; }, + + // Computed methods shorthand violations + [computed]: function () { return "computed"; }, + [computed]: async function () { return "async computed"; }, + [computed]: function* () { yield "computed gen"; }, + ["computed-string"]: function () { return "computed string"; }, + ["comp" + "uted" + "-con" + "cat"]: function () { return "computed concat"; }, + [computed()]: function () { return "computed dynamic"; }, }; diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidExplicit.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidExplicit.js.snap index 025328af8eef..89729a97884a 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidExplicit.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidExplicit.js.snap @@ -4,24 +4,41 @@ expression: invalidExplicit.js --- # Input ```js -const obj = { +const invalidExplicit = { + // Basic property shorthand violations foo: foo, - bar: function () { return "bar"; }, + bar: bar, + baz: baz, + + // Method shorthand violations + method: function () { return "method"; }, + async: async function () { return "async"; }, + generator: function* () { yield "gen"; }, + asyncGenerator: async function* () { yield "async gen"; }, + + // Computed methods shorthand violations + [computed]: function () { return "computed"; }, + [computed]: async function () { return "async computed"; }, + [computed]: function* () { yield "computed gen"; }, + ["computed-string"]: function () { return "computed string"; }, + ["comp" + "uted" + "-con" + "cat"]: function () { return "computed concat"; }, + [computed()]: function () { return "computed dynamic"; }, }; ``` # Diagnostics ``` -invalidExplicit.js:2:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalidExplicit.js:3:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Do not use explicit object property syntax when shorthand syntax is possible. - 1 │ const obj = { - > 2 │ foo: foo, + 1 │ const invalidExplicit = { + 2 │ // Basic property shorthand violations + > 3 │ foo: foo, │ ^^^^^^^^ - 3 │ bar: function () { return "bar"; }, - 4 │ }; + 4 │ bar: bar, + 5 │ baz: baz, i Using shorthand object property syntax makes object definitions more concise. @@ -29,16 +46,201 @@ invalidExplicit.js:2:5 lint/nursery/useConsistentObjectDefinition ━━━━ ``` ``` -invalidExplicit.js:3:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalidExplicit.js:4:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use explicit object property syntax when shorthand syntax is possible. + + 2 │ // Basic property shorthand violations + 3 │ foo: foo, + > 4 │ bar: bar, + │ ^^^^^^^^ + 5 │ baz: baz, + 6 │ + + i Using shorthand object property syntax makes object definitions more concise. + + +``` + +``` +invalidExplicit.js:5:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use explicit object property syntax when shorthand syntax is possible. + + 3 │ foo: foo, + 4 │ bar: bar, + > 5 │ baz: baz, + │ ^^^^^^^^ + 6 │ + 7 │ // Method shorthand violations + + i Using shorthand object property syntax makes object definitions more concise. + + +``` + +``` +invalidExplicit.js:8:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use explicit object property syntax when shorthand syntax is possible. + + 7 │ // Method shorthand violations + > 8 │ method: function () { return "method"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 9 │ async: async function () { return "async"; }, + 10 │ generator: function* () { yield "gen"; }, + + i Using shorthand object property syntax makes object definitions more concise. + + +``` + +``` +invalidExplicit.js:9:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use explicit object property syntax when shorthand syntax is possible. + + 7 │ // Method shorthand violations + 8 │ method: function () { return "method"; }, + > 9 │ async: async function () { return "async"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 10 │ generator: function* () { yield "gen"; }, + 11 │ asyncGenerator: async function* () { yield "async gen"; }, + + i Using shorthand object property syntax makes object definitions more concise. + + +``` + +``` +invalidExplicit.js:10:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use explicit object property syntax when shorthand syntax is possible. + + 8 │ method: function () { return "method"; }, + 9 │ async: async function () { return "async"; }, + > 10 │ generator: function* () { yield "gen"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 11 │ asyncGenerator: async function* () { yield "async gen"; }, + 12 │ + + i Using shorthand object property syntax makes object definitions more concise. + + +``` + +``` +invalidExplicit.js:11:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use explicit object property syntax when shorthand syntax is possible. + + 9 │ async: async function () { return "async"; }, + 10 │ generator: function* () { yield "gen"; }, + > 11 │ asyncGenerator: async function* () { yield "async gen"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 12 │ + 13 │ // Computed methods shorthand violations + + i Using shorthand object property syntax makes object definitions more concise. + + +``` + +``` +invalidExplicit.js:14:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use explicit object property syntax when shorthand syntax is possible. + + 13 │ // Computed methods shorthand violations + > 14 │ [computed]: function () { return "computed"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 15 │ [computed]: async function () { return "async computed"; }, + 16 │ [computed]: function* () { yield "computed gen"; }, + + i Using shorthand object property syntax makes object definitions more concise. + + +``` + +``` +invalidExplicit.js:15:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use explicit object property syntax when shorthand syntax is possible. + + 13 │ // Computed methods shorthand violations + 14 │ [computed]: function () { return "computed"; }, + > 15 │ [computed]: async function () { return "async computed"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 16 │ [computed]: function* () { yield "computed gen"; }, + 17 │ ["computed-string"]: function () { return "computed string"; }, + + i Using shorthand object property syntax makes object definitions more concise. + + +``` + +``` +invalidExplicit.js:16:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use explicit object property syntax when shorthand syntax is possible. + + 14 │ [computed]: function () { return "computed"; }, + 15 │ [computed]: async function () { return "async computed"; }, + > 16 │ [computed]: function* () { yield "computed gen"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 17 │ ["computed-string"]: function () { return "computed string"; }, + 18 │ ["comp" + "uted" + "-con" + "cat"]: function () { return "computed concat"; }, + + i Using shorthand object property syntax makes object definitions more concise. + + +``` + +``` +invalidExplicit.js:17:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use explicit object property syntax when shorthand syntax is possible. + + 15 │ [computed]: async function () { return "async computed"; }, + 16 │ [computed]: function* () { yield "computed gen"; }, + > 17 │ ["computed-string"]: function () { return "computed string"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 18 │ ["comp" + "uted" + "-con" + "cat"]: function () { return "computed concat"; }, + 19 │ [computed()]: function () { return "computed dynamic"; }, + + i Using shorthand object property syntax makes object definitions more concise. + + +``` + +``` +invalidExplicit.js:18:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use explicit object property syntax when shorthand syntax is possible. + + 16 │ [computed]: function* () { yield "computed gen"; }, + 17 │ ["computed-string"]: function () { return "computed string"; }, + > 18 │ ["comp" + "uted" + "-con" + "cat"]: function () { return "computed concat"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 19 │ [computed()]: function () { return "computed dynamic"; }, + 20 │ }; + + i Using shorthand object property syntax makes object definitions more concise. + + +``` + +``` +invalidExplicit.js:19:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Do not use explicit object property syntax when shorthand syntax is possible. - 1 │ const obj = { - 2 │ foo: foo, - > 3 │ bar: function () { return "bar"; }, - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 4 │ }; - 5 │ + 17 │ ["computed-string"]: function () { return "computed string"; }, + 18 │ ["comp" + "uted" + "-con" + "cat"]: function () { return "computed concat"; }, + > 19 │ [computed()]: function () { return "computed dynamic"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 20 │ }; + 21 │ i Using shorthand object property syntax makes object definitions more concise. diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidShorthand.js b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidShorthand.js index 5665ad898d4e..6862fecc0545 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidShorthand.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidShorthand.js @@ -1,4 +1,24 @@ -const obj = { - foo, - bar() { return "bar"; }, +const invalidShorthand = { + // Basic property explicit violations + prop, + shortProp, + + // Method explicit violations + method() { return "method"; }, + async async() { return "async"; }, + *generator() { yield "gen"; }, + async *asyncGenerator() { yield "async gen"; }, + + // Computed methods + [computed]() { return "computed"; }, + async [computed]() { return "async computed"; }, + *[computed]() { yield "computed gen"; }, + ["computed-string"]() { return "computed string"; }, + ["comp" + "uted" + "-con" + "cat"]() { return "computed concat"; }, + [computed()]() { return "computed dynamic"; }, + + // String literal methods + 'quotedMethod'() { return "quoted"; }, + "doubleQuoted"() { return "double quoted"; }, + async 'asyncQuoted'() { return "async quoted"; }, }; diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidShorthand.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidShorthand.js.snap index 9d838a326aab..357a1da6fc25 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidShorthand.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/invalidShorthand.js.snap @@ -4,24 +4,45 @@ expression: invalidShorthand.js --- # Input ```js -const obj = { - foo, - bar() { return "bar"; }, +const invalidShorthand = { + // Basic property explicit violations + prop, + shortProp, + + // Method explicit violations + method() { return "method"; }, + async async() { return "async"; }, + *generator() { yield "gen"; }, + async *asyncGenerator() { yield "async gen"; }, + + // Computed methods + [computed]() { return "computed"; }, + async [computed]() { return "async computed"; }, + *[computed]() { yield "computed gen"; }, + ["computed-string"]() { return "computed string"; }, + ["comp" + "uted" + "-con" + "cat"]() { return "computed concat"; }, + [computed()]() { return "computed dynamic"; }, + + // String literal methods + 'quotedMethod'() { return "quoted"; }, + "doubleQuoted"() { return "double quoted"; }, + async 'asyncQuoted'() { return "async quoted"; }, }; ``` # Diagnostics ``` -invalidShorthand.js:2:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalidShorthand.js:3:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Do not use shorthand object property syntax. - 1 │ const obj = { - > 2 │ foo, - │ ^^^ - 3 │ bar() { return "bar"; }, - 4 │ }; + 1 │ const invalidShorthand = { + 2 │ // Basic property explicit violations + > 3 │ prop, + │ ^^^^ + 4 │ shortProp, + 5 │ i Using explicit object property syntax makes object definitions more readable and consistent. @@ -29,16 +50,234 @@ invalidShorthand.js:2:5 lint/nursery/useConsistentObjectDefinition ━━━━ ``` ``` -invalidShorthand.js:3:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalidShorthand.js:4:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Do not use shorthand object property syntax. - 1 │ const obj = { - 2 │ foo, - > 3 │ bar() { return "bar"; }, - │ ^^^^^^^^^^^^^^^^^^^^^^^ - 4 │ }; + 2 │ // Basic property explicit violations + 3 │ prop, + > 4 │ shortProp, + │ ^^^^^^^^^ 5 │ + 6 │ // Method explicit violations + + i Using explicit object property syntax makes object definitions more readable and consistent. + + +``` + +``` +invalidShorthand.js:7:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use shorthand object property syntax. + + 6 │ // Method explicit violations + > 7 │ method() { return "method"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 8 │ async async() { return "async"; }, + 9 │ *generator() { yield "gen"; }, + + i Using explicit object property syntax makes object definitions more readable and consistent. + + +``` + +``` +invalidShorthand.js:8:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use shorthand object property syntax. + + 6 │ // Method explicit violations + 7 │ method() { return "method"; }, + > 8 │ async async() { return "async"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 9 │ *generator() { yield "gen"; }, + 10 │ async *asyncGenerator() { yield "async gen"; }, + + i Using explicit object property syntax makes object definitions more readable and consistent. + + +``` + +``` +invalidShorthand.js:9:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use shorthand object property syntax. + + 7 │ method() { return "method"; }, + 8 │ async async() { return "async"; }, + > 9 │ *generator() { yield "gen"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 10 │ async *asyncGenerator() { yield "async gen"; }, + 11 │ + + i Using explicit object property syntax makes object definitions more readable and consistent. + + +``` + +``` +invalidShorthand.js:10:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use shorthand object property syntax. + + 8 │ async async() { return "async"; }, + 9 │ *generator() { yield "gen"; }, + > 10 │ async *asyncGenerator() { yield "async gen"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 11 │ + 12 │ // Computed methods + + i Using explicit object property syntax makes object definitions more readable and consistent. + + +``` + +``` +invalidShorthand.js:13:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use shorthand object property syntax. + + 12 │ // Computed methods + > 13 │ [computed]() { return "computed"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 14 │ async [computed]() { return "async computed"; }, + 15 │ *[computed]() { yield "computed gen"; }, + + i Using explicit object property syntax makes object definitions more readable and consistent. + + +``` + +``` +invalidShorthand.js:14:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use shorthand object property syntax. + + 12 │ // Computed methods + 13 │ [computed]() { return "computed"; }, + > 14 │ async [computed]() { return "async computed"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 15 │ *[computed]() { yield "computed gen"; }, + 16 │ ["computed-string"]() { return "computed string"; }, + + i Using explicit object property syntax makes object definitions more readable and consistent. + + +``` + +``` +invalidShorthand.js:15:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use shorthand object property syntax. + + 13 │ [computed]() { return "computed"; }, + 14 │ async [computed]() { return "async computed"; }, + > 15 │ *[computed]() { yield "computed gen"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 16 │ ["computed-string"]() { return "computed string"; }, + 17 │ ["comp" + "uted" + "-con" + "cat"]() { return "computed concat"; }, + + i Using explicit object property syntax makes object definitions more readable and consistent. + + +``` + +``` +invalidShorthand.js:16:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use shorthand object property syntax. + + 14 │ async [computed]() { return "async computed"; }, + 15 │ *[computed]() { yield "computed gen"; }, + > 16 │ ["computed-string"]() { return "computed string"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 17 │ ["comp" + "uted" + "-con" + "cat"]() { return "computed concat"; }, + 18 │ [computed()]() { return "computed dynamic"; }, + + i Using explicit object property syntax makes object definitions more readable and consistent. + + +``` + +``` +invalidShorthand.js:17:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use shorthand object property syntax. + + 15 │ *[computed]() { yield "computed gen"; }, + 16 │ ["computed-string"]() { return "computed string"; }, + > 17 │ ["comp" + "uted" + "-con" + "cat"]() { return "computed concat"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 18 │ [computed()]() { return "computed dynamic"; }, + 19 │ + + i Using explicit object property syntax makes object definitions more readable and consistent. + + +``` + +``` +invalidShorthand.js:18:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use shorthand object property syntax. + + 16 │ ["computed-string"]() { return "computed string"; }, + 17 │ ["comp" + "uted" + "-con" + "cat"]() { return "computed concat"; }, + > 18 │ [computed()]() { return "computed dynamic"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 19 │ + 20 │ // String literal methods + + i Using explicit object property syntax makes object definitions more readable and consistent. + + +``` + +``` +invalidShorthand.js:21:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use shorthand object property syntax. + + 20 │ // String literal methods + > 21 │ 'quotedMethod'() { return "quoted"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 22 │ "doubleQuoted"() { return "double quoted"; }, + 23 │ async 'asyncQuoted'() { return "async quoted"; }, + + i Using explicit object property syntax makes object definitions more readable and consistent. + + +``` + +``` +invalidShorthand.js:22:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use shorthand object property syntax. + + 20 │ // String literal methods + 21 │ 'quotedMethod'() { return "quoted"; }, + > 22 │ "doubleQuoted"() { return "double quoted"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 23 │ async 'asyncQuoted'() { return "async quoted"; }, + 24 │ }; + + i Using explicit object property syntax makes object definitions more readable and consistent. + + +``` + +``` +invalidShorthand.js:23:5 lint/nursery/useConsistentObjectDefinition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use shorthand object property syntax. + + 21 │ 'quotedMethod'() { return "quoted"; }, + 22 │ "doubleQuoted"() { return "double quoted"; }, + > 23 │ async 'asyncQuoted'() { return "async quoted"; }, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 24 │ }; + 25 │ i Using explicit object property syntax makes object definitions more readable and consistent. diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validExplicit.js b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validExplicit.js index b63b03eb99cc..5e5cac760297 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validExplicit.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validExplicit.js @@ -1,7 +1,44 @@ -const obj = { +const validExplicit = { + // Properties with explicit values foo: foo, - bar: function () { return "bar"; }, - arrow: () => { "arrow" }, + bar: bar, + baz: baz, + + // Methods with function expressions + method: function () { return "method"; }, + async: async function () { return "async"; }, + generator: function* () { yield "gen"; }, + asyncGenerator: async function* () { yield "async gen"; }, + + // String literals + "stringLiteral": "stringLiteral", + "quotedProperty": quotedProperty, + 'singleQuoted': singleQuoted, + + // Computed methods + [computed]: function () { return "computed"; }, + [computed]: async function () { return "async computed"; }, + + // Under this sections should go properties that can't be shorthanded + // Meaning they are valid with either explicit or shorthand property option + + // Call expressions + call: example(), + callLiteral: "example"(), + + // Computed properties + [dynamic()]: dynamicValue, + [computed]: computed, + ["computed-string"]: computedString, + + // Arrow functions + arrow: () => "arrow", + arrowWithBlock: () => { return "arrow block"; }, + asyncArrow: async () => "async arrow", + + // Accessors get getter() { return "getter"; }, - set setter(value) { this._setter = value; } + set setter(value) { this._setter = value; }, + + ...spread, }; diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validExplicit.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validExplicit.js.snap index 2dbcc89d8260..06906839a21f 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validExplicit.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validExplicit.js.snap @@ -4,12 +4,49 @@ expression: validExplicit.js --- # Input ```js -const obj = { +const validExplicit = { + // Properties with explicit values foo: foo, - bar: function () { return "bar"; }, - arrow: () => { "arrow" }, + bar: bar, + baz: baz, + + // Methods with function expressions + method: function () { return "method"; }, + async: async function () { return "async"; }, + generator: function* () { yield "gen"; }, + asyncGenerator: async function* () { yield "async gen"; }, + + // String literals + "stringLiteral": "stringLiteral", + "quotedProperty": quotedProperty, + 'singleQuoted': singleQuoted, + + // Computed methods + [computed]: function () { return "computed"; }, + [computed]: async function () { return "async computed"; }, + + // Under this sections should go properties that can't be shorthanded + // Meaning they are valid with either explicit or shorthand property option + + // Call expressions + call: example(), + callLiteral: "example"(), + + // Computed properties + [dynamic()]: dynamicValue, + [computed]: computed, + ["computed-string"]: computedString, + + // Arrow functions + arrow: () => "arrow", + arrowWithBlock: () => { return "arrow block"; }, + asyncArrow: async () => "async arrow", + + // Accessors get getter() { return "getter"; }, - set setter(value) { this._setter = value; } + set setter(value) { this._setter = value; }, + + ...spread, }; ``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validShorthand.js b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validShorthand.js index 6e87e93f09d9..3a6ed088b570 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validShorthand.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validShorthand.js @@ -1,7 +1,45 @@ -const obj = { +const validShorthand = { + // Property shorthand foo, - bar() { return "bar"; }, - arrow: () => { "arrow" }, + bar, + baz, + + // Method shorthand + method() { return "method"; }, + async async() { return "async"; }, + *generator() { yield "gen"; }, + async *asyncGenerator() { yield "async gen"; }, + + // String literal methods + 'quotedMethod'() { return "quoted"; }, + "doubleQuoted"() { return "double quoted"; }, + async 'asyncQuoted'() { return "async quoted"; }, + + // Computed methods + [computed]() { return "computed"; }, + async [computed]() { return "async computed"; }, + *[computed]() { yield "computed gen"; }, + + // Under this sections should go properties that can't be shorthanded + // Meaning they are valid with either explicit or shorthand property option + + // Call expressions + call: example(), + callLiteral: "example"(), + + // Computed properties + [dynamic()]: dynamicValue, + [computed]: computed, + ["computed-string"]: computedString, + + // Arrow functions + arrow: () => "arrow", + arrowWithBlock: () => { return "arrow block"; }, + asyncArrow: async () => "async arrow", + + // Accessors get getter() { return "getter"; }, - set setter(value) { this._setter = value; } + set setter(value) { this._setter = value; }, + + ...spread, }; diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validShorthand.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validShorthand.js.snap index 7a280c74a3b6..337428cd5a41 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validShorthand.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validShorthand.js.snap @@ -4,12 +4,50 @@ expression: validShorthand.js --- # Input ```js -const obj = { +const validShorthand = { + // Property shorthand foo, - bar() { return "bar"; }, - arrow: () => { "arrow" }, + bar, + baz, + + // Method shorthand + method() { return "method"; }, + async async() { return "async"; }, + *generator() { yield "gen"; }, + async *asyncGenerator() { yield "async gen"; }, + + // String literal methods + 'quotedMethod'() { return "quoted"; }, + "doubleQuoted"() { return "double quoted"; }, + async 'asyncQuoted'() { return "async quoted"; }, + + // Computed methods + [computed]() { return "computed"; }, + async [computed]() { return "async computed"; }, + *[computed]() { yield "computed gen"; }, + + // Under this sections should go properties that can't be shorthanded + // Meaning they are valid with either explicit or shorthand property option + + // Call expressions + call: example(), + callLiteral: "example"(), + + // Computed properties + [dynamic()]: dynamicValue, + [computed]: computed, + ["computed-string"]: computedString, + + // Arrow functions + arrow: () => "arrow", + arrowWithBlock: () => { return "arrow block"; }, + asyncArrow: async () => "async arrow", + + // Accessors get getter() { return "getter"; }, - set setter(value) { this._setter = value; } + set setter(value) { this._setter = value; }, + + ...spread, }; ``` diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index a25252b0337a..88a79a6ba0af 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -2925,8 +2925,10 @@ "additionalProperties": false }, "ObjectPropertySyntax": { - "type": "string", - "enum": ["explicit", "shorthand"] + "oneOf": [ + { "description": "{foo: foo}", "type": "string", "enum": ["explicit"] }, + { "description": "{foo}", "type": "string", "enum": ["shorthand"] } + ] }, "Options": { "type": "object", From e578c0bda05707afe55a37df10b105eea2edc89d Mon Sep 17 00:00:00 2001 From: dy0gu Date: Wed, 12 Feb 2025 14:33:39 +0000 Subject: [PATCH 06/10] refactor: improve rule code readability --- .../use_consistent_object_definition.rs | 83 +++++++++++-------- .../validExplicit.js | 10 +-- .../validExplicit.js.snap | 10 +-- .../validShorthand.js | 5 ++ .../validShorthand.js.snap | 5 ++ 5 files changed, 67 insertions(+), 46 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs b/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs index 55d379b288ab..39e132ba7ecf 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs @@ -102,56 +102,67 @@ impl Rule for UseConsistentObjectDefinition { let options = ctx.options(); match binding { AnyJsObjectMember::JsShorthandPropertyObjectMember(_) => match options.syntax { + // Shorthand properties should error when explicit is expected + ObjectPropertySyntax::Shorthand => None, + ObjectPropertySyntax::Explicit => Some(()), + }, + AnyJsObjectMember::JsMethodObjectMember(_) => match options.syntax { + // Shorthand methods should error when explicit is expected ObjectPropertySyntax::Shorthand => None, ObjectPropertySyntax::Explicit => Some(()), }, AnyJsObjectMember::JsPropertyObjectMember(source) => { - let member_token = source.name().ok()?; - let member_id = match member_token { - AnyJsObjectMemberName::JsLiteralMemberName(literal) => { - let literal_token = literal.value().ok()?; - inner_string_text(&literal_token) + let value_token = source.value().ok()?; + let value_id = match value_token { + AnyJsExpression::JsIdentifierExpression(identifier_token) => { + // If expression is an identifier, get ID to compare it against the property name later + let variable_token = identifier_token.name().ok()?.value_token().ok()?; + inner_string_text(&variable_token) } - AnyJsObjectMemberName::JsComputedMemberName(_computed) => { - let reference_token = source.value().ok()?; - match reference_token { - AnyJsExpression::JsFunctionExpression(function) => { - let function_token = function.function_token().ok()?; - inner_string_text(&function_token) - } - _ => return None, + AnyJsExpression::JsFunctionExpression(_function_token) => { + // Functions are always shorthandable + match options.syntax { + ObjectPropertySyntax::Shorthand => return Some(()), + ObjectPropertySyntax::Explicit => return None, } } _ => return None, }; - let reference_token = source.value().ok()?; - let reference_id = match reference_token { - AnyJsExpression::JsIdentifierExpression(identifier) => { - let variable_token = identifier.name().ok()?.value_token().ok()?; - inner_string_text(&variable_token) + let name_token = source.name().ok()?; + let _name_id = match name_token { + AnyJsObjectMemberName::JsLiteralMemberName(literal_token) => { + match options.syntax { + ObjectPropertySyntax::Shorthand => { + // Throw shorthand error if the value is the same as the property name + // We use `text_trimmed` to preserve quotes when comparing, we need this + // because {foo: foo} can be shorthanded, but {"foo": foo} cannot + if literal_token.value().ok()?.text_trimmed() == value_id.trim() { + return Some(()); + } else { + return None; + } + } + ObjectPropertySyntax::Explicit => { + return None; + } + } } - AnyJsExpression::JsFunctionExpression(function) => { - let function_token = function.function_token().ok()?; - inner_string_text(&function_token) + AnyJsObjectMemberName::JsComputedMemberName(_computed_token) => { + let reference_token = source.value().ok()?; + // Computed is always shorthandable if the value is a function, else never + match reference_token { + AnyJsExpression::JsFunctionExpression(_function_token) => { + match options.syntax { + ObjectPropertySyntax::Shorthand => return Some(()), + ObjectPropertySyntax::Explicit => return None, + } + } + _ => return None, + } } _ => return None, }; - - match options.syntax { - ObjectPropertySyntax::Shorthand => { - if member_id == reference_id || "function" == reference_id { - Some(()) - } else { - None - } - } - ObjectPropertySyntax::Explicit => None, - } } - AnyJsObjectMember::JsMethodObjectMember(_) => match options.syntax { - ObjectPropertySyntax::Shorthand => None, - ObjectPropertySyntax::Explicit => Some(()), - }, _ => None, } } diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validExplicit.js b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validExplicit.js index 5e5cac760297..839fc0ffa4d5 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validExplicit.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validExplicit.js @@ -10,11 +10,6 @@ const validExplicit = { generator: function* () { yield "gen"; }, asyncGenerator: async function* () { yield "async gen"; }, - // String literals - "stringLiteral": "stringLiteral", - "quotedProperty": quotedProperty, - 'singleQuoted': singleQuoted, - // Computed methods [computed]: function () { return "computed"; }, [computed]: async function () { return "async computed"; }, @@ -22,6 +17,11 @@ const validExplicit = { // Under this sections should go properties that can't be shorthanded // Meaning they are valid with either explicit or shorthand property option + // String literals + "stringLiteral": "stringLiteral", + "quotedProperty": quotedProperty, + 'singleQuoted': singleQuoted, + // Call expressions call: example(), callLiteral: "example"(), diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validExplicit.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validExplicit.js.snap index 06906839a21f..128e444a2200 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validExplicit.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validExplicit.js.snap @@ -16,11 +16,6 @@ const validExplicit = { generator: function* () { yield "gen"; }, asyncGenerator: async function* () { yield "async gen"; }, - // String literals - "stringLiteral": "stringLiteral", - "quotedProperty": quotedProperty, - 'singleQuoted': singleQuoted, - // Computed methods [computed]: function () { return "computed"; }, [computed]: async function () { return "async computed"; }, @@ -28,6 +23,11 @@ const validExplicit = { // Under this sections should go properties that can't be shorthanded // Meaning they are valid with either explicit or shorthand property option + // String literals + "stringLiteral": "stringLiteral", + "quotedProperty": quotedProperty, + 'singleQuoted': singleQuoted, + // Call expressions call: example(), callLiteral: "example"(), diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validShorthand.js b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validShorthand.js index 3a6ed088b570..6d97be010593 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validShorthand.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validShorthand.js @@ -23,6 +23,11 @@ const validShorthand = { // Under this sections should go properties that can't be shorthanded // Meaning they are valid with either explicit or shorthand property option + // String literals + "stringLiteral": "stringLiteral", + "quotedProperty": quotedProperty, + 'singleQuoted': singleQuoted, + // Call expressions call: example(), callLiteral: "example"(), diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validShorthand.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validShorthand.js.snap index 337428cd5a41..e30d0ca9b27c 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validShorthand.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentObjectDefinition/validShorthand.js.snap @@ -29,6 +29,11 @@ const validShorthand = { // Under this sections should go properties that can't be shorthanded // Meaning they are valid with either explicit or shorthand property option + // String literals + "stringLiteral": "stringLiteral", + "quotedProperty": quotedProperty, + 'singleQuoted': singleQuoted, + // Call expressions call: example(), callLiteral: "example"(), From 796326a4742072465eb8a76e80fa2bd6c514f925 Mon Sep 17 00:00:00 2001 From: dy0gu Date: Wed, 12 Feb 2025 15:20:08 +0000 Subject: [PATCH 07/10] docs: add example for each option --- .../use_consistent_object_definition.rs | 48 +++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs b/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs index 39e132ba7ecf..1ed5830cb37f 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs @@ -20,21 +20,31 @@ declare_lint_rule! { /// ## Example /// /// ### Invalid - /// ```js,expect_diagnostic + /// + /// ```json,options + /// { + /// "options": { + /// "syntax": "explicit" + /// } + /// } + /// ``` + /// + /// ```js,expect_diagnostic,use_options /// let foo = 1; /// let invalid = { /// foo /// }; /// ``` /// - /// ```js,expect_diagnostic + /// ```js,expect_diagnostic,use_options /// let invalid = { /// bar() { return "bar"; }, /// }; /// ``` /// /// ### Valid - /// ```js + /// + /// ```js,use_options /// let foo = 1; /// let valid = { /// foo: foo, @@ -42,6 +52,38 @@ declare_lint_rule! { /// }; /// ``` /// + /// ### Invalid + /// + /// ```json,options + /// { + /// "options": { + /// "syntax": "shorthand" + /// } + /// } + /// + /// ```js,expect_diagnostic,use_options + /// let foo = 1; + /// let invalid = { + /// foo: foo + /// }; + /// ``` + /// + /// ```js,expect_diagnostic,use_options + /// let invalid = { + /// bar: function() { return "bar"; }, + /// }; + /// ``` + /// + /// ### Valid + /// + /// ```js,use_options + /// let foo = 1; + /// let valid = { + /// foo, + /// bar() { return "bar"; }, + /// }; + /// + /// /// ## Options /// /// Use the options to specify the syntax of object literals to enforce. From d84c6c13601fbfd74e6bb17281ef2a6827726f80 Mon Sep 17 00:00:00 2001 From: dy0gu Date: Wed, 12 Feb 2025 15:42:26 +0000 Subject: [PATCH 08/10] docs: fix broken syntax --- .../src/lint/nursery/use_consistent_object_definition.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs b/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs index 1ed5830cb37f..8eddbbe92e10 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs @@ -56,10 +56,11 @@ declare_lint_rule! { /// /// ```json,options /// { - /// "options": { - /// "syntax": "shorthand" - /// } + /// "options": { + /// "syntax": "shorthand" + /// } /// } + /// ``` /// /// ```js,expect_diagnostic,use_options /// let foo = 1; @@ -82,7 +83,7 @@ declare_lint_rule! { /// foo, /// bar() { return "bar"; }, /// }; - /// + /// ``` /// /// ## Options /// From 2ee71f70854c97a142bc1e74edda7d1b1d65716e Mon Sep 17 00:00:00 2001 From: dy0gu Date: Tue, 25 Feb 2025 11:58:15 +0000 Subject: [PATCH 09/10] refactor: correct lint --- .../use_consistent_object_definition.rs | 29 +++++++++---------- .../@biomejs/backend-jsonrpc/src/workspace.ts | 3 ++ .../@biomejs/biome/configuration_schema.json | 9 ++++-- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs b/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs index 8eddbbe92e10..a22740d3c69a 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_consistent_object_definition.rs @@ -100,8 +100,8 @@ declare_lint_rule! { /// ### syntax /// /// The syntax to use: - /// - `explicit`: enforces the use of explicit object property syntax in every case - /// - `shorthand`: enforces the use of shorthand object property syntax when possible + /// - `explicit`: enforces the use of explicit object property syntax in every case. + /// - `shorthand`: enforces the use of shorthand object property syntax when possible. /// /// **Default:** `explicit` /// @@ -120,6 +120,7 @@ declare_lint_rule! { #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[serde(rename_all = "camelCase", deny_unknown_fields, default)] pub struct UseConsistentObjectDefinitionOptions { + /// The preferred syntax to enforce. syntax: ObjectPropertySyntax, } @@ -127,10 +128,10 @@ pub struct UseConsistentObjectDefinitionOptions { #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub enum ObjectPropertySyntax { - /// {foo: foo} + /// `{foo: foo}` #[default] Explicit, - /// {foo} + /// `{foo}` Shorthand, } @@ -172,7 +173,7 @@ impl Rule for UseConsistentObjectDefinition { _ => return None, }; let name_token = source.name().ok()?; - let _name_id = match name_token { + match name_token { AnyJsObjectMemberName::JsLiteralMemberName(literal_token) => { match options.syntax { ObjectPropertySyntax::Shorthand => { @@ -180,14 +181,12 @@ impl Rule for UseConsistentObjectDefinition { // We use `text_trimmed` to preserve quotes when comparing, we need this // because {foo: foo} can be shorthanded, but {"foo": foo} cannot if literal_token.value().ok()?.text_trimmed() == value_id.trim() { - return Some(()); + Some(()) } else { - return None; + None } } - ObjectPropertySyntax::Explicit => { - return None; - } + ObjectPropertySyntax::Explicit => None, } } AnyJsObjectMemberName::JsComputedMemberName(_computed_token) => { @@ -196,15 +195,15 @@ impl Rule for UseConsistentObjectDefinition { match reference_token { AnyJsExpression::JsFunctionExpression(_function_token) => { match options.syntax { - ObjectPropertySyntax::Shorthand => return Some(()), - ObjectPropertySyntax::Explicit => return None, + ObjectPropertySyntax::Shorthand => Some(()), + ObjectPropertySyntax::Explicit => None, } } - _ => return None, + _ => None, } } - _ => return None, - }; + _ => None, + } } _ => None, } diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index f384f155eba0..92fc24549ab4 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -2791,6 +2791,9 @@ export interface ConsistentMemberAccessibilityOptions { accessibility?: Accessibility; } export interface UseConsistentObjectDefinitionOptions { + /** + * The preferred syntax to enforce. + */ syntax?: ObjectPropertySyntax; } export interface UtilityClassSortingOptions { diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index 88a79a6ba0af..59453864a3c4 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -2926,8 +2926,12 @@ }, "ObjectPropertySyntax": { "oneOf": [ - { "description": "{foo: foo}", "type": "string", "enum": ["explicit"] }, - { "description": "{foo}", "type": "string", "enum": ["shorthand"] } + { + "description": "`{foo: foo}`", + "type": "string", + "enum": ["explicit"] + }, + { "description": "`{foo}`", "type": "string", "enum": ["shorthand"] } ] }, "Options": { @@ -4903,6 +4907,7 @@ "type": "object", "properties": { "syntax": { + "description": "The preferred syntax to enforce.", "default": "explicit", "allOf": [{ "$ref": "#/definitions/ObjectPropertySyntax" }] } From 65ee913d7adcf6f734391353c23e6c2194a9574b Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Tue, 25 Feb 2025 15:29:28 +0000 Subject: [PATCH 10/10] conflicts, rebase and changeset --- .changeset/famous-ravens-cross.md | 30 ++++++- .../src/analyzer/linter/rules.rs | 80 ++++++++++++------- crates/biome_js_analyze/src/lint/nursery.rs | 3 +- .../@biomejs/biome/configuration_schema.json | 53 ++++++++++++ 4 files changed, 134 insertions(+), 32 deletions(-) diff --git a/.changeset/famous-ravens-cross.md b/.changeset/famous-ravens-cross.md index 727d061f4c39..25c2f090f795 100644 --- a/.changeset/famous-ravens-cross.md +++ b/.changeset/famous-ravens-cross.md @@ -2,4 +2,32 @@ "@biomejs/biome": minor --- -Add the useConsistentObjectDefinition rule. +Add the new JavaScript rule `useConsistentObjectDefinition` rule. The rule enforces a consistent style for the definition of objects: + +By the default, the rule enforces a shorthand style: + +```js +const validShorthand = { + // Property shorthand + foo, + + // Method shorthand + method() { + return "method"; + }, +} +``` + +Alternatively, the rule can be configured to enforce an explicit style: + +```js +const invalidExplicit = { + // Basic property shorthand violations + foo: foo, + + // Method shorthand violations + method: function () { + return "method"; + }, +} +``` diff --git a/crates/biome_configuration/src/analyzer/linter/rules.rs b/crates/biome_configuration/src/analyzer/linter/rules.rs index 5b245e3024f1..ea86a9c8b2a8 100644 --- a/crates/biome_configuration/src/analyzer/linter/rules.rs +++ b/crates/biome_configuration/src/analyzer/linter/rules.rs @@ -3207,6 +3207,10 @@ pub struct Nursery { #[serde(skip_serializing_if = "Option::is_none")] pub use_consistent_member_accessibility: Option>, + #[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>, #[doc = "Require specifying the reason argument when using @deprecated directive"] #[serde(skip_serializing_if = "Option::is_none")] pub use_deprecated_reason: @@ -3310,6 +3314,7 @@ impl Nursery { "useComponentExportOnlyModules", "useConsistentCurlyBraces", "useConsistentMemberAccessibility", + "useConsistentObjectDefinition", "useDeprecatedReason", "useExplicitType", "useExportsLast", @@ -3339,10 +3344,10 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[40]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[51]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[57]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[59]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[61]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[52]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[58]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[60]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[62]), ]; const ALL_RULES_AS_FILTERS: &'static [RuleFilter<'static>] = &[ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[0]), @@ -3409,6 +3414,7 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[61]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[62]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[63]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[64]), ]; } impl RuleGroupExt for Nursery { @@ -3675,71 +3681,76 @@ impl RuleGroupExt for Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50])); } } - if let Some(rule) = self.use_deprecated_reason.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[51])); } } - if let Some(rule) = self.use_explicit_type.as_ref() { + if let Some(rule) = self.use_deprecated_reason.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[52])); } } - 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[53])); } } - if let Some(rule) = self.use_google_font_display.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[54])); } } - if let Some(rule) = self.use_google_font_preconnect.as_ref() { + if let Some(rule) = self.use_google_font_display.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[55])); } } - if let Some(rule) = self.use_guard_for_in.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[56])); } } - if let Some(rule) = self.use_named_operation.as_ref() { + if let Some(rule) = self.use_guard_for_in.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[57])); } } - 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[58])); } } - if let Some(rule) = self.use_parse_int_radix.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[59])); } } - if let Some(rule) = self.use_sorted_classes.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[60])); } } - if let Some(rule) = self.use_strict_mode.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[61])); } } - if let Some(rule) = self.use_trim_start_end.as_ref() { + if let Some(rule) = self.use_strict_mode.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[62])); } } - if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if let Some(rule) = self.use_trim_start_end.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[63])); } } + if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[64])); + } + } index_set } fn get_disabled_rules(&self) -> FxHashSet> { @@ -3999,71 +4010,76 @@ impl RuleGroupExt for Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50])); } } - if let Some(rule) = self.use_deprecated_reason.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[51])); } } - if let Some(rule) = self.use_explicit_type.as_ref() { + if let Some(rule) = self.use_deprecated_reason.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[52])); } } - 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[53])); } } - if let Some(rule) = self.use_google_font_display.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[54])); } } - if let Some(rule) = self.use_google_font_preconnect.as_ref() { + if let Some(rule) = self.use_google_font_display.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[55])); } } - if let Some(rule) = self.use_guard_for_in.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[56])); } } - if let Some(rule) = self.use_named_operation.as_ref() { + if let Some(rule) = self.use_guard_for_in.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[57])); } } - 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[58])); } } - if let Some(rule) = self.use_parse_int_radix.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[59])); } } - if let Some(rule) = self.use_sorted_classes.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[60])); } } - if let Some(rule) = self.use_strict_mode.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[61])); } } - if let Some(rule) = self.use_trim_start_end.as_ref() { + if let Some(rule) = self.use_strict_mode.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[62])); } } - if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if let Some(rule) = self.use_trim_start_end.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[63])); } } + if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[64])); + } + } index_set } #[doc = r" Checks if, given a rule name, matches one of the rules contained in this category"] @@ -4298,6 +4314,10 @@ impl RuleGroupExt for Nursery { .use_consistent_member_accessibility .as_ref() .map(|conf| (conf.level(), conf.get_options())), + "useConsistentObjectDefinition" => self + .use_consistent_object_definition + .as_ref() + .map(|conf| (conf.level(), conf.get_options())), "useDeprecatedReason" => self .use_deprecated_reason .as_ref() diff --git a/crates/biome_js_analyze/src/lint/nursery.rs b/crates/biome_js_analyze/src/lint/nursery.rs index dad10bd25812..1bf09ac09222 100644 --- a/crates/biome_js_analyze/src/lint/nursery.rs +++ b/crates/biome_js_analyze/src/lint/nursery.rs @@ -44,6 +44,7 @@ pub mod use_collapsed_if; pub mod use_component_export_only_modules; pub mod use_consistent_curly_braces; pub mod use_consistent_member_accessibility; +pub mod use_consistent_object_definition; pub mod use_explicit_type; pub mod use_exports_last; pub mod use_google_font_display; @@ -54,4 +55,4 @@ pub mod use_sorted_classes; pub mod use_strict_mode; pub mod use_trim_start_end; pub mod use_valid_autocomplete; -declare_lint_group! { pub Nursery { name : "nursery" , rules : [self :: no_await_in_loop :: NoAwaitInLoop , self :: no_common_js :: NoCommonJs , self :: no_constant_binary_expression :: NoConstantBinaryExpression , self :: no_destructured_props :: NoDestructuredProps , self :: no_document_cookie :: NoDocumentCookie , self :: no_document_import_in_page :: NoDocumentImportInPage , self :: no_duplicate_else_if :: NoDuplicateElseIf , self :: no_dynamic_namespace_import_access :: NoDynamicNamespaceImportAccess , self :: no_enum :: NoEnum , self :: no_exported_imports :: NoExportedImports , self :: no_floating_promises :: NoFloatingPromises , self :: no_global_dirname_filename :: NoGlobalDirnameFilename , self :: no_head_element :: NoHeadElement , self :: no_head_import_in_document :: NoHeadImportInDocument , self :: no_img_element :: NoImgElement , self :: no_import_cycles :: NoImportCycles , self :: no_irregular_whitespace :: NoIrregularWhitespace , self :: no_nested_ternary :: NoNestedTernary , self :: no_noninteractive_element_interactions :: NoNoninteractiveElementInteractions , self :: no_octal_escape :: NoOctalEscape , self :: no_package_private_imports :: NoPackagePrivateImports , self :: no_process_env :: NoProcessEnv , self :: no_process_global :: NoProcessGlobal , self :: no_restricted_imports :: NoRestrictedImports , self :: no_restricted_types :: NoRestrictedTypes , self :: no_secrets :: NoSecrets , self :: no_static_element_interactions :: NoStaticElementInteractions , self :: no_substr :: NoSubstr , self :: no_template_curly_in_string :: NoTemplateCurlyInString , self :: no_ts_ignore :: NoTsIgnore , self :: no_unwanted_polyfillio :: NoUnwantedPolyfillio , self :: no_useless_escape_in_regex :: NoUselessEscapeInRegex , self :: no_useless_string_raw :: NoUselessStringRaw , self :: no_useless_undefined :: NoUselessUndefined , self :: use_adjacent_overload_signatures :: UseAdjacentOverloadSignatures , self :: use_aria_props_supported_by_role :: UseAriaPropsSupportedByRole , self :: use_at_index :: UseAtIndex , self :: use_collapsed_if :: UseCollapsedIf , self :: use_component_export_only_modules :: UseComponentExportOnlyModules , self :: use_consistent_curly_braces :: UseConsistentCurlyBraces , self :: use_consistent_member_accessibility :: UseConsistentMemberAccessibility , self :: use_explicit_type :: UseExplicitType , self :: use_exports_last :: UseExportsLast , self :: use_google_font_display :: UseGoogleFontDisplay , self :: use_google_font_preconnect :: UseGoogleFontPreconnect , self :: use_guard_for_in :: UseGuardForIn , self :: use_parse_int_radix :: UseParseIntRadix , self :: use_sorted_classes :: UseSortedClasses , self :: use_strict_mode :: UseStrictMode , self :: use_trim_start_end :: UseTrimStartEnd , self :: use_valid_autocomplete :: UseValidAutocomplete ,] } } +declare_lint_group! { pub Nursery { name : "nursery" , rules : [self :: no_await_in_loop :: NoAwaitInLoop , self :: no_common_js :: NoCommonJs , self :: no_constant_binary_expression :: NoConstantBinaryExpression , self :: no_destructured_props :: NoDestructuredProps , self :: no_document_cookie :: NoDocumentCookie , self :: no_document_import_in_page :: NoDocumentImportInPage , self :: no_duplicate_else_if :: NoDuplicateElseIf , self :: no_dynamic_namespace_import_access :: NoDynamicNamespaceImportAccess , self :: no_enum :: NoEnum , self :: no_exported_imports :: NoExportedImports , self :: no_floating_promises :: NoFloatingPromises , self :: no_global_dirname_filename :: NoGlobalDirnameFilename , self :: no_head_element :: NoHeadElement , self :: no_head_import_in_document :: NoHeadImportInDocument , self :: no_img_element :: NoImgElement , self :: no_import_cycles :: NoImportCycles , self :: no_irregular_whitespace :: NoIrregularWhitespace , self :: no_nested_ternary :: NoNestedTernary , self :: no_noninteractive_element_interactions :: NoNoninteractiveElementInteractions , self :: no_octal_escape :: NoOctalEscape , self :: no_package_private_imports :: NoPackagePrivateImports , self :: no_process_env :: NoProcessEnv , self :: no_process_global :: NoProcessGlobal , self :: no_restricted_imports :: NoRestrictedImports , self :: no_restricted_types :: NoRestrictedTypes , self :: no_secrets :: NoSecrets , self :: no_static_element_interactions :: NoStaticElementInteractions , self :: no_substr :: NoSubstr , self :: no_template_curly_in_string :: NoTemplateCurlyInString , self :: no_ts_ignore :: NoTsIgnore , self :: no_unwanted_polyfillio :: NoUnwantedPolyfillio , self :: no_useless_escape_in_regex :: NoUselessEscapeInRegex , self :: no_useless_string_raw :: NoUselessStringRaw , self :: no_useless_undefined :: NoUselessUndefined , self :: use_adjacent_overload_signatures :: UseAdjacentOverloadSignatures , self :: use_aria_props_supported_by_role :: UseAriaPropsSupportedByRole , self :: use_at_index :: UseAtIndex , self :: use_collapsed_if :: UseCollapsedIf , self :: use_component_export_only_modules :: UseComponentExportOnlyModules , self :: use_consistent_curly_braces :: UseConsistentCurlyBraces , self :: use_consistent_member_accessibility :: UseConsistentMemberAccessibility , self :: use_consistent_object_definition :: UseConsistentObjectDefinition , self :: use_explicit_type :: UseExplicitType , self :: use_exports_last :: UseExportsLast , self :: use_google_font_display :: UseGoogleFontDisplay , self :: use_google_font_preconnect :: UseGoogleFontPreconnect , self :: use_guard_for_in :: UseGuardForIn , self :: use_parse_int_radix :: UseParseIntRadix , self :: use_sorted_classes :: UseSortedClasses , self :: use_strict_mode :: UseStrictMode , self :: use_trim_start_end :: UseTrimStartEnd , self :: use_valid_autocomplete :: UseValidAutocomplete ,] } } diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index 55d84270eecd..c2c99ea70096 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -2817,6 +2817,15 @@ { "type": "null" } ] }, + "useConsistentObjectDefinition": { + "description": "Require the consistent declaration of object literals. Defaults to explicit definitions.", + "anyOf": [ + { + "$ref": "#/definitions/UseConsistentObjectDefinitionConfiguration" + }, + { "type": "null" } + ] + }, "useDeprecatedReason": { "description": "Require specifying the reason argument when using @deprecated directive", "anyOf": [ @@ -2911,6 +2920,16 @@ }, "additionalProperties": false }, + "ObjectPropertySyntax": { + "oneOf": [ + { + "description": "`{foo: foo}`", + "type": "string", + "enum": ["explicit"] + }, + { "description": "`{foo}`", "type": "string", "enum": ["shorthand"] } + ] + }, "ObjectWrap": { "type": "string", "enum": ["preserve", "collapse"] }, "Options": { "type": "object", @@ -3619,6 +3638,23 @@ }, "additionalProperties": false }, + "RuleWithUseConsistentObjectDefinitionOptions": { + "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/UseConsistentObjectDefinitionOptions" } + ] + } + }, + "additionalProperties": false + }, "RuleWithUseExhaustiveDependenciesOptions": { "type": "object", "required": ["level"], @@ -4858,6 +4894,23 @@ }, "additionalProperties": false }, + "UseConsistentObjectDefinitionConfiguration": { + "anyOf": [ + { "$ref": "#/definitions/RulePlainConfiguration" }, + { "$ref": "#/definitions/RuleWithUseConsistentObjectDefinitionOptions" } + ] + }, + "UseConsistentObjectDefinitionOptions": { + "type": "object", + "properties": { + "syntax": { + "description": "The preferred syntax to enforce.", + "default": "explicit", + "allOf": [{ "$ref": "#/definitions/ObjectPropertySyntax" }] + } + }, + "additionalProperties": false + }, "UseExhaustiveDependenciesConfiguration": { "anyOf": [ { "$ref": "#/definitions/RulePlainConfiguration" },