diff --git a/.changeset/add-no-v-options-api.md b/.changeset/add-no-v-options-api.md new file mode 100644 index 000000000000..b821ad6d956f --- /dev/null +++ b/.changeset/add-no-v-options-api.md @@ -0,0 +1,21 @@ +--- +"@biomejs/biome": patch +--- + +Added a new nursery rule [`noVueOptionsApi`](https://biomejs.dev/linter/rules/no-vue-options-api/). + +Biome now reports Vue Options API usage, which is incompatible with Vue 3.6's Vapor Mode. +This rule detects Options API patterns in ` +``` diff --git a/crates/biome_configuration/src/analyzer/linter/rules.rs b/crates/biome_configuration/src/analyzer/linter/rules.rs index 7755fc0c3289..945f2b69d902 100644 --- a/crates/biome_configuration/src/analyzer/linter/rules.rs +++ b/crates/biome_configuration/src/analyzer/linter/rules.rs @@ -355,6 +355,7 @@ pub enum RuleName { NoVoidTypeReturn, NoVueDataObjectDeclaration, NoVueDuplicateKeys, + NoVueOptionsApi, NoVueReservedKeys, NoVueReservedProps, NoVueSetupPropsReactivityLoss, @@ -785,6 +786,7 @@ impl RuleName { Self::NoVoidTypeReturn => "noVoidTypeReturn", Self::NoVueDataObjectDeclaration => "noVueDataObjectDeclaration", Self::NoVueDuplicateKeys => "noVueDuplicateKeys", + Self::NoVueOptionsApi => "noVueOptionsApi", Self::NoVueReservedKeys => "noVueReservedKeys", Self::NoVueReservedProps => "noVueReservedProps", Self::NoVueSetupPropsReactivityLoss => "noVueSetupPropsReactivityLoss", @@ -1211,6 +1213,7 @@ impl RuleName { Self::NoVoidTypeReturn => RuleGroup::Correctness, Self::NoVueDataObjectDeclaration => RuleGroup::Nursery, Self::NoVueDuplicateKeys => RuleGroup::Nursery, + Self::NoVueOptionsApi => RuleGroup::Nursery, Self::NoVueReservedKeys => RuleGroup::Nursery, Self::NoVueReservedProps => RuleGroup::Nursery, Self::NoVueSetupPropsReactivityLoss => RuleGroup::Nursery, @@ -1646,6 +1649,7 @@ impl std::str::FromStr for RuleName { "noVoidTypeReturn" => Ok(Self::NoVoidTypeReturn), "noVueDataObjectDeclaration" => Ok(Self::NoVueDataObjectDeclaration), "noVueDuplicateKeys" => Ok(Self::NoVueDuplicateKeys), + "noVueOptionsApi" => Ok(Self::NoVueOptionsApi), "noVueReservedKeys" => Ok(Self::NoVueReservedKeys), "noVueReservedProps" => Ok(Self::NoVueReservedProps), "noVueSetupPropsReactivityLoss" => Ok(Self::NoVueSetupPropsReactivityLoss), diff --git a/crates/biome_configuration/src/generated/domain_selector.rs b/crates/biome_configuration/src/generated/domain_selector.rs index 03fd957d578a..e3d23ac44550 100644 --- a/crates/biome_configuration/src/generated/domain_selector.rs +++ b/crates/biome_configuration/src/generated/domain_selector.rs @@ -93,6 +93,7 @@ static VUE_FILTERS: LazyLock>> = LazyLock::new(|| { vec![ RuleFilter::Rule("nursery", "noVueDataObjectDeclaration"), RuleFilter::Rule("nursery", "noVueDuplicateKeys"), + RuleFilter::Rule("nursery", "noVueOptionsApi"), RuleFilter::Rule("nursery", "noVueReservedKeys"), RuleFilter::Rule("nursery", "noVueReservedProps"), RuleFilter::Rule("nursery", "noVueSetupPropsReactivityLoss"), diff --git a/crates/biome_diagnostics_categories/src/categories.rs b/crates/biome_diagnostics_categories/src/categories.rs index b745c7784b85..0f2c57e72a99 100644 --- a/crates/biome_diagnostics_categories/src/categories.rs +++ b/crates/biome_diagnostics_categories/src/categories.rs @@ -207,6 +207,7 @@ define_categories! { "lint/nursery/noUselessUndefined": "https://biomejs.dev/linter/rules/no-useless-undefined", "lint/nursery/noVueDataObjectDeclaration": "https://biomejs.dev/linter/rules/no-vue-data-object-declaration", "lint/nursery/noVueDuplicateKeys": "https://biomejs.dev/linter/rules/no-vue-duplicate-keys", + "lint/nursery/noVueOptionsApi": "https://biomejs.dev/linter/rules/no-vue-options-api", "lint/nursery/noVueReservedKeys": "https://biomejs.dev/linter/rules/no-vue-reserved-keys", "lint/nursery/noVueReservedProps": "https://biomejs.dev/linter/rules/no-vue-reserved-props", "lint/nursery/noVueSetupPropsReactivityLoss": "https://biomejs.dev/linter/rules/no-vue-setup-props-reactivity-loss", diff --git a/crates/biome_html_analyze/src/lint/nursery/use_vue_vapor.rs b/crates/biome_html_analyze/src/lint/nursery/use_vue_vapor.rs index 437914536fdf..692036fdf691 100644 --- a/crates/biome_html_analyze/src/lint/nursery/use_vue_vapor.rs +++ b/crates/biome_html_analyze/src/lint/nursery/use_vue_vapor.rs @@ -35,6 +35,10 @@ declare_lint_rule! { /// /// ``` /// + /// ## Related Rules + /// + /// - [noVueOptionsApi](https://biomejs.dev/linter/rules/no-vue-options-api): Disallows the Options API format, which is incompatible with Vapor Mode + /// pub UseVueVapor { version: "2.3.11", name: "useVueVapor", diff --git a/crates/biome_js_analyze/src/lint/nursery.rs b/crates/biome_js_analyze/src/lint/nursery.rs index 4cd53bca6438..d74a6ffe51b6 100644 --- a/crates/biome_js_analyze/src/lint/nursery.rs +++ b/crates/biome_js_analyze/src/lint/nursery.rs @@ -40,6 +40,7 @@ pub mod no_useless_catch_binding; pub mod no_useless_undefined; pub mod no_vue_data_object_declaration; pub mod no_vue_duplicate_keys; +pub mod no_vue_options_api; pub mod no_vue_reserved_keys; pub mod no_vue_reserved_props; pub mod no_vue_setup_props_reactivity_loss; @@ -60,4 +61,4 @@ pub mod use_spread; pub mod use_vue_consistent_define_props_declaration; pub mod use_vue_define_macros_order; pub mod use_vue_multi_word_component_names; -declare_lint_group! { pub Nursery { name : "nursery" , rules : [self :: no_ambiguous_anchor_text :: NoAmbiguousAnchorText , self :: no_before_interactive_script_outside_document :: NoBeforeInteractiveScriptOutsideDocument , self :: no_continue :: NoContinue , self :: no_deprecated_imports :: NoDeprecatedImports , self :: no_duplicate_enum_values :: NoDuplicateEnumValues , self :: no_duplicated_spread_props :: NoDuplicatedSpreadProps , self :: no_empty_source :: NoEmptySource , self :: no_equals_to_null :: NoEqualsToNull , self :: no_excessive_lines_per_file :: NoExcessiveLinesPerFile , self :: no_floating_promises :: NoFloatingPromises , self :: no_for_in :: NoForIn , self :: no_import_cycles :: NoImportCycles , self :: no_increment_decrement :: NoIncrementDecrement , self :: no_jsx_literals :: NoJsxLiterals , self :: no_jsx_props_bind :: NoJsxPropsBind , self :: no_leaked_render :: NoLeakedRender , self :: no_misused_promises :: NoMisusedPromises , self :: no_multi_assign :: NoMultiAssign , self :: no_multi_str :: NoMultiStr , self :: no_next_async_client_component :: NoNextAsyncClientComponent , self :: no_parameters_only_used_in_recursion :: NoParametersOnlyUsedInRecursion , self :: no_proto :: NoProto , self :: no_react_forward_ref :: NoReactForwardRef , self :: no_return_assign :: NoReturnAssign , self :: no_script_url :: NoScriptUrl , self :: no_shadow :: NoShadow , self :: no_sync_scripts :: NoSyncScripts , self :: no_ternary :: NoTernary , self :: no_undeclared_env_vars :: NoUndeclaredEnvVars , self :: no_unknown_attribute :: NoUnknownAttribute , self :: no_unnecessary_conditions :: NoUnnecessaryConditions , self :: no_unresolved_imports :: NoUnresolvedImports , self :: no_unused_expressions :: NoUnusedExpressions , self :: no_useless_catch_binding :: NoUselessCatchBinding , self :: no_useless_undefined :: NoUselessUndefined , self :: no_vue_data_object_declaration :: NoVueDataObjectDeclaration , self :: no_vue_duplicate_keys :: NoVueDuplicateKeys , self :: no_vue_reserved_keys :: NoVueReservedKeys , self :: no_vue_reserved_props :: NoVueReservedProps , self :: no_vue_setup_props_reactivity_loss :: NoVueSetupPropsReactivityLoss , self :: use_array_sort_compare :: UseArraySortCompare , self :: use_await_thenable :: UseAwaitThenable , self :: use_consistent_arrow_return :: UseConsistentArrowReturn , self :: use_destructuring :: UseDestructuring , self :: use_error_cause :: UseErrorCause , self :: use_exhaustive_switch_cases :: UseExhaustiveSwitchCases , self :: use_explicit_type :: UseExplicitType , self :: use_find :: UseFind , self :: use_max_params :: UseMaxParams , self :: use_qwik_method_usage :: UseQwikMethodUsage , self :: use_qwik_valid_lexical_scope :: UseQwikValidLexicalScope , self :: use_regexp_exec :: UseRegexpExec , self :: use_sorted_classes :: UseSortedClasses , self :: use_spread :: UseSpread , self :: use_vue_consistent_define_props_declaration :: UseVueConsistentDefinePropsDeclaration , self :: use_vue_define_macros_order :: UseVueDefineMacrosOrder , self :: use_vue_multi_word_component_names :: UseVueMultiWordComponentNames ,] } } +declare_lint_group! { pub Nursery { name : "nursery" , rules : [self :: no_ambiguous_anchor_text :: NoAmbiguousAnchorText , self :: no_before_interactive_script_outside_document :: NoBeforeInteractiveScriptOutsideDocument , self :: no_continue :: NoContinue , self :: no_deprecated_imports :: NoDeprecatedImports , self :: no_duplicate_enum_values :: NoDuplicateEnumValues , self :: no_duplicated_spread_props :: NoDuplicatedSpreadProps , self :: no_empty_source :: NoEmptySource , self :: no_equals_to_null :: NoEqualsToNull , self :: no_excessive_lines_per_file :: NoExcessiveLinesPerFile , self :: no_floating_promises :: NoFloatingPromises , self :: no_for_in :: NoForIn , self :: no_import_cycles :: NoImportCycles , self :: no_increment_decrement :: NoIncrementDecrement , self :: no_jsx_literals :: NoJsxLiterals , self :: no_jsx_props_bind :: NoJsxPropsBind , self :: no_leaked_render :: NoLeakedRender , self :: no_misused_promises :: NoMisusedPromises , self :: no_multi_assign :: NoMultiAssign , self :: no_multi_str :: NoMultiStr , self :: no_next_async_client_component :: NoNextAsyncClientComponent , self :: no_parameters_only_used_in_recursion :: NoParametersOnlyUsedInRecursion , self :: no_proto :: NoProto , self :: no_react_forward_ref :: NoReactForwardRef , self :: no_return_assign :: NoReturnAssign , self :: no_script_url :: NoScriptUrl , self :: no_shadow :: NoShadow , self :: no_sync_scripts :: NoSyncScripts , self :: no_ternary :: NoTernary , self :: no_undeclared_env_vars :: NoUndeclaredEnvVars , self :: no_unknown_attribute :: NoUnknownAttribute , self :: no_unnecessary_conditions :: NoUnnecessaryConditions , self :: no_unresolved_imports :: NoUnresolvedImports , self :: no_unused_expressions :: NoUnusedExpressions , self :: no_useless_catch_binding :: NoUselessCatchBinding , self :: no_useless_undefined :: NoUselessUndefined , self :: no_vue_data_object_declaration :: NoVueDataObjectDeclaration , self :: no_vue_duplicate_keys :: NoVueDuplicateKeys , self :: no_vue_options_api :: NoVueOptionsApi , self :: no_vue_reserved_keys :: NoVueReservedKeys , self :: no_vue_reserved_props :: NoVueReservedProps , self :: no_vue_setup_props_reactivity_loss :: NoVueSetupPropsReactivityLoss , self :: use_array_sort_compare :: UseArraySortCompare , self :: use_await_thenable :: UseAwaitThenable , self :: use_consistent_arrow_return :: UseConsistentArrowReturn , self :: use_destructuring :: UseDestructuring , self :: use_error_cause :: UseErrorCause , self :: use_exhaustive_switch_cases :: UseExhaustiveSwitchCases , self :: use_explicit_type :: UseExplicitType , self :: use_find :: UseFind , self :: use_max_params :: UseMaxParams , self :: use_qwik_method_usage :: UseQwikMethodUsage , self :: use_qwik_valid_lexical_scope :: UseQwikValidLexicalScope , self :: use_regexp_exec :: UseRegexpExec , self :: use_sorted_classes :: UseSortedClasses , self :: use_spread :: UseSpread , self :: use_vue_consistent_define_props_declaration :: UseVueConsistentDefinePropsDeclaration , self :: use_vue_define_macros_order :: UseVueDefineMacrosOrder , self :: use_vue_multi_word_component_names :: UseVueMultiWordComponentNames ,] } } diff --git a/crates/biome_js_analyze/src/lint/nursery/no_vue_options_api.rs b/crates/biome_js_analyze/src/lint/nursery/no_vue_options_api.rs new file mode 100644 index 000000000000..ed70d6532a48 --- /dev/null +++ b/crates/biome_js_analyze/src/lint/nursery/no_vue_options_api.rs @@ -0,0 +1,216 @@ +use biome_analyze::{Rule, RuleDiagnostic, RuleDomain, context::RuleContext, declare_lint_rule}; +use biome_console::markup; +use biome_diagnostics::Severity; +use biome_js_syntax::{AnyJsExpression, JsFileSource}; +use biome_rowan::{AstNode, TextRange}; +use biome_rule_options::no_vue_options_api::NoVueOptionsApiOptions; + +use crate::frameworks::vue::vue_call::is_vue_api_reference; +use crate::frameworks::vue::vue_component::{ + AnyVueComponent, VueComponent, VueComponentQuery, VueOptionsApiBasedComponent, +}; + +declare_lint_rule! { + /// Disallow the use of Vue Options API. + /// + /// Vue 3.6's Vapor Mode does not support the Options API. + /// Components must use the Composition API (` + /// ``` + /// + /// ```vue,expect_diagnostic + /// + /// ``` + /// + /// ```vue,expect_diagnostic + /// + /// ``` + /// + /// ```vue,expect_diagnostic + /// + /// ``` + /// + /// ```js,expect_diagnostic + /// import { defineComponent } from 'vue' + /// + /// defineComponent({ + /// name: 'MyComponent', + /// data() { + /// return { count: 0 } + /// } + /// }) + /// ``` + /// + /// ### Valid + /// + /// ```vue + /// + /// ``` + /// + /// ```vue + /// + /// ``` + /// + /// ```vue + /// + /// ``` + /// + /// ## Related Rules + /// + /// - [useVueVapor](https://biomejs.dev/linter/rules/use-vue-vapor): Enforces the use of Vapor mode in Vue components + /// + /// ## Resources + /// + /// - [Vue 3 Composition API](https://vuejs.org/api/composition-api-setup.html) + /// - [Options API vs Composition API](https://vuejs.org/guide/introduction.html#api-styles) + /// + pub NoVueOptionsApi { + version: "next", + name: "noVueOptionsApi", + language: "js", + recommended: false, + severity: Severity::Error, + domains: &[RuleDomain::Vue], + } +} + +/// State for detected Options API component. +pub struct RuleState { + range: TextRange, +} + +impl Rule for NoVueOptionsApi { + type Query = VueComponentQuery; + type State = RuleState; + type Signals = Option; + type Options = NoVueOptionsApiOptions; + + fn run(ctx: &RuleContext) -> Self::Signals { + let component = VueComponent::from_potential_component( + ctx.query(), + ctx.model(), + ctx.source_type::(), + ctx.file_path(), + )?; + + // diff --git a/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-createapp-data.vue.snap b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-createapp-data.vue.snap new file mode 100644 index 000000000000..ec3885cd3bc4 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-createapp-data.vue.snap @@ -0,0 +1,43 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: invalid-createapp-data.vue +--- +# Input +```ts +// should generate diagnostics +// createApp with Options API is not supported in Vapor Mode +import { createApp } from "vue"; + +createApp({ + data() { + return { count: 0 }; + }, +}).mount("#app"); + +``` + +# Diagnostics +``` +invalid-createapp-data.vue:5:11 lint/nursery/noVueOptionsApi ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Using the Options API is not allowed. + + 3 │ import { createApp } from "vue"; + 4 │ + > 5 │ createApp({ + │ ^ + > 6 │ data() { + > 7 │ return { count: 0 }; + > 8 │ }, + > 9 │ }).mount("#app"); + │ ^ + 10 │ + + i Although the Options API is still supported by Vue, using the Composition API is recommended, and makes it possible to use Vue's Vapor mode for better performance. + + i Use diff --git a/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-definecomponent-data.vue.snap b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-definecomponent-data.vue.snap new file mode 100644 index 000000000000..d0954ab9eac3 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-definecomponent-data.vue.snap @@ -0,0 +1,43 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: invalid-definecomponent-data.vue +--- +# Input +```ts +// should generate diagnostics +// defineComponent with Options API is not supported in Vapor Mode +import { defineComponent } from "vue"; + +export default defineComponent({ + data() { + return { count: 0 }; + }, +}); + +``` + +# Diagnostics +``` +invalid-definecomponent-data.vue:5:32 lint/nursery/noVueOptionsApi ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Using the Options API is not allowed. + + 3 │ import { defineComponent } from "vue"; + 4 │ + > 5 │ export default defineComponent({ + │ ^ + > 6 │ data() { + > 7 │ return { count: 0 }; + > 8 │ }, + > 9 │ }); + │ ^ + 10 │ + + i Although the Options API is still supported by Vue, using the Composition API is recommended, and makes it possible to use Vue's Vapor mode for better performance. + + i Use diff --git a/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-export-default-computed.vue.snap b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-export-default-computed.vue.snap new file mode 100644 index 000000000000..b5cfed753e5b --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-export-default-computed.vue.snap @@ -0,0 +1,45 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: invalid-export-default-computed.vue +--- +# Input +```ts +// should generate diagnostics +// Options API: computed is not supported in Vapor Mode +export default { + computed: { + doubleCount() { + return this.count * 2; + }, + }, +}; + +``` + +# Diagnostics +``` +invalid-export-default-computed.vue:3:16 lint/nursery/noVueOptionsApi ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Using the Options API is not allowed. + + 1 │ // should generate diagnostics + 2 │ // Options API: computed is not supported in Vapor Mode + > 3 │ export default { + │ ^ + > 4 │ computed: { + > 5 │ doubleCount() { + > 6 │ return this.count * 2; + > 7 │ }, + > 8 │ }, + > 9 │ }; + │ ^ + 10 │ + + i Although the Options API is still supported by Vue, using the Composition API is recommended, and makes it possible to use Vue's Vapor mode for better performance. + + i Use diff --git a/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-export-default-data.vue.snap b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-export-default-data.vue.snap new file mode 100644 index 000000000000..e61098bd6cfb --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-export-default-data.vue.snap @@ -0,0 +1,41 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: invalid-export-default-data.vue +--- +# Input +```ts +// should generate diagnostics +// Options API: data() is not supported in Vapor Mode +export default { + data() { + return { count: 0 }; + }, +}; + +``` + +# Diagnostics +``` +invalid-export-default-data.vue:3:16 lint/nursery/noVueOptionsApi ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Using the Options API is not allowed. + + 1 │ // should generate diagnostics + 2 │ // Options API: data() is not supported in Vapor Mode + > 3 │ export default { + │ ^ + > 4 │ data() { + > 5 │ return { count: 0 }; + > 6 │ }, + > 7 │ }; + │ ^ + 8 │ + + i Although the Options API is still supported by Vue, using the Composition API is recommended, and makes it possible to use Vue's Vapor mode for better performance. + + i Use + diff --git a/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-export-default-methods.vue.snap b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-export-default-methods.vue.snap new file mode 100644 index 000000000000..2ad434db7239 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-export-default-methods.vue.snap @@ -0,0 +1,45 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: invalid-export-default-methods.vue +--- +# Input +```ts +// should generate diagnostics +// Options API: methods is not supported in Vapor Mode +export default { + methods: { + increment() { + this.count++ + } + } +} + +``` + +# Diagnostics +``` +invalid-export-default-methods.vue:3:16 lint/nursery/noVueOptionsApi ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Using the Options API is not allowed. + + 1 │ // should generate diagnostics + 2 │ // Options API: methods is not supported in Vapor Mode + > 3 │ export default { + │ ^ + > 4 │ methods: { + > 5 │ increment() { + > 6 │ this.count++ + > 7 │ } + > 8 │ } + > 9 │ } + │ ^ + 10 │ + + i Although the Options API is still supported by Vue, using the Composition API is recommended, and makes it possible to use Vue's Vapor mode for better performance. + + i Use + diff --git a/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-export-default-mounted.vue.snap b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-export-default-mounted.vue.snap new file mode 100644 index 000000000000..585b9f64d6c6 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-export-default-mounted.vue.snap @@ -0,0 +1,41 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: invalid-export-default-mounted.vue +--- +# Input +```ts +// should generate diagnostics +// Options API: lifecycle hooks are not supported in Vapor Mode +export default { + mounted() { + console.log('component mounted') + } +} + +``` + +# Diagnostics +``` +invalid-export-default-mounted.vue:3:16 lint/nursery/noVueOptionsApi ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Using the Options API is not allowed. + + 1 │ // should generate diagnostics + 2 │ // Options API: lifecycle hooks are not supported in Vapor Mode + > 3 │ export default { + │ ^ + > 4 │ mounted() { + > 5 │ console.log('component mounted') + > 6 │ } + > 7 │ } + │ ^ + 8 │ + + i Although the Options API is still supported by Vue, using the Composition API is recommended, and makes it possible to use Vue's Vapor mode for better performance. + + i Use + diff --git a/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-export-default-watch.vue.snap b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-export-default-watch.vue.snap new file mode 100644 index 000000000000..9904412cd5b5 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-export-default-watch.vue.snap @@ -0,0 +1,45 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: invalid-export-default-watch.vue +--- +# Input +```ts +// should generate diagnostics +// Options API: watch is not supported in Vapor Mode +export default { + watch: { + count(newVal, oldVal) { + console.log('count changed') + } + } +} + +``` + +# Diagnostics +``` +invalid-export-default-watch.vue:3:16 lint/nursery/noVueOptionsApi ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Using the Options API is not allowed. + + 1 │ // should generate diagnostics + 2 │ // Options API: watch is not supported in Vapor Mode + > 3 │ export default { + │ ^ + > 4 │ watch: { + > 5 │ count(newVal, oldVal) { + > 6 │ console.log('count changed') + > 7 │ } + > 8 │ } + > 9 │ } + │ ^ + 10 │ + + i Although the Options API is still supported by Vue, using the Composition API is recommended, and makes it possible to use Vue's Vapor mode for better performance. + + i Use + diff --git a/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-full-options-api.vue.snap b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-full-options-api.vue.snap new file mode 100644 index 000000000000..09064d268340 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-full-options-api.vue.snap @@ -0,0 +1,62 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: invalid-full-options-api.vue +--- +# Input +```ts +// should generate diagnostics +// Full Options API component with multiple properties - all should be flagged +export default { + name: 'MyComponent', + props: ['message'], + data() { + return { count: 0 } + }, + computed: { + doubleCount() { + return this.count * 2 + } + }, + watch: { + count(newVal) { + console.log('changed') + } + }, + methods: { + increment() { + this.count++ + } + }, + mounted() { + console.log('mounted') + } +} + +``` + +# Diagnostics +``` +invalid-full-options-api.vue:3:16 lint/nursery/noVueOptionsApi ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Using the Options API is not allowed. + + 1 │ // should generate diagnostics + 2 │ // Full Options API component with multiple properties - all should be flagged + > 3 │ export default { + │ ^ + > 4 │ name: 'MyComponent', + ... + > 25 │ console.log('mounted') + > 26 │ } + > 27 │ } + │ ^ + 28 │ + + i Although the Options API is still supported by Vue, using the Composition API is recommended, and makes it possible to use Vue's Vapor mode for better performance. + + i Use diff --git a/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-mixed-setup-data.vue.snap b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-mixed-setup-data.vue.snap new file mode 100644 index 000000000000..29513543d11b --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-mixed-setup-data.vue.snap @@ -0,0 +1,46 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: invalid-mixed-setup-data.vue +--- +# Input +```ts +// should generate diagnostics +// Mixed setup() with Options API is not supported in Vapor Mode +export default { + setup() { + const someRef = ref(0); + return { someRef }; + }, + data() { + return { count: 0 }; + }, +}; + +``` + +# Diagnostics +``` +invalid-mixed-setup-data.vue:3:16 lint/nursery/noVueOptionsApi ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Using the Options API is not allowed. + + 1 │ // should generate diagnostics + 2 │ // Mixed setup() with Options API is not supported in Vapor Mode + > 3 │ export default { + │ ^ + > 4 │ setup() { + ... + > 9 │ return { count: 0 }; + > 10 │ }, + > 11 │ }; + │ ^ + 12 │ + + i Although the Options API is still supported by Vue, using the Composition API is recommended, and makes it possible to use Vue's Vapor mode for better performance. + + i Use + diff --git a/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-multiple-options.vue.snap b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-multiple-options.vue.snap new file mode 100644 index 000000000000..057aa6f594e4 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-multiple-options.vue.snap @@ -0,0 +1,47 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: invalid-multiple-options.vue +--- +# Input +```ts +// should generate diagnostics +// Multiple Options API properties - all should be flagged +export default { + data() { + return { count: 0 } + }, + methods: { + increment() { + this.count++ + } + } +} + +``` + +# Diagnostics +``` +invalid-multiple-options.vue:3:16 lint/nursery/noVueOptionsApi ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Using the Options API is not allowed. + + 1 │ // should generate diagnostics + 2 │ // Multiple Options API properties - all should be flagged + > 3 │ export default { + │ ^ + > 4 │ data() { + ... + > 10 │ } + > 11 │ } + > 12 │ } + │ ^ + 13 │ + + i Although the Options API is still supported by Vue, using the Composition API is recommended, and makes it possible to use Vue's Vapor mode for better performance. + + i Use + diff --git a/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-props-only.vue.snap b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-props-only.vue.snap new file mode 100644 index 000000000000..578c69d04a8c --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/invalid-props-only.vue.snap @@ -0,0 +1,37 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: invalid-props-only.vue +--- +# Input +```ts +// should generate diagnostics +// Options API with props only - still Options API, not Composition API +export default { + props: ['message'] +} + +``` + +# Diagnostics +``` +invalid-props-only.vue:3:16 lint/nursery/noVueOptionsApi ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Using the Options API is not allowed. + + 1 │ // should generate diagnostics + 2 │ // Options API with props only - still Options API, not Composition API + > 3 │ export default { + │ ^ + > 4 │ props: ['message'] + > 5 │ } + │ ^ + 6 │ + + i Although the Options API is still supported by Vue, using the Composition API is recommended, and makes it possible to use Vue's Vapor mode for better performance. + + i Use + diff --git a/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-definecomponent-function-signature.vue.snap b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-definecomponent-function-signature.vue.snap new file mode 100644 index 000000000000..7084084cdffa --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-definecomponent-function-signature.vue.snap @@ -0,0 +1,16 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: valid-definecomponent-function-signature.vue +--- +# Input +```ts +// should not generate diagnostics +// defineComponent with function signature in .vue file is Composition API (Vue 3.3+) +import { defineComponent, ref, h } from "vue"; + +export default defineComponent((props) => { + const count = ref(0); + return () => h("div", count.value); +}); + +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-definecomponent-function-with-options.js b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-definecomponent-function-with-options.js new file mode 100644 index 000000000000..0849cec8ab3a --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-definecomponent-function-with-options.js @@ -0,0 +1,19 @@ +// should not generate diagnostics +// defineComponent with function signature and extra options (Vue 3.3+) +import { defineComponent, ref, h } from "vue"; + +const Comp = defineComponent( + (props) => { + const count = ref(0); + return () => h("div", count.value); + }, + // extra options - this is still Composition API, not Options API + { + props: { + message: String, + }, + emits: ["update"], + } +); + +export default Comp; diff --git a/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-definecomponent-function-with-options.js.snap b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-definecomponent-function-with-options.js.snap new file mode 100644 index 000000000000..904d88184a91 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-definecomponent-function-with-options.js.snap @@ -0,0 +1,27 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: valid-definecomponent-function-with-options.js +--- +# Input +```js +// should not generate diagnostics +// defineComponent with function signature and extra options (Vue 3.3+) +import { defineComponent, ref, h } from "vue"; + +const Comp = defineComponent( + (props) => { + const count = ref(0); + return () => h("div", count.value); + }, + // extra options - this is still Composition API, not Options API + { + props: { + message: String, + }, + emits: ["update"], + } +); + +export default Comp; + +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-functional-component.js b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-functional-component.js new file mode 100644 index 000000000000..5034f1edf8cf --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-functional-component.js @@ -0,0 +1,5 @@ +// should not generate diagnostics +export default function MyComponent(props) { + return h('div', props.message) +} + diff --git a/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-functional-component.js.snap b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-functional-component.js.snap new file mode 100644 index 000000000000..2d3cc90a3ab8 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-functional-component.js.snap @@ -0,0 +1,13 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: valid-functional-component.js +--- +# Input +```js +// should not generate diagnostics +export default function MyComponent(props) { + return h('div', props.message) +} + + +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-non-vue-object.js b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-non-vue-object.js new file mode 100644 index 000000000000..5cb94910d9ec --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-non-vue-object.js @@ -0,0 +1,7 @@ +// should not generate diagnostics +// Regular JS file, not a Vue component +export default { + data: { message: 'hello' }, + methods: { doSomething: () => {} } +} + diff --git a/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-non-vue-object.js.snap b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-non-vue-object.js.snap new file mode 100644 index 000000000000..96084c303721 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-non-vue-object.js.snap @@ -0,0 +1,15 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: valid-non-vue-object.js +--- +# Input +```js +// should not generate diagnostics +// Regular JS file, not a Vue component +export default { + data: { message: 'hello' }, + methods: { doSomething: () => {} } +} + + +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-passthrough-export.js b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-passthrough-export.js new file mode 100644 index 000000000000..b49b2e3f0369 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-passthrough-export.js @@ -0,0 +1,4 @@ +// should not generate diagnostics +import MyComponent from './MyComponent.vue' +export default MyComponent + diff --git a/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-passthrough-export.js.snap b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-passthrough-export.js.snap new file mode 100644 index 000000000000..c4ef89ccaeb9 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-passthrough-export.js.snap @@ -0,0 +1,12 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: valid-passthrough-export.js +--- +# Input +```js +// should not generate diagnostics +import MyComponent from './MyComponent.vue' +export default MyComponent + + +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-script-setup.vue b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-script-setup.vue new file mode 100644 index 000000000000..0590c284bcca --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-script-setup.vue @@ -0,0 +1,12 @@ + + + diff --git a/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-script-setup.vue.snap b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-script-setup.vue.snap new file mode 100644 index 000000000000..f7ea1039d7f3 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noVueOptionsApi/valid-script-setup.vue.snap @@ -0,0 +1,19 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: valid-script-setup.vue +--- +# Input +```ts +