Skip to content

Conversation

AsherDe
Copy link

@AsherDe AsherDe commented Sep 15, 2025

Summary

This PR introduces a new lint rule, vue/noSetupPropsReactivityLoss, ported from eslint-plugin-vue to prevent a common bug in Vue's Composition API.

The motivation for this change is to help developers avoid accidentally losing props reactivity. In Vue, destructuring the props object directly in the setup function's parameters (e.g., setup({ count })) or in its root scope (e.g., const { count } = props) breaks the reactive link. This can lead to components that do not update correctly when their props change, which can be difficult to debug.

This new rule identifies these specific patterns and provides a clear diagnostic to the user. It correctly handles common exceptions, ignoring destructuring that occurs within nested functions (like watch callbacks or returned render functions) and when using reactivity-preserving helpers like toRefs.

A changeset has been added for this new feature.

Fixes #6305

Test Plan

The rule's implementation is verified through a comprehensive suite of spec (snapshot) tests to ensure its correctness and robustness across various edge cases.

  • Invalid cases that correctly trigger the rule have been tested, including:

    • Simple destructuring in setup function parameters (setup({ count })).
    • Simple destructuring via variable assignment (const { count } = props).
    • Destructuring of multiple properties, with renaming, and with default values.
    • Usage of const, let, and var for assignments.
    • Nested object destructuring and array destructuring.
  • Valid cases that are correctly ignored by the rule have also been thoroughly tested:

    • Accessing props via direct member expression (props.count).
    • Destructuring that occurs inside nested functions (e.g., within watch callbacks or returned render functions).
    • Using reactivity-preserving helpers like toRefs().
    • Destructuring from a local variable that is not the props object.
    • Different setup function signatures (e.g., with a context parameter, as an arrow function, or with no parameters).
    • Components that do not have a setup method at all.

Docs

As this is a new lint rule, the documentation is included directly within the rule's source code (declare_lint_rule!) as rustdoc comments. This documentation explains the purpose of the rule and provides Invalid and Valid code examples, which are also used in the spec tests.

No separate website PR is required for the rule's core documentation at this time.

Copy link

changeset-bot bot commented Sep 15, 2025

🦋 Changeset detected

Latest commit: 56869c2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@biomejs/biome Patch
@biomejs/cli-win32-x64 Patch
@biomejs/cli-win32-arm64 Patch
@biomejs/cli-darwin-x64 Patch
@biomejs/cli-darwin-arm64 Patch
@biomejs/cli-linux-x64 Patch
@biomejs/cli-linux-arm64 Patch
@biomejs/cli-linux-x64-musl Patch
@biomejs/cli-linux-arm64-musl Patch
@biomejs/wasm-web Patch
@biomejs/wasm-bundler Patch
@biomejs/wasm-nodejs Patch
@biomejs/backend-jsonrpc Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions bot added A-Project Area: project A-Linter Area: linter L-JavaScript Language: JavaScript and super languages A-Diagnostic Area: diagnostocis labels Sep 15, 2025
Copy link
Contributor

coderabbitai bot commented Sep 15, 2025

Walkthrough

Adds a new Vue nursery lint rule noVueSetupPropsReactivityLoss. Exports pub mod no_vue_setup_props_reactivity_loss and introduces an empty config struct NoVueSetupPropsReactivityLossOptions. Implements the lint NoVueSetupPropsReactivityLoss which detects parameter destructuring of the first setup parameter and emits diagnostics, with helpers to locate setup functions and compute text ranges. Adds test fixtures (valid and invalid cases) and a changeset entry registering the new rule.

Suggested labels

D-Vue

Suggested reviewers

  • dyc3
  • ematipico
  • mdevils

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Linked Issues Check ⚠️ Warning The PR implements the named Vue lint rule and parameter‐destructuring checks as per issue #6305 but does not yet include root-scope destructuring detection or safe exception handling like toRefs, which are also specified in the linked issue. To fully comply with issue #6305, implement root-scope destructuring detection and ignore reactivity-preserving patterns (e.g., toRefs) or clearly document their inclusion in a planned follow-up, updating the issue and PR description accordingly.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title succinctly and accurately describes the primary change as implementing the new Vue lint rule without introducing unnecessary detail or noise.
Out of Scope Changes Check ✅ Passed All changes introduced are focused on adding the new lint rule, its configuration, tests, and associated changeset without touching unrelated code, so no out-of-scope modifications are present.
Description Check ✅ Passed The description clearly outlines the motivation, implementation details, test plan, documentation approach, and links the fix to issue #6305, making it directly related to the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9450bfe and 56869c2.

📒 Files selected for processing (1)
  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
**/*.{rs,toml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Before committing, format Rust and TOML files (e.g., via just f/just format)

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Document rules, assists, and options via inline rustdoc in Rust source

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
🧬 Code graph analysis (1)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (2)
  • RuleDomain (904-912)
  • TextRange (9283-9283)
🔇 Additional comments (13)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (13)

1-11: Imports look good.

All imports are properly organized at the top of the file and appear necessary for the implementation.


13-50: Documentation accurately reflects the current implementation scope.

The rustdoc correctly documents parameter destructuring detection. Since this PR is limited to parameter destructuring only (with root-scope detection planned for Part 2), the documentation appropriately matches what's implemented.


52-58: LGTM.

The Violation struct with a single TextRange field is appropriate for the current scope.


60-63: Good abstraction for different setup function forms.

The SetupFunction enum cleanly handles both method and property-value function patterns.


65-104: Rule implementation handles the expected Vue component patterns.

The run method correctly matches both export default objects and defineComponent calls, and the diagnostic provides clear guidance.


106-116: LGTM.

The function correctly extracts the object expression argument and delegates to member checking.


118-125: Clean pipeline for checking object members.

The functional approach with filter_map chains is idiomatic and easy to follow.


127-135: Core parameter destructuring detection is correct.

The function properly flags both object and array destructuring patterns whilst allowing simple identifier bindings.


137-165: Comprehensive parameter extraction across all function forms.

The implementation correctly handles arrows (both single binding and parenthesized parameters), declarations, and expressions.


167-185: Setup function detection covers the expected patterns.

The function handles both method shorthand (setup() {}) and property forms (setup: function() {}).


187-197: LGTM.

The helper correctly extracts function and arrow function expressions.


203-213: Clean parameter extraction from JsParameters.

The iterator chain with error handling is appropriate and follows idiomatic Rust patterns.


199-201: Verify quoted ‘setup’ key handling.
Tests don’t cover quoted “setup” keys; confirm that name().text() strips quotes on literal member names and add fixtures for 'setup': and "setup":.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (7)
.changeset/yellow-parts-appear.md (1)

5-7: Changeset polish: add docs link, fix punctuation, and include a valid snippet.

  • Please include a link to the rule’s docs, per our changeset guidelines.
  • The “Invalid code example:” line should end with a full stop.
  • Consider adding a minimal “Valid code example.” using toRefs to show the safe pattern.

Apply this diff:

@@
-Added the `vue/noSetupPropsReactivityLoss` rule to the nursery.
+Added the `vue/noSetupPropsReactivityLoss` rule to the nursery.
@@
-This new rule disallows usages that cause the reactivity of `props` passed to the `setup` function to be lost.
+This new rule disallows usages that cause the reactivity of `props` passed to the `setup` function to be lost.
+See documentation: https://biomejs.dev/linter/rules/vue/noSetupPropsReactivityLoss.
@@
-Invalid code example:
+Invalid code example.
@@

export default {
setup({ count }) {
// count is no longer reactive here.
return () => h("div", count);
},
};

+
+Valid code example.
+
+```jsx
+export default {
+  setup(props) {
+    const { count } = toRefs(props);
+    return () => h("div", count.value);
+  },
+};
+```

Also applies to: 9-9, 18-18

crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_assignment.js (1)

1-7: Good invalid fixture; optional expectation hint.

Add a short top comment to indicate this should produce a diagnostic (helps skim large suites).

-// Destructuring assignment
+// Destructuring assignment
+/* should generate diagnostics */
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_params.js (1)

1-6: Covers parameter destructuring nicely; add hint comment.

Same tiny note as above for consistency across fixtures.

-// Destructuring in setup parameters
+// Destructuring in setup parameters
+/* should generate diagnostics */
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_toRefs.js (1)

4-9: Nice positive case; consider adding toRef/alias variants.

To harden coverage, add a companion fixture exercising toRef(props, "count") and aliasing (const { count: c } = toRefs(props)).

Example new file:

// crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_toRef.js
export default {
  setup(props) {
    const c = toRef(props, "count");
    const { count: alias } = toRefs(props);
    return () => h('div', c.value + alias.value);
  }
}
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/array_destructuring.js (1)

1-6: Drop array-destructuring test — upstream rule only flags object-pattern/member access

Verified: eslint-plugin-vue's no-setup-props-reactivity-loss flags object-pattern destructuring and member expressions of props, not array destructuring. Remove or skip this test to keep parity and avoid drift.

Location: crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/array_destructuring.js

crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs (2)

89-92: Consider documenting the RuleState enum variants.

While the variant names are descriptive, adding doc comments would clarify when each state is triggered for future maintainers.

+/// Represents different ways props reactivity can be lost in Vue setup
 pub enum RuleState {
+    /// Props were destructured directly in the setup function parameters
     DestructuredParameter(TextRange),
+    /// Props were destructured via assignment in the setup body root scope
     DestructuredAssignment(TextRange),
 }

236-268: Consider extracting the reactive helper check for clarity.

The nested logic for checking reactive helpers could be clearer as a separate function.

+fn is_reactive_helper_call(call_expr: &JsCallExpression) -> bool {
+    call_expr
+        .callee()
+        .ok()
+        .and_then(|callee| callee.as_js_identifier_expression().cloned())
+        .and_then(|id_expr| id_expr.name().ok())
+        .and_then(|name| name.value_token().ok())
+        .is_some_and(|token| {
+            let text = token.text_trimmed();
+            matches!(text, "toRefs" | "toRef" | "reactive" | "ref")
+        })
+}

 let should_report = match init_expr {
     AnyJsExpression::JsIdentifierExpression(id_expr) => id_expr
         .name()
         .ok()
         .and_then(|name| name.value_token().ok())
         .is_some_and(|token| token.text_trimmed() == props_name),
     AnyJsExpression::JsCallExpression(call_expr) => {
-        let is_reactive_helper = call_expr
-            .callee()
-            .ok()
-            .and_then(|callee| callee.as_js_identifier_expression().cloned())
-            .and_then(|id_expr| id_expr.name().ok())
-            .and_then(|name| name.value_token().ok())
-            .is_some_and(|token| {
-                let text = token.text_trimmed();
-                matches!(text, "toRefs" | "toRef" | "reactive" | "ref")
-            });
-
-        !is_reactive_helper
+        !is_reactive_helper_call(call_expr)
             && call_expr.arguments().ok().is_some_and(|args| {
                 args.args().into_iter().flatten().any(|arg| {
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c9f00eb and a212358.

⛔ Files ignored due to path filters (14)
  • crates/biome_configuration/src/analyzer/linter/rules.rs is excluded by !**/rules.rs and included by **
  • crates/biome_diagnostics_categories/src/categories.rs is excluded by !**/categories.rs and included by **
  • crates/biome_js_analyze/src/lint/nursery.rs is excluded by !**/nursery.rs and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/array_destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_assignment.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_params.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/multiple_destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/nested_destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_callbacks.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_toRefs.js.snap is excluded by !**/*.snap and included by **
  • packages/@biomejs/backend-jsonrpc/src/workspace.ts is excluded by !**/backend-jsonrpc/src/workspace.ts and included by **
  • packages/@biomejs/biome/configuration_schema.json is excluded by !**/configuration_schema.json and included by **
📒 Files selected for processing (13)
  • .changeset/yellow-parts-appear.md (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/array_destructuring.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_assignment.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_params.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/multiple_destructuring.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/nested_destructuring.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_callbacks.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_toRefs.js (1 hunks)
  • crates/biome_rule_options/src/lib.rs (1 hunks)
  • crates/biome_rule_options/src/no_setup_props_reactivity_loss.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_rule_options/src/no_setup_props_reactivity_loss.rs
  • crates/biome_rule_options/src/lib.rs
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_toRefs.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/nested_destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/multiple_destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/array_destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_params.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_callbacks.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_assignment.js
  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid.js
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Format all Rust source files before committing (just f)

Files:

  • crates/biome_rule_options/src/no_setup_props_reactivity_loss.rs
  • crates/biome_rule_options/src/lib.rs
  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_toRefs.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/nested_destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/multiple_destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/array_destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_params.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_callbacks.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_assignment.js
  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid.js
**/tests/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place test files under a tests/ directory in each crate

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_toRefs.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/nested_destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/multiple_destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/array_destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_params.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_callbacks.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_assignment.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid.js
.changeset/*.md

📄 CodeRabbit inference engine (CONTRIBUTING.md)

.changeset/*.md: In changeset files, only use #### or ##### headers; avoid other header levels
Changeset descriptions should use past tense for what you did (e.g., "Added...")
Describe current Biome behavior in present tense within changesets (e.g., "Biome now supports...")
For bug fixes in changesets, start with a link to the issue (e.g., "Fixed #1234: ...")
When referencing rules or assists in changesets, include links to their documentation pages
Include a minimal code block in the changeset when applicable to demonstrate the change
End every sentence in the changeset description with a period

Files:

  • .changeset/yellow-parts-appear.md
🧠 Learnings (3)
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/crates/biome_rule_options/lib/**/*.rs : Options types must implement serialization/deserialization and schema support using derives: `Serialize`, `Deserialize`, `Deserializable`, and `#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]`

Applied to files:

  • crates/biome_rule_options/src/no_setup_props_reactivity_loss.rs
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/crates/biome_rule_options/lib/**/*.rs : Define per-rule options types in `biome_rule_options` crate (one file per rule, e.g., `lib/use_my_rule.rs`)

Applied to files:

  • crates/biome_rule_options/src/lib.rs
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/**/src/{lint,assist}/**/*.rs : In declare_lint_rule! macros, set `version: "next"` for new or updated rules

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs
🧬 Code graph analysis (10)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_toRefs.js (5)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_assignment.js (1)
  • props (4-4)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid.js (4)
  • props (12-12)
  • props (57-57)
  • props (58-58)
  • props (74-74)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/multiple_destructuring.js (2)
  • props (4-4)
  • props (5-5)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid.js (3)
  • toRefs (69-69)
  • count (54-54)
  • count (86-86)
crates/biome_js_analyze/tests/specs/suspicious/noRedeclare/valid-declaration-merging.ts (1)
  • h (131-133)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/nested_destructuring.js (1)
crates/biome_js_analyze/tests/specs/suspicious/noRedeclare/valid-declaration-merging.ts (1)
  • h (131-133)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/multiple_destructuring.js (3)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_assignment.js (1)
  • props (4-4)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid.js (4)
  • props (12-12)
  • props (57-57)
  • props (58-58)
  • props (74-74)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid.js (2)
  • count (54-54)
  • count (86-86)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/array_destructuring.js (1)
crates/biome_js_analyze/tests/specs/suspicious/noRedeclare/valid-declaration-merging.ts (1)
  • h (131-133)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_params.js (2)
crates/biome_js_analyze/tests/specs/suspicious/noRedeclare/valid-declaration-merging.ts (1)
  • h (131-133)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid.js (2)
  • count (54-54)
  • count (86-86)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_callbacks.js (4)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_assignment.js (1)
  • props (4-4)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid.js (4)
  • props (12-12)
  • props (57-57)
  • props (58-58)
  • props (74-74)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/multiple_destructuring.js (2)
  • props (4-4)
  • props (5-5)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid.js (2)
  • count (54-54)
  • count (86-86)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_assignment.js (3)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid.js (4)
  • props (12-12)
  • props (57-57)
  • props (58-58)
  • props (74-74)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/multiple_destructuring.js (2)
  • props (4-4)
  • props (5-5)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid.js (2)
  • count (54-54)
  • count (86-86)
crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs (2)
crates/biome_js_syntax/src/function_ext.rs (1)
  • statements (94-111)
crates/biome_js_syntax/src/expr_ext.rs (2)
  • callee (32-37)
  • value_token (1547-1556)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid.js (3)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_assignment.js (1)
  • props (4-4)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid.js (4)
  • props (12-12)
  • props (57-57)
  • props (58-58)
  • props (74-74)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/multiple_destructuring.js (2)
  • props (4-4)
  • props (5-5)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid.js (4)
crates/biome_js_analyze/tests/specs/suspicious/noRedeclare/valid-declaration-merging.ts (1)
  • h (131-133)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid.js (2)
  • count (54-54)
  • count (86-86)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_assignment.js (1)
  • props (4-4)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/multiple_destructuring.js (2)
  • props (4-4)
  • props (5-5)
🔇 Additional comments (8)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/nested_destructuring.js (1)

1-6: LGTM — nested parameter destructuring covered.

This neatly captures the nested case the rule should flag.

crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/multiple_destructuring.js (1)

1-8: LGTM — multiple assignments case is covered.

Captures repeated root-scope destructuring within setup.

crates/biome_rule_options/src/lib.rs (1)

165-165: LGTM — module exposed and options file present.
Options file: crates/biome_rule_options/src/no_setup_props_reactivity_loss.rs — contains pub struct NoSetupPropsReactivityLossOptions {}.

crates/biome_rule_options/src/no_setup_props_reactivity_loss.rs (1)

1-6: LGTM! The options struct follows the established patterns.

The empty options struct is correctly configured with all required derives and attributes for serialisation/deserialisation support, following the codebase conventions.

crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_callbacks.js (1)

1-15: LGTM! Test case correctly validates nested scope destructuring.

The test properly demonstrates that destructuring within callbacks and render functions is permitted, as these maintain the reactive context.

crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid.js (1)

1-77: Excellent test coverage for invalid patterns!

The test suite comprehensively covers all major destructuring patterns that break reactivity: parameter destructuring, root-scope assignments, array destructuring, nested patterns, and various declaration types (const/let/var).

crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs (1)

11-87: Comprehensive documentation with clear examples!

The rule documentation excellently explains the reactivity issue and provides both invalid and valid examples, making it easy for users to understand the problem and solutions.

crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid.js (1)

1-103: Thorough test coverage for valid patterns!

The test suite covers all the key valid scenarios: direct props usage, nested destructuring, reactive helpers (toRefs), computed properties, and edge cases like arrow functions and context parameters.

@AsherDe AsherDe force-pushed the feat/vue-reactivity-rule branch from 61a2780 to f925549 Compare September 16, 2025 03:06
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid.js (2)

34-38: Array pattern in setup params is dubious for Vue props.

The rule’s contract is about destructuring the “props object”. Array destructuring here isn’t a realistic Vue usage and may not be flagged depending on implementation. Either drop this case or add a clarifying comment asserting parity with eslint-plugin-vue if it intentionally warns on any non‑Identifier first param.

If parity is intended, please confirm the upstream rule’s behaviour and that our snapshot shows a diagnostic for this block.


72-77: Consider one more “invalid” for assignment without declaration.

Add a case for bare assignment destructuring, which also breaks reactivity:

  • ({ count } = props)
  • And optionally default-in-assignment: const { count = 0 } = props

I can spin up a separate spec file (e.g., destructuring_assignment_no_decl.js) with these examples if you’d like.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 61a2780 and f925549.

⛔ Files ignored due to path filters (14)
  • crates/biome_configuration/src/analyzer/linter/rules.rs is excluded by !**/rules.rs and included by **
  • crates/biome_diagnostics_categories/src/categories.rs is excluded by !**/categories.rs and included by **
  • crates/biome_js_analyze/src/lint/nursery.rs is excluded by !**/nursery.rs and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/array_destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_assignment.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_params.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/multiple_destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/nested_destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_callbacks.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_toRefs.js.snap is excluded by !**/*.snap and included by **
  • packages/@biomejs/backend-jsonrpc/src/workspace.ts is excluded by !**/backend-jsonrpc/src/workspace.ts and included by **
  • packages/@biomejs/biome/configuration_schema.json is excluded by !**/configuration_schema.json and included by **
📒 Files selected for processing (13)
  • .changeset/yellow-parts-appear.md (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_assignment.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_params.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/multiple_destructuring.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/nested_destructuring.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_callbacks.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_toRef.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_toRefs.js (1 hunks)
  • crates/biome_rule_options/src/lib.rs (1 hunks)
  • crates/biome_rule_options/src/no_setup_props_reactivity_loss.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (12)
  • crates/biome_rule_options/src/lib.rs
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_assignment.js
  • crates/biome_rule_options/src/no_setup_props_reactivity_loss.rs
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_params.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/nested_destructuring.js
  • .changeset/yellow-parts-appear.md
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_callbacks.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/multiple_destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_toRef.js
  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_toRefs.js
🧰 Additional context used
📓 Path-based instructions (3)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid.js
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid.js
**/tests/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place test files under a tests/ directory in each crate

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid.js
🧬 Code graph analysis (1)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid.js (3)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid.js (2)
  • count (54-54)
  • count (86-86)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_assignment.js (1)
  • props (5-5)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/multiple_destructuring.js (2)
  • props (5-5)
  • props (6-6)
🔇 Additional comments (2)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid.js (2)

2-7: Good coverage of the core “invalid” patterns.

These cases accurately exercise parameter destructuring, root-scope destructuring (including renames), nested properties, defaults, and multiple declarations with var/let/const. Keep them—just not all in the same file.

Also applies to: 10-16, 19-23, 26-31, 41-45, 48-52, 55-61, 64-69, 72-77


55-61: Ensure both destructurings emit separate diagnostics.

Two root-scope destructurings from props should yield two lint messages (no dedup). Worth double‑checking the snapshot to avoid accidental suppression.

@AsherDe AsherDe force-pushed the feat/vue-reactivity-rule branch from f925549 to 5aa1e66 Compare September 16, 2025 05:01
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs (2)

261-272: Reactive helper detection could be more robust.

The function correctly identifies Vue's reactivity helpers, but consider whether it should also handle namespaced imports (e.g., Vue.toRefs) or renamed imports (e.g., import { toRefs as tr }).

For enhanced coverage, consider extending the helper detection:

 fn is_reactive_helper_call(call_expr: &JsCallExpression) -> bool {
-    call_expr
-        .callee()
-        .ok()
-        .and_then(|callee| callee.as_js_identifier_expression().cloned())
-        .and_then(|id_expr| id_expr.name().ok())
-        .and_then(|name| name.value_token().ok())
-        .is_some_and(|token| {
-            let text = token.text_trimmed();
-            matches!(text, "toRefs" | "toRef" | "reactive" | "ref")
-        })
+    let callee = call_expr.callee().ok()?;
+    
+    // Handle direct calls (toRefs, toRef, etc.)
+    if let Some(id_expr) = callee.as_js_identifier_expression() {
+        return id_expr.name().ok()
+            .and_then(|name| name.value_token().ok())
+            .is_some_and(|token| {
+                let text = token.text_trimmed();
+                matches!(text, "toRefs" | "toRef" | "reactive" | "ref")
+            });
+    }
+    
+    // Handle member expressions (Vue.toRefs, etc.)
+    if let Some(member_expr) = callee.as_js_static_member_expression() {
+        return member_expr.member().ok()
+            .and_then(|member| member.as_js_name())
+            .and_then(|name| name.value_token().ok())
+            .is_some_and(|token| {
+                let text = token.text_trimmed();
+                matches!(text, "toRefs" | "toRef" | "reactive" | "ref")
+            });
+    }
+    
+    false
 }

162-169: Handle quoted and computed 'setup' property names

find_setup_method currently only matches literal names via name_token.text() == "setup"; this will miss quoted property keys and computed member names — normalise/strip quotes for string-literal keys and also check as_js_computed_member_name().
Location: crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs:162–169

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f925549 and 5aa1e66.

⛔ Files ignored due to path filters (6)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_assignment.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_params.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid_assignment_no_decl.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/multiple_destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/nested_destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_toRef.js.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (7)
  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_assignment.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_params.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid_assignment_no_decl.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/multiple_destructuring.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/nested_destructuring.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_toRef.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/multiple_destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_params.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/nested_destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/valid_toRef.js
  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_assignment.js
🧰 Additional context used
📓 Path-based instructions (4)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid_assignment_no_decl.js
  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid_assignment_no_decl.js
  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs
**/tests/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place test files under a tests/ directory in each crate

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid_assignment_no_decl.js
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Format all Rust source files before committing (just f)

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs
🧠 Learnings (1)
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/**/src/{lint,assist}/**/*.rs : In declare_lint_rule! macros, set `version: "next"` for new or updated rules

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs
🧬 Code graph analysis (2)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid_assignment_no_decl.js (2)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/destructuring_assignment.js (1)
  • props (5-5)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/multiple_destructuring.js (2)
  • props (5-5)
  • props (6-6)
crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs (2)
crates/biome_js_syntax/src/function_ext.rs (1)
  • statements (94-111)
crates/biome_js_syntax/src/expr_ext.rs (2)
  • callee (32-37)
  • value_token (1547-1556)
🔇 Additional comments (5)
crates/biome_js_analyze/tests/specs/nursery/noSetupPropsReactivityLoss/invalid_assignment_no_decl.js (1)

1-9: Well-crafted test case for destructuring assignment pattern.

This fixture properly tests the DestructuredAssignment pattern where a variable is declared separately (let count;) and then assigned via destructuring (({ count } = props)). The test structure aligns perfectly with the rule's detection logic and complements the existing test coverage.

crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs (4)

82-88: Rule declaration follows expected conventions.

The rule metadata is properly configured with version: "next" as required for new rules, and the naming follows Vue.js conventions consistently.


203-218: Props parameter extraction handles complex cases well.

The parameter name extraction correctly navigates through the AST layers to identify the props parameter name. The chain of optional unwrapping is appropriate for handling various parameter declaration patterns.


241-250: Parenthesised assignment handling is thorough.

Good attention to detail in handling both direct assignment expressions and parenthesised ones (({ count } = props)). This covers the test case in invalid_assignment_no_decl.js properly.


310-325: Verify call-expression argument matching for props

File: crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs:310–325 — the matcher only accepts a JsIdentifierExpression named "props" and relies on is_reactive_helper_call to exclude toRef(s)/toRefs (tests already cover those). Ensure it still flags plain calls like someFunction(props) and add/verify tests for these edge cases:

  • parenthesised args: someFunction((props))
  • member access / property use: someFunction(props.count)
  • spread/object wrappers: someFunction({...props}) or someFunction({ ...props })
  • aliasing/variable pass-through: const p = props; someFunction(p)
  • non-identifier patterns (object/array destructuring as arg)

Copy link
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @AsherDe for your contribution. There are some things that must be addressed

If you used some used AI tools for this PR, please disclose it.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (5)
crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs (5)

82-87: Add Vue domain and rule source metadata

This rule should be scoped to the Vue domain and attribute its provenance (ported from eslint-plugin-vue). Please add the appropriate domains and source metadata in declare_lint_rule!.

To mirror existing conventions, search for how other framework‑scoped rules declare these fields and follow the same pattern:

#!/bin/bash
# Find examples of 'domains' and 'source' usage in rules
rg -nP '(?s)declare_lint_rule!\s*\{[^}]*\bdomains\b' crates | sed -n '1,120p'
rg -nP '(?s)declare_lint_rule!\s*\{[^}]*\bsource\b' crates | sed -n '1,120p'

Apply something along these lines (adjust to match the repo’s canonical types):

     pub NoSetupPropsReactivityLoss {
         version: "next",
         name: "noSetupPropsReactivityLoss",
         language: "js",
         recommended: false,
+        domains: ["vue"],
+        source: RuleSource::Eslint { plugin: "eslint-plugin-vue", rule: "no-setup-props-reactivity-loss" },
     }

13-26: Tighten docs and remove unsupported claim

  • Clarify the first sentence with Vue context.
  • The rule doesn’t implement “member access patterns” checks; drop that bullet to avoid over‑promising.
-    /// Disallow usages that lose the reactivity of `props` passed to `setup`.
+    /// Disallow usages that lose the reactivity of `props` passed to `setup` in Vue projects.
@@
-    /// This rule reports:
-    /// - Direct destructuring of props in setup function parameters
-    /// - Destructuring assignment of props in the root scope of setup
-    /// - Member access patterns that could break reactivity
+    /// This rule reports:
+    /// - Direct destructuring of props in setup function parameters.
+    /// - Destructuring assignment of props in the root scope of `setup`.
@@
-    /// Note: Destructuring is allowed inside nested functions, callbacks, and
+    /// Note: destructuring is allowed inside nested functions, callbacks, and

Also applies to: 20-23


1-1: Use Ast query (no semantic model needed here)

The rule doesn’t use symbol resolution; prefer Ast<...> over Semantic<...>.

- use crate::services::semantic::Semantic;
+ use crate::services::node::Ast;
@@
-    type Query = Semantic<JsExportDefaultExpressionClause>;
+    type Query = Ast<JsExportDefaultExpressionClause>;

Also applies to: 98-103


209-211: Prefer borrowing Option<&T> and avoid string allocations

  • Pass Option<&JsParameters> instead of &Option<JsParameters>.
  • Pass Option<&str> for the props name and derive it via as_deref(); avoid String allocations.
-        check_setup_body_statements(body.statements().iter(), signals, &params.ok());
+        check_setup_body_statements(body.statements().iter(), signals, params.ok().as_ref());
@@
-fn check_setup_body_statements(
-    statements: impl Iterator<Item = AnyJsStatement>,
-    signals: &mut Vec<RuleState>,
-    params: &Option<biome_js_syntax::JsParameters>,
-) {
+fn check_setup_body_statements(
+    statements: impl Iterator<Item = AnyJsStatement>,
+    signals: &mut Vec<RuleState>,
+    params: Option<&biome_js_syntax::JsParameters>,
+) {
@@
-        .map(|n| n.text().to_string());
+        .map(|n| n.text_trimmed().to_string()); // optionally: refactor to avoid allocation entirely
@@
-fn check_variable_declaration_for_props_destructuring(
+fn check_variable_declaration_for_props_destructuring(
     declaration: &JsVariableDeclaration,
     signals: &mut Vec<RuleState>,
-    props_param_name: &Option<String>,
+    props_param_name: Option<&str>,
 ) {
-    let Some(props_name) = props_param_name else {
+    let Some(props_name) = props_param_name else {
         return;
     };
@@
-fn check_assignment_expression_for_props_destructuring(
+fn check_assignment_expression_for_props_destructuring(
     assign_expr: &JsAssignmentExpression,
     signals: &mut Vec<RuleState>,
-    props_param_name: &Option<String>,
+    props_param_name: Option<&str>,
 ) {
-    let Some(props_name) = props_param_name.as_ref() else {
+    let Some(props_name) = props_param_name else {
         return;
     };

And at the call sites:

-                    &props_param_name,
+                    props_param_name.as_deref(),

Same for the assignment helper call.

Also applies to: 198-201, 306-311, 368-371, 227-227


129-152: Sharper diagnostic phrasing (error first, then why)

Follow Biome’s “pillars”: crisp error, brief why, actionable note. Current text is okay but can be stronger.

-                        "Destructuring props in setup function parameters causes reactivity loss."
+                        "Do not destructure `props` in `setup` parameters."
                 )
                 .note(markup! {
-                    "Use the props object directly to maintain reactivity: `props.propertyName`"
+                    "Why: destructuring breaks Vue’s reactivity link to `props`. Use `props.propertyName` or destructure inside callbacks/render functions."
                 }),
@@
-                        "Destructuring props in the root scope of setup causes reactivity loss."
+                        "Avoid destructuring `props` in the root scope of `setup`."
                 )
                 .note(markup! {
-                    "Use the props object directly or destructure inside callbacks/render functions."
+                    "Why: destructuring at the root of `setup` breaks reactivity. Access via `props.x` or destructure inside callbacks/render functions."
                 }),
🧹 Nitpick comments (1)
crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs (1)

90-97: Make RuleState private

No external consumers need this; keeping it private avoids surface‑area creep.

-pub enum RuleState {
+enum RuleState {
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5aa1e66 and dbf3453.

📒 Files selected for processing (1)
  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Format all Rust source files before committing (just f)

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs
🧠 Learnings (9)
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/**/src/{lint,assist}/**/*.rs : In declare_lint_rule! macros, set `version: "next"` for new or updated rules

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/**/src/{lint,assist}/**/*.rs : Follow rule naming conventions: use `no<Concept>` to forbid and `use<Concept>` to mandate; prefer consistent prefixes (e.g., `noDuplicate<Concept>`, `useConsistent<Concept>`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/crates/biome_rule_options/lib/**/*.rs : Define per-rule options types in `biome_rule_options` crate (one file per rule, e.g., `lib/use_my_rule.rs`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/**/src/**/nursery/**/*.rs : Place all new rules in the nursery group (implement rule files under a `.../src/*/nursery/` directory)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/**/src/{lint,assist}/**/*.rs : Recommended rules with domains are enabled only when users enable the matching domains; use `domains` metadata judiciously

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/**/src/{lint,assist}/**/*.rs : Code blocks in rule docs must specify language; invalid snippets require `expect_diagnostic`; use `options`/`full_options`/`use_options` markers as appropriate

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/**/src/{lint,assist}/**/*.rs : Documentation for rules: first paragraph must be a single line; include `## Examples` with `### Invalid` first then `### Valid`

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/crates/biome_js_analyze/tests/quick_test.rs : Quick test: edit `tests/quick_test.rs`, remove or comment `#[ignore]`, set `SOURCE`, and adjust `RuleFilter`

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs
📚 Learning: 2025-08-11T11:46:05.836Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_diagnostics/CONTRIBUTING.md:0-0
Timestamp: 2025-08-11T11:46:05.836Z
Learning: Diagnostics should explain why something went wrong, using log advices and links to relevant documentation

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs
🧬 Code graph analysis (1)
crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs (2)
crates/biome_js_syntax/src/function_ext.rs (1)
  • statements (94-111)
crates/biome_js_syntax/src/expr_ext.rs (2)
  • callee (32-37)
  • value_token (1547-1556)
🔇 Additional comments (1)
crates/biome_js_analyze/src/lint/nursery/no_setup_props_reactivity_loss.rs (1)

266-300: Helper detection looks good

is_reactive_helper_call handles identifiers and static members (Vue.toRefs etc.). Nice and tidy.

Copy link

codspeed-hq bot commented Sep 16, 2025

CodSpeed Performance Report

Merging #7513 will not alter performance

Comparing AsherDe:feat/vue-reactivity-rule (56869c2) with main (d6da4d5)

Summary

✅ 53 untouched
⏩ 85 skipped1

Footnotes

  1. 85 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@ematipico
Copy link
Member

ematipico commented Sep 16, 2025

By the way, you might have noticed that using a "top to bottom" strategy might be very challenging, because you need to track all places where props are used. The current implementation doesn't cut it.

You have two options:

@AsherDe
Copy link
Author

AsherDe commented Sep 16, 2025

@ematipico Thanks so much for this detailed and insightful feedback.

To be honest, I was actually struggling with the top-down approach, wondering how I could reliably cover all the different ways props might be passed around and used. Your explanation makes perfect sense and clarifies the exact problem I was wrestling with.

I will go with Option 1 and refactor the rule using the bottom-up strategy. The no_solid_destructured_props.rs example is a fantastic guide, and I'm confident I can rework the logic based on that pattern.

Thanks again for the great guidance! I'll push the updated implementation once it's ready.

@AsherDe AsherDe force-pushed the feat/vue-reactivity-rule branch from 4e92f8f to 7fb5a8d Compare September 18, 2025 12:47
@github-actions github-actions bot added the A-CLI Area: CLI label Sep 18, 2025
@AsherDe AsherDe force-pushed the feat/vue-reactivity-rule branch from 7fb5a8d to 0fafd9b Compare September 18, 2025 17:09
@AsherDe
Copy link
Author

AsherDe commented Sep 18, 2025

Hi @ematipico ,

Thanks again for the great feedback and guidance. Your suggestion to switch to a bottom-up strategy was spot on, and your no_solid_destructured_props.rs rule was the perfect reference to guide the refactor.

I've pushed the new implementation. Here’s a quick overview of the changes:

  • New Strategy: The rule now uses a "bottom-up" approach. The Query entry point is now AnyJsFunction instead of the component export.

  • Identifying setup: I've added a new helper function, is_vue_setup_function, which is responsible for determining if a given function is actually a setup method within a valid Vue component (it checks for export default, defineComponent, etc.).

  • Detecting Violations:

    • For parameter destructuring, the logic remains straightforward, checking the function's first parameter.
    • For root-scope destructuring, the rule now uses the SemanticModel. It finds the binding for the props parameter and then iterates through all its reads. For each read, it verifies two things:
      1. Is the reference directly within the root scope of the setup function?
      2. Is it part of a destructuring assignment?
  • Other Fixes: I also incorporated the other review feedback, so the rule now correctly handles namespaced reactive helpers (e.g., Vue.toRefs) and quoted property names (e.g., "setup": ...).

I believe this new implementation is much more robust and correctly handles the edge cases. It's ready for another look whenever you have a chance. Thanks again for your help!

@AsherDe AsherDe requested a review from ematipico September 18, 2025 17:45
Copy link
Contributor

@dyc3 dyc3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few notes

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (12)
.changeset/grumpy-pigs-chew.md (1)

7-8: Add present‑tense behaviour line and a docs link.

Changesets should include the “what Biome does now” sentence and a link to the rule docs.

Apply this diff:

 This new rule disallows usages that cause the reactivity of `props` passed to the `setup` function to be lost.
+
+Biome now reports destructuring in Vue setup that causes props reactivity loss.
+
+See documentation: https://biomejs.dev/linter/rules/vue/no-setup-props-reactivity-loss/.
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js (1)

1-50: Add a few more variable destructuring variants (rest, rename, defaults, nested arrays).

These strengthen coverage for root‑scope destructuring patterns.

Append examples like:

+
+// Variable destructuring with rest + renaming
+export default {
+  setup(props) {
+    const { foo: f, ...rest } = props;
+    return () => h('div', f + rest.bar)
+  }
+}
+
+// Variable destructuring with defaults
+export default {
+  setup(props) {
+    const { foo = 'x', bar = 1 } = props;
+    return () => h('div', foo + bar)
+  }
+}
+
+// Nested object+array destructuring from props
+export default {
+  setup(props) {
+    const { items: [first, second] } = props;
+    return () => h('div', first + second)
+  }
+}
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-typescript.ts (1)

1-38: Add a generic defineComponent case.

Covers TS generic inference at the component factory boundary.

Append:

+
+// defineComponent with generic Props
+interface Props2 { foo: string; bar?: number }
+export default defineComponent<Props2>({
+  setup(props) {
+    const { foo, bar } = props;
+    return () => h('div', foo + bar)
+  }
+});
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js (1)

24-30: Also test property initialiser and quoted key forms of setup.

Covers setup: ({ foo }) => {} and ["setup"]({ foo }) {} which are common and mentioned in the PR.

Append:

+
+// Property initialiser (arrow) with parameter destructuring
+export default {
+  setup: ({ foo, bar }) => {
+    return () => h('div', foo + bar)
+  }
+}
+
+// Quoted setup key
+export default {
+  ["setup"]({ foo, bar }) {
+    return () => h('div', foo + bar)
+  }
+}
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js (1)

40-49: Add more control‑flow variants (for‑of, while).

Ensures we still report outside nested functions.

Append:

+
+// Destructuring in for-of
+export default {
+  setup(props) {
+    for (const x of [1]) {
+      const { foo } = props;
+      console.log(foo, x);
+    }
+    return () => h('div', props.bar)
+  }
+}
+
+// Destructuring in while loop
+export default {
+  setup(props) {
+    let i = 0;
+    while (i++ < 1) {
+      const { foo } = props;
+      console.log(foo);
+    }
+    return () => h('div', props.bar)
+  }
+}
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-nested-contexts.js (2)

19-24: Use computed here for clarity (currently using ref(() => ...)).

Purely readability; still parses either way.

Apply this diff:

-    const computed = ref(() => {
+    const value = computed(() => {
       const { foo, bar } = props;  // OK in computed
       return foo + bar;
     });
-    return () => h('div', computed.value)
+    return () => h('div', value.value)

37-55: Optional: add an onMounted (or similar lifecycle) callback example.

Strengthens “nested function” coverage with a canonical lifecycle.

Append:

+
+// Destructuring in lifecycle callback
+export default {
+  setup(props) {
+    onMounted(() => {
+      const { foo } = props; // OK in lifecycle callback
+      console.log(foo);
+    });
+    return () => h('div', props.foo)
+  }
+}
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (3)

29-35: Minor: reactive(props) is unconventional on props

Wrapping props in reactive is unnecessary in Vue 3 (props are already reactive but readonly). It’s fine here (no destructuring), but the comment “preserve reactivity” may mislead newcomers.

Apply if you want to tighten wording:

-// Using reactive wrapper
+// Using a reactive wrapper (not generally needed for `props`; shown for rule coverage)

37-42: “ref(props.foo)” doesn’t preserve the reactive link

ref(props.foo) snapshots the value; it won’t update when props.foo changes. The rule should ignore it (no destructuring), but the comment “preserve reactivity” is inaccurate. Prefer toRef(props, 'foo').

-// Using ref for individual properties
+// Using ref for individual properties (does NOT keep linkage; shown for rule coverage)
-    const foo = ref(props.foo);
+    const foo = toRef(props, 'foo');

1-43: Add a namespaced Vue.toRefs positive case

To validate namespaced helpers are treated as safe, consider adding a case with Vue.toRefs(props).

+// Namespaced toRefs
+import * as Vue from 'vue';
+export default {
+  setup(props) {
+    const { foo } = Vue.toRefs(props);
+    return () => h('div', foo.value)
+  }
+}
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (2)

206-207: Tiny copy tweak: mention toRef alongside toRefs

The note suggests toRefs only. Add toRef for single‑prop cases.

-                    "Use 'props.propertyName' or 'toRefs(props)' to maintain reactivity."
+                    "Use 'props.propertyName', 'toRef(props, \"name\")', or 'toRefs(props)' to maintain reactivity."

302-306: Optional: support computed member name ["setup"]

Some codebases use computed literal keys: "setup" via ["setup"]. Low frequency, but easy to support.

 fn is_member_named_setup(name: &biome_js_syntax::AnyJsObjectMemberName) -> bool {
-    name.as_js_literal_member_name()
-        .and_then(|literal| literal.name().ok())
-        .is_some_and(|token| token.text() == "setup")
+    if let Some(literal) = name.as_js_literal_member_name() {
+        return literal.name().ok().is_some_and(|token| token.text() == "setup");
+    }
+    if let Some(comp) = name.as_js_computed_member_name() {
+        if let Ok(expr) = comp.expression() {
+            if let Some(str_lit) = expr.as_js_string_literal_expression() {
+                return str_lit.value_token().ok().is_some_and(|t| t.text_trimmed() == "setup");
+            }
+        }
+    }
+    false
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4e92f8f and dd5327c.

⛔ Files ignored due to path filters (14)
  • crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs is excluded by !**/migrate/eslint_any_rule_to_biome.rs and included by **
  • crates/biome_configuration/src/analyzer/linter/rules.rs is excluded by !**/rules.rs and included by **
  • crates/biome_diagnostics_categories/src/categories.rs is excluded by !**/categories.rs and included by **
  • crates/biome_js_analyze/src/lint/nursery.rs is excluded by !**/nursery.rs and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-typescript.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-nested-contexts.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-typescript.ts.snap is excluded by !**/*.snap and included by **
  • packages/@biomejs/backend-jsonrpc/src/workspace.ts is excluded by !**/backend-jsonrpc/src/workspace.ts and included by **
  • packages/@biomejs/biome/configuration_schema.json is excluded by !**/configuration_schema.json and included by **
📒 Files selected for processing (12)
  • .changeset/grumpy-pigs-chew.md (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-typescript.ts (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-nested-contexts.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-typescript.ts (1 hunks)
  • crates/biome_rule_options/src/lib.rs (1 hunks)
  • crates/biome_rule_options/src/no_vue_setup_props_reactivity_loss.rs (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-typescript.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/biome_rule_options/src/lib.rs
🧰 Additional context used
📓 Path-based instructions (5)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-nested-contexts.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-typescript.ts
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js
  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-nested-contexts.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-typescript.ts
  • crates/biome_rule_options/src/no_vue_setup_props_reactivity_loss.rs
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js
  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js
**/tests/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place test files under a tests/ directory in each crate

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-nested-contexts.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-typescript.ts
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Format all Rust source files before committing (just f)

Files:

  • crates/biome_rule_options/src/no_vue_setup_props_reactivity_loss.rs
  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
.changeset/*.md

📄 CodeRabbit inference engine (CONTRIBUTING.md)

.changeset/*.md: In changeset files, only use #### or ##### headers; avoid other header levels
Changeset descriptions should use past tense for what you did (e.g., "Added...")
Describe current Biome behavior in present tense within changesets (e.g., "Biome now supports...")
For bug fixes in changesets, start with a link to the issue (e.g., "Fixed #1234: ...")
When referencing rules or assists in changesets, include links to their documentation pages
Include a minimal code block in the changeset when applicable to demonstrate the change
End every sentence in the changeset description with a period

Files:

  • .changeset/grumpy-pigs-chew.md
🧠 Learnings (2)
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/crates/biome_rule_options/lib/**/*.rs : Options types must implement serialization/deserialization and schema support using derives: `Serialize`, `Deserialize`, `Deserializable`, and `#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]`

Applied to files:

  • crates/biome_rule_options/src/no_vue_setup_props_reactivity_loss.rs
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/**/src/{lint,assist}/**/*.rs : In declare_lint_rule! macros, set `version: "next"` for new or updated rules

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
🧬 Code graph analysis (7)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-nested-contexts.js (4)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js (4)
  • props (7-7)
  • props (18-18)
  • props (31-31)
  • props (44-44)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js (4)
  • props (6-6)
  • props (14-14)
  • props (39-39)
  • foo (30-30)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js (2)
  • foo (21-21)
  • foo (35-35)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (2)
  • foo (24-24)
  • foo (40-40)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-typescript.ts (5)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js (4)
  • props (7-7)
  • props (18-18)
  • props (31-31)
  • props (44-44)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js (4)
  • props (6-6)
  • props (14-14)
  • props (39-39)
  • foo (30-30)
crates/biome_js_analyze/tests/specs/suspicious/noRedeclare/valid-declaration-merging.ts (1)
  • h (131-133)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js (2)
  • foo (21-21)
  • foo (35-35)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (2)
  • foo (24-24)
  • foo (40-40)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js (3)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js (4)
  • props (6-6)
  • props (14-14)
  • props (39-39)
  • foo (30-30)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js (2)
  • foo (21-21)
  • foo (35-35)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (2)
  • foo (24-24)
  • foo (40-40)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (3)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js (4)
  • props (7-7)
  • props (18-18)
  • props (31-31)
  • props (44-44)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js (4)
  • props (6-6)
  • props (14-14)
  • props (39-39)
  • foo (30-30)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js (2)
  • foo (21-21)
  • foo (35-35)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (2)
crates/biome_analyze/src/rule.rs (1)
  • inspired (249-254)
crates/biome_js_syntax/src/expr_ext.rs (1)
  • callee (32-37)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js (1)
crates/biome_js_analyze/tests/specs/suspicious/noRedeclare/valid-declaration-merging.ts (1)
  • h (131-133)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js (3)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js (4)
  • props (7-7)
  • props (18-18)
  • props (31-31)
  • props (44-44)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js (2)
  • foo (21-21)
  • foo (35-35)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (2)
  • foo (24-24)
  • foo (40-40)
🔇 Additional comments (6)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js (1)

1-37: LGTM: solid coverage of non‑Vue contexts.

Scenarios look right for ensuring we don’t over‑match. Nothing to fix here.

crates/biome_rule_options/src/no_vue_setup_props_reactivity_loss.rs (2)

1-6: Derives and serde config look correct.

Matches our options‑type requirements (Serialize/Deserialize/Deserializable + optional JsonSchema). Nice one.


6-6: Add short doc comment to NoVueSetupPropsReactivityLossOptions

Tiny docstring helps discoverability in generated docs.

Apply this diff:

-pub struct NoVueSetupPropsReactivityLossOptions {}
+/// Options for `vue/noSetupPropsReactivityLoss`. This rule currently has no options.
+pub struct NoVueSetupPropsReactivityLossOptions {}

Verify the public module export exists in crates/biome_rule_options/src/lib.rs:

rg -nP '^\s*pub\s+mod\s+no_vue_setup_props_reactivity_loss' crates/biome_rule_options/src/lib.rs

No matches were found here — if the rule slug was renamed to noSetupPropsReactivityLoss, align the type/module/file names and add the pub mod export.

.changeset/grumpy-pigs-chew.md (1)

5-5: Keep existing slug; fix spacing in the changeset.

The repo defines the rule as noVueSetupPropsReactivityLoss in source and tests — do not rename the slug; just add the missing space in the changeset.

Locations:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (name: "noVueSetupPropsReactivityLoss")
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js.snap

Apply this diff:

-Added the nursery rule`vue/noVueSetupPropsReactivityLoss`.
+Added the nursery rule `vue/noVueSetupPropsReactivityLoss`.

Likely an incorrect or invalid review comment.

crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (1)

13-19: Looks good: toRefs pattern correctly treated as safe

This aligns with the rule’s intent and mirrors Vue guidance. No changes needed.

crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (1)

118-126: Rule metadata LGTM

Version set to “next” and source attribution via inspired() look correct.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dd5327c and d21c8c6.

⛔ Files ignored due to path filters (4)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-typescript.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_unicode_table/src/tables.rs is excluded by !**/biome_unicode_table/src/tables.rs and included by **
📒 Files selected for processing (1)
  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Format all Rust source files before committing (just f)

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
🧠 Learnings (2)
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/**/src/{lint,assist}/**/*.rs : In declare_lint_rule! macros, set `version: "next"` for new or updated rules

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/crates/biome_js_analyze/lib/src/{lint,assist}/**/*.rs : When banning globals (e.g., `noConsoleLog`), check the semantic model to avoid false positives from locally shadowed bindings

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
🧬 Code graph analysis (1)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (2)
crates/biome_analyze/src/rule.rs (4)
  • domains (584-587)
  • recommended (554-557)
  • sources (569-572)
  • inspired (249-254)
crates/biome_js_syntax/src/expr_ext.rs (1)
  • callee (32-37)
🔇 Additional comments (6)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (6)

1-13: Clean imports and good framework integration.

Properly using the existing Vue framework infrastructure. The semantic model integration looks correct.


15-126: Comprehensive rule documentation with excellent examples.

The rustdoc is thorough, covering all major scenarios with clear invalid/valid examples. Good adherence to the learned pattern of setting version: "next".


214-260: Solid component detection logic.

Good coverage across different Vue component patterns (Options API, defineComponent, createApp). The filtering by "setup" name is correct.


590-607: Proper parameter destructuring detection.

Correctly identifies both object and array binding patterns in setup parameters, with appropriate fallback to identifier binding analysis.


352-364: Fix broken root-scope read detection.

The cast is applied to the parent node rather than the reference itself, causing the filter to always fail. Only toRefs(props) makes object destructuring safe, so missing these detections is critical.

Apply this fix:

-    for reference in model.as_binding(props_binding).all_reads() {
-        if let Some(identifier) =
-            biome_js_syntax::JsReferenceIdentifier::cast(reference.syntax().clone())
+    for reference in model.as_binding(props_binding).all_reads() {
+        if let Some(identifier) =
+            biome_js_syntax::JsReferenceIdentifier::cast(reference.syntax().clone())

531-588: Over-permissive reactive API validation needs tightening.

The current implementation treats reactive, ref, and toRef as safe, but only toRefs(props) makes object destructuring safe. Also missing support for namespaced calls like Vue.toRefs().

Restrict to only accept toRefs from Vue packages and handle static member expressions:

-fn is_reactive_api_call(expr: &AnyJsExpression, model: &SemanticModel) -> bool {
+fn is_reactive_api_call(expr: &AnyJsExpression, model: &SemanticModel) -> bool {
     let Some(call_expr) = expr.as_js_call_expression() else {
         return false;
     };

     let Ok(callee) = call_expr.callee() else {
         return false;
     };

-    let Some(ident_expr) = callee.as_js_identifier_expression() else {
-        return false;
-    };
-
-    let Ok(name) = ident_expr.name() else {
-        return false;
-    };
-
-    let Ok(token) = name.value_token() else {
-        return false;
-    };
-
-    let function_name = token.text_trimmed();
-
-    let is_vue_reactive_function = matches!(function_name, "toRefs" | "toRef" | "reactive" | "ref");
-
-    if !is_vue_reactive_function {
-        return false;
-    }
+    // Support both bare calls (toRefs) and namespaced calls (Vue.toRefs)
+    let (function_name, identifier_to_check) = match &callee {
+        AnyJsExpression::JsIdentifierExpression(ident_expr) => {
+            let name = ident_expr.name().ok()?.value_token().ok()?;
+            (name.text_trimmed(), ident_expr)
+        }
+        AnyJsExpression::JsStaticMemberExpression(member_expr) => {
+            let prop = member_expr.member().ok()?.value_token().ok()?;
+            let obj = member_expr.object().ok()?.as_js_identifier_expression()?;
+            (prop.text_trimmed(), obj)
+        }
+        _ => return false,
+    };
+
+    // Only toRefs preserves reactivity for object destructuring
+    if function_name != "toRefs" {
+        return false;
+    }

-    let Some(name_node) = ident_expr.name().ok() else {
-        return true;
-    };
+    let Some(name_node) = identifier_to_check.name().ok() else {
+        return false;
+    };

     let Some(binding) = model.binding(&name_node) else {
-        return true;
+        return false;
     };

     if !binding.is_imported() {
-        return true;
+        return false;
     };

     let binding_node = binding.syntax();

     for ancestor in binding_node.ancestors() {
         let Some(import_decl) = biome_js_syntax::JsImport::cast(ancestor) else {
             continue;
         };
         let Ok(source) = import_decl.source_text() else {
             continue;
         };
         let source_value = source.text();
         return source_value == "vue"
             || source_value == "@vue/reactivity"
             || source_value == "@vue/composition-api";
     }

-    true
+    false
 }

@AsherDe AsherDe force-pushed the feat/vue-reactivity-rule branch from 87c701b to d813736 Compare September 23, 2025 04:02
@AsherDe AsherDe requested a review from dyc3 September 23, 2025 04:02
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (5)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-wrong-package-import.js (1)

1-9: Add a couple of variants to harden import‑origin checks (optional)

Consider adding siblings to catch edge cases:

  • Namespaced wrong origin
  • Aliased wrong origin
  • CommonJS require

Examples:

// invalid-wrong-namespace-import.js
import * as NotVue from 'some-other-library';
export default { setup(props) { const { bar } = NotVue.toRefs(props); return () => h('div', bar.value) } };
// invalid-wrong-alias-import.js
import { toRefs as tr } from 'some-other-library';
export default { setup(props) { const { bar } = tr(props); return () => h('div', bar.value) } };
// invalid-wrong-package-require.js
const { toRefs } = require('some-other-library');
module.exports = { default: { setup(props) { const { bar } = toRefs(props); return () => h('div', bar.value) } } };
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js (1)

17-29: Optional: add a “nested + alias + default” parameter pattern

A tiny extra guardrail: e.g. setup({ user: { name: n = 'x' }, foo: f = 1 }) {} to ensure nested patterns are flagged as well.

crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js (1)

26-38: Optional: add an else branch or ternary example

E.g. destructure in an else {} or const x = cond ? ({ foo } = props) : 0; to harden edge cases.

crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (2)

262-272: Parenthesised functions aren’t unwrapped

export default (props) => {} or setup: ((props) => {}) won’t be detected because JsParenthesizedExpression isn’t handled.

Apply this diff to unwrap parentheses recursively:

 fn extract_function_from_expression(expr: &AnyJsExpression) -> Option<AnyJsFunction> {
     match expr {
         AnyJsExpression::JsFunctionExpression(func_expr) => {
             Some(AnyJsFunction::JsFunctionExpression(func_expr.clone()))
         }
         AnyJsExpression::JsArrowFunctionExpression(arrow_func) => {
             Some(AnyJsFunction::JsArrowFunctionExpression(arrow_func.clone()))
         }
+        AnyJsExpression::JsParenthesizedExpression(paren) => {
+            let inner = paren.expression().ok()?;
+            extract_function_from_expression(&inner)
+        }
         _ => None,
     }
 }

204-210: Nudge: mention toRef for single prop in note

The note suggests toRefs(props) only. Add “or toRef(props, 'name') for a single prop” to align with the docs/examples below.

- "Use 'props.propertyName' or 'toRefs(props)' to maintain reactivity."
+ "Use 'props.propertyName' or 'toRefs(props)' (or 'toRef(props, \"name\")' for a single prop) to maintain reactivity."
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 87c701b and d813736.

⛔ Files ignored due to path filters (18)
  • crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs is excluded by !**/migrate/eslint_any_rule_to_biome.rs and included by **
  • crates/biome_configuration/src/analyzer/linter/rules.rs is excluded by !**/rules.rs and included by **
  • crates/biome_diagnostics_categories/src/categories.rs is excluded by !**/categories.rs and included by **
  • crates/biome_js_analyze/src/lint/nursery.rs is excluded by !**/nursery.rs and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-local-function.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-namespaced-call.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-typescript.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-unimported-toRefs.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-wrong-package-import.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-nested-contexts.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-typescript.ts.snap is excluded by !**/*.snap and included by **
  • packages/@biomejs/backend-jsonrpc/src/workspace.ts is excluded by !**/backend-jsonrpc/src/workspace.ts and included by **
  • packages/@biomejs/biome/configuration_schema.json is excluded by !**/configuration_schema.json and included by **
📒 Files selected for processing (16)
  • .changeset/grumpy-pigs-chew.md (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-local-function.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-namespaced-call.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-typescript.ts (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-unimported-toRefs.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-wrong-package-import.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-nested-contexts.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-typescript.ts (1 hunks)
  • crates/biome_rule_options/src/lib.rs (1 hunks)
  • crates/biome_rule_options/src/no_vue_setup_props_reactivity_loss.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (12)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-unimported-toRefs.js
  • .changeset/grumpy-pigs-chew.md
  • crates/biome_rule_options/src/lib.rs
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-namespaced-call.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-local-function.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-typescript.ts
  • crates/biome_rule_options/src/no_vue_setup_props_reactivity_loss.rs
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-nested-contexts.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-typescript.ts
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js
🧰 Additional context used
📓 Path-based instructions (4)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-wrong-package-import.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js
  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-wrong-package-import.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js
  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
**/tests/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place test files under a tests/ directory in each crate

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-wrong-package-import.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Format all Rust source files before committing (just f)

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
🧠 Learnings (2)
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/**/src/{lint,assist}/**/*.rs : In declare_lint_rule! macros, set `version: "next"` for new or updated rules

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/crates/biome_js_analyze/lib/src/{lint,assist}/**/*.rs : When banning globals (e.g., `noConsoleLog`), check the semantic model to avoid false positives from locally shadowed bindings

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
🧬 Code graph analysis (3)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js (4)
crates/biome_js_analyze/tests/specs/suspicious/noRedeclare/valid-declaration-merging.ts (1)
  • h (131-133)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (2)
  • foo (25-25)
  • foo (41-41)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js (1)
  • foo (30-30)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js (2)
  • foo (21-21)
  • foo (35-35)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-wrong-package-import.js (3)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-local-function.js (1)
  • toRefs (8-8)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-unimported-toRefs.js (1)
  • toRefs (4-4)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (1)
  • toRefs (17-17)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js (3)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js (4)
  • props (6-6)
  • props (14-14)
  • props (39-39)
  • foo (30-30)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (2)
  • foo (25-25)
  • foo (41-41)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js (2)
  • foo (21-21)
  • foo (35-35)
🔇 Additional comments (7)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-wrong-package-import.js (3)

2-2: Good negative: wrong‑package import should be flagged

This neatly asserts that only Vue’s own toRefs (or explicitly allowed sources) are exempt. Solid fixture.


6-6: Covers root‑scope destructuring path

Destructuring happens at setup’s root (not a nested fn), so it exercises the rule’s primary failure mode. All good.


6-6: Import-origin resolution for toRefs/namespace: confirmed

is_reactive_api_call and is_vue_namespace_or_import in crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (≈lines 531–625) inspect binding.is_imported(), find the import declaration and compare the import source (e.g. "@vue/reactivity"), so the rule does not blindly whitelist unknown packages.

crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js (1)

3-43: Solid coverage of parameter destructuring cases

Covers defaults, renames, rest, defineComponent and named export variants. Looks good.

crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js (1)

3-49: Good spread across control‑flow constructs

if/try/switch/for are covered nicely and align with the rule’s “root‑scope” focus.

crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (2)

349-367: Root‑scope read/destructure logic looks sound — verified

Ran the suggested rg; tests include invalid conditional destructurings and valid nested contexts, confirming root‑scope matching behaves as expected.


214-260: Minor: unify Setup detection to avoid second pass

Handle AnyVueComponent::Setup inside find_setup_functions so setup() is discovered in one pass and avoid duplicating diagnostics from check_additional_setup_patterns.

Couldn't verify here — ripgrep returned "No files were searched". Run locally to confirm there are no duplicated diagnostics after the change:

rg -n --hidden --glob '!**/target/**' 'NoVueSetupPropsReactivityLoss'

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (3)

467-472: Range-based containment is a bit coarse

Using ranges to test “props within initializer” can misfire on edge cases with overlapping trivia/comments. Ancestor checks are more precise.

Consider replacing with a structural check (walk the initializer AST to find the reference node) instead of range containment.


711-728: Export‑default function fallback broadens scope unnecessarily

Checking export default (() => …) as a “setup” increases false positives. With the earlier gating change, this can be removed.


540-588: Approve — toRefs check matches eslint-plugin-vue; consider adding toRef and import normalisation

Upstream treats toRef/toRefs as the safe helpers; your restriction to Vue-imported toRefs is aligned.

  • Recognise toRef (singular) in is_reactive_api_call so single-prop conversions are also treated as safe.
  • Optional: normalise import-source matching to include monorepo/alias paths (e.g. 'vue/dist/*') if your workspace uses them; otherwise keep strict matching to avoid false positives.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d813736 and 27c687d.

📒 Files selected for processing (1)
  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Format all Rust source files before committing (just f)

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
🧠 Learnings (2)
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/**/src/{lint,assist}/**/*.rs : In declare_lint_rule! macros, set `version: "next"` for new or updated rules

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/crates/biome_js_analyze/lib/src/{lint,assist}/**/*.rs : When banning globals (e.g., `noConsoleLog`), check the semantic model to avoid false positives from locally shadowed bindings

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
🧬 Code graph analysis (1)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (2)
crates/biome_analyze/src/rule.rs (1)
  • inspired (249-254)
crates/biome_js_syntax/src/expr_ext.rs (1)
  • callee (32-37)
🔇 Additional comments (6)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (6)

118-125: Rule metadata looks spot‑on

version: "next", Vue domain, and source mapping are correctly declared.


296-310: Nice: handles identifier and quoted “setup”

Good coverage for setup(props) {} and "setup"() {}.


354-376: Root-scope read detection looks correct

Casts the reference node (not parent), checks nearest function ancestor equals setup. This resolves the earlier bug.


175-211: Diagnostic guidance is clear and actionable

Messages and notes guide users towards props.property and toRefs(props).


23-27: Docs: don’t suggest toRef for destructuring

Only toRefs(props) makes object destructuring safe. Mentioning toRef here is misleading.

Apply this diff:

-    /// - Destructuring assignment of props in the root scope of setup (unless using `toRefs` or `toRef`)
+    /// - Destructuring assignment of props in the root scope of setup (unless using `toRefs`)

149-173: Limit analysis to recognised Vue components to avoid false positives

When VueComponent::from_potential_component returns None, we still lint via the “plain JS” paths, which can flag non‑Vue files. Gate the rule to recognised components only.

Apply this diff:

-        match component {
-            Some(component) => {
-                for setup_function in find_setup_functions(&component) {
-                    violations.extend(check_setup_function(&setup_function, ctx.model()));
-                }
-            }
-            None => {
-                violations.extend(check_plain_js_setup_functions(ctx.query(), ctx.model()));
-            }
-        }
-
-        violations.extend(check_additional_setup_patterns(ctx.query(), ctx.model()));
+        if let Some(component) = component {
+            for setup_function in find_setup_functions(&component) {
+                violations.extend(check_setup_function(&setup_function, ctx.model()));
+            }
+        }

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (5)

24-27: Docs: tighten wording around safe exception

Object destructuring is only safe when destructuring the result of toRefs(props). toRef doesn’t involve object destructuring.

-    /// - Destructuring assignment of props in the root scope of setup (unless using `toRefs` or `toRef`)
+    /// - Destructuring assignment of props in the root scope of setup (unless destructuring from `toRefs(props)`)

208-209: Docs: mention toRef for single-property access

Small clarity boost for users preferring toRef for one prop.

-                    "Use 'props.propertyName' or 'toRefs(props)' to maintain reactivity."
+                    "Use 'props.propertyName', 'toRefs(props)', or 'toRef(props, \"key\")' to maintain reactivity."

214-260: Avoid duplicate setup discovery across APIs

Collecting from both *_api.setup_func() and iter_declaration_groups() risks double-reporting the same function.

     match component.kind() {
         AnyVueComponent::OptionsApi(options_api) => {
             if let Some(setup_func_expr) = options_api.setup_func()
                 && let Some(func) = extract_function_from_expression(&setup_func_expr)
             {
                 setup_functions.push(SetupFunction::Function(func));
             }
@@
     }
-
-    setup_functions
+    {
+        use std::collections::HashSet;
+        let mut seen: HashSet<TextRange> = HashSet::new();
+        let mut deduped = Vec::new();
+        for f in setup_functions {
+            let range = match &f {
+                SetupFunction::Function(func) => func.range(),
+                SetupFunction::Method(method) => method.range(),
+            };
+            if seen.insert(range) {
+                deduped.push(f);
+            }
+        }
+        deduped
+    }

Run your snapshots to confirm diagnostics don’t double-fire.


147-148: Align rule Options with the published options type (if present)

The PR summary mentions NoVueSetupPropsReactivityLossOptions. If exported, wire it here instead of ().

-    type Options = ();
+    type Options = biome_rule_options::NoVueSetupPropsReactivityLossOptions;

If the options type isn’t actually used, consider removing it from biome_rule_options to avoid dead API.


531-579: Support Vue["toRefs"](...) and similar computed-member forms

Currently only identifier and static member calls are recognised. Computed string member access is common in some codebases/tools.

     match &callee {
         AnyJsExpression::JsIdentifierExpression(ident_expr) => {
             …
         }
         AnyJsExpression::JsStaticMemberExpression(member_expr) => {
             …
         }
+        AnyJsExpression::JsComputedMemberExpression(comp_member) => {
+            let Ok(prop_expr) = comp_member.member() else { return false; };
+            let is_to_refs = prop_expr
+                .as_js_string_literal_expression()
+                .and_then(|s| s.value_token().ok())
+                .is_some_and(|tok| tok.text_trimmed() == "toRefs");
+            if !is_to_refs {
+                return false;
+            }
+            let Ok(object) = comp_member.object() else { return false; };
+            is_vue_namespace_or_import(&object, model)
+        }
         _ => false,
     }

Optional: normalise expr to skip parentheses and TS casts before matching (e.g., toRefs(props) as const). If there’s a helper like omit_parentheses()/cast skipping in your expr extensions, use it when extracting callee.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 27c687d and 9786499.

📒 Files selected for processing (1)
  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Format all Rust source files before committing (just f)

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
🧠 Learnings (1)
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/crates/biome_js_analyze/lib/src/{lint,assist}/**/*.rs : When banning globals (e.g., `noConsoleLog`), check the semantic model to avoid false positives from locally shadowed bindings

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
🧬 Code graph analysis (1)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (2)
crates/biome_analyze/src/rule.rs (4)
  • domains (584-587)
  • recommended (554-557)
  • sources (569-572)
  • inspired (249-254)
crates/biome_js_syntax/src/expr_ext.rs (1)
  • callee (32-37)
🔇 Additional comments (5)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (5)

671-688: Heuristic guard for plain call expressions

If VueComponentQuery already gates these, ignore this. Otherwise, guard on callee resolving to defineComponent/createApp imported from Vue to avoid non‑Vue false positives.

 fn extract_setup_from_call_expression(
     call_expr: &biome_js_syntax::JsCallExpression,
     model: &SemanticModel,
 ) -> Vec<Violation> {
+    // Optional guard:
+    // if !callee_is_vue_factory(call_expr, model) { return Vec::new(); }

Happy to sketch callee_is_vue_factory if you want it.


352-364: Good fix on reference casting; root-scope reads are now detected

Casting the reference node itself (not its parent) resolves the earlier false negative.


369-381: Root-scope detection logic looks sound

Nearest function ancestor equals the setup function node; nested callbacks are excluded as intended.


521-529: Safety check limited to toRefs is correct

Only toRefs(props) preserves reactivity for object destructuring; others (toRef, reactive, ref) should not whitelist destructuring.

Add/keep tests for:

  • const { x } = reactive(props)
  • const { x } = toRef(props, "x") (should be flagged)
  • const { x } = Vue["toRefs"](props) (should be allowed once implemented)

296-301: Fix: recognise identifier-named setup methods (not only quoted)

Methods written as setup(props) {} are currently missed because only literal names are handled.

Apply:

-fn is_member_named_setup(name: &biome_js_syntax::AnyJsObjectMemberName) -> bool {
-    if let Some(lit) = name.as_js_literal_member_name() {
-        return lit.value().is_ok_and(|tok| tok.text_trimmed() == "setup");
-    }
-    false
-}
+fn is_member_named_setup(name: &biome_js_syntax::AnyJsObjectMemberName) -> bool {
+    if let Some(id) = name.as_js_identifier_member_name() {
+        return id.value_token().is_ok_and(|tok| tok.text_trimmed() == "setup");
+    }
+    if let Some(lit) = name.as_js_literal_member_name() {
+        return lit.name().is_ok_and(|tok| tok.text_trimmed() == "setup");
+    }
+    false
+}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (2)

223-251: Over-filtering by raw text "setup" drops quoted members; let the helper decide

The pre-filter filter(|(name, _)| name.text() == "setup") misses "setup" (quoted) and duplicates logic already handled by is_member_named_setup. This causes false negatives.

Apply this diff to rely solely on extract_setup_from_object_member:

-            setup_functions.extend(
-                options_api
-                    .iter_declaration_groups()
-                    .filter(|(name, _)| name.text() == "setup")
-                    .filter_map(|(_, member)| extract_setup_from_object_member(&member)),
-            );
+            setup_functions.extend(
+                options_api
+                    .iter_declaration_groups()
+                    .filter_map(|(_, member)| extract_setup_from_object_member(&member)),
+            );
@@
-            setup_functions.extend(
-                define_component
-                    .iter_declaration_groups()
-                    .filter(|(name, _)| name.text() == "setup")
-                    .filter_map(|(_, member)| extract_setup_from_object_member(&member)),
-            );
+            setup_functions.extend(
+                define_component
+                    .iter_declaration_groups()
+                    .filter_map(|(_, member)| extract_setup_from_object_member(&member)),
+            );
@@
-            setup_functions.extend(
-                create_app
-                    .iter_declaration_groups()
-                    .filter(|(name, _)| name.text() == "setup")
-                    .filter_map(|(_, member)| extract_setup_from_object_member(&member)),
-            );
+            setup_functions.extend(
+                create_app
+                    .iter_declaration_groups()
+                    .filter_map(|(_, member)| extract_setup_from_object_member(&member)),
+            );

702-746: Optional: further guard plain-call path

If you ever see false positives here, additionally gate on known Vue factories (defineComponent/createApp) via semantic resolution. Current query likely suffices, so this is optional.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9786499 and 5b7688c.

⛔ Files ignored due to path filters (9)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-local-function.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-namespaced-call.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-typescript.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-unimported-toRefs.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-wrong-package-import.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (2)
  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Format all Rust source files before committing (just f)

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
**/tests/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place test files under a tests/ directory in each crate

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js
🧠 Learnings (1)
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/crates/biome_js_analyze/lib/src/{lint,assist}/**/*.rs : When banning globals (e.g., `noConsoleLog`), check the semantic model to avoid false positives from locally shadowed bindings

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
🧬 Code graph analysis (2)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (2)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (1)
  • Vue (51-51)
crates/biome_js_syntax/src/expr_ext.rs (1)
  • callee (32-37)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (5)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js (4)
  • props (6-6)
  • props (14-14)
  • props (39-39)
  • foo (30-30)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-unimported-toRefs.js (1)
  • toRefs (4-4)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-wrong-package-import.js (1)
  • toRefs (6-6)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-local-function.js (1)
  • toRefs (8-8)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js (2)
  • foo (21-21)
  • foo (35-35)
🔇 Additional comments (3)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (1)

1-55: LGTM: solid coverage of safe patterns (incl. namespaced/computed).

No diagnostics expected; aligns with rule intent.

crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (2)

306-324: setup() detection misses identifier member names

is_member_named_setup only handles literal names; it skips the common setup(props) {} identifier form, causing false negatives (esp. in createApp/defineComponent member scanning).

Apply this diff to support both identifier and literal member names:

-fn is_member_named_setup(name: &biome_js_syntax::AnyJsObjectMemberName) -> bool {
-    match name {
-        biome_js_syntax::AnyJsObjectMemberName::JsLiteralMemberName(lit) => {
-            lit.value().is_ok_and(|tok| {
-                let text = tok.text_trimmed();
-                if (text.starts_with('"') && text.ends_with('"'))
-                    || (text.starts_with('\'') && text.ends_with('\''))
-                {
-                    let inner = &text[1..text.len() - 1];
-                    inner == "setup"
-                } else {
-                    text == "setup"
-                }
-            })
-        }
-        biome_js_syntax::AnyJsObjectMemberName::JsComputedMemberName(_) => false,
-        biome_js_syntax::AnyJsObjectMemberName::JsMetavariable(_) => false,
-    }
-}
+fn is_member_named_setup(name: &biome_js_syntax::AnyJsObjectMemberName) -> bool {
+    if let Some(id) = name.as_js_identifier_member_name() {
+        return id
+            .value_token()
+            .is_ok_and(|tok| tok.text_trimmed() == "setup");
+    }
+    if let Some(lit) = name.as_js_literal_member_name() {
+        return lit.value().is_ok_and(|tok| {
+            let text = tok.text_trimmed();
+            if (text.starts_with('"') && text.ends_with('"'))
+                || (text.starts_with('\'') && text.ends_with('\''))
+            {
+                &text[1..text.len() - 1] == "setup"
+            } else {
+                text == "setup"
+            }
+        });
+    }
+    false
+}

554-637: Only treat toRefs(...) as safe for destructuring (remove toRef)

For object/array destructuring, only toRefs(props) preserves reactivity. Allowing toRef here creates false negatives (e.g., const { value } = toRef(props, 'k')).

Apply this diff to restrict the safe callee to toRefs:

-            if function_name != "toRefs" && function_name != "toRef" {
+            if function_name != "toRefs" {
                 return false;
             }
@@
-            if property_name != "toRefs" && property_name != "toRef" {
+            if property_name != "toRefs" {
                 return false;
             }
@@
-                        property_name == "toRefs" || property_name == "toRef"
+                        property_name == "toRefs"

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (2)

746-763: Guard plain-call path to Vue factories to avoid non‑Vue false positives

This path inspects any call whose first arg is an object, which can catch unrelated code. Guard on defineComponent/createApp (including namespaced forms) imported from vue.

 fn extract_setup_from_call_expression(
     call_expr: &biome_js_syntax::JsCallExpression,
     model: &SemanticModel,
 ) -> Vec<Violation> {
+    // Ensure this is a Vue factory before inspecting its first argument
+    if let Ok(callee) = call_expr.callee() {
+        if !is_vue_factory_callee(&callee, model) {
+            return Vec::new();
+        }
+    } else {
+        return Vec::new();
+    }

Add helper:

fn is_vue_factory_callee(callee: &AnyJsExpression, model: &SemanticModel) -> bool {
    use AnyJsExpression::*;
    match callee {
        JsIdentifierExpression(id) => {
            let Ok(name) = id.name() else { return false; };
            let Ok(tok) = name.value_token() else { return false; };
            let n = tok.text_trimmed();
            if n != "defineComponent" && n != "createApp" {
                return false;
            }
            model
                .binding(&name)
                .is_some_and(|b| is_vue_reactive_function_binding(&b))
        }
        JsStaticMemberExpression(m) => {
            let Ok(member) = m.member() else { return false; };
            let Ok(prop_tok) = member.value_token() else { return false; };
            let n = prop_tok.text_trimmed();
            if n != "defineComponent" && n != "createApp" {
                return false;
            }
            let Ok(obj) = m.object() else { return false; };
            is_vue_namespace_or_import(&obj, model)
        }
        JsComputedMemberExpression(m) => {
            let Ok(mem_expr) = m.member() else { return false; };
            let name = if let Some(lit) = mem_expr.as_js_string_literal_expression() {
                lit.value_token().ok().map(|t| t.text_trimmed())
            } else {
                None
            };
            if name != Some("defineComponent") && name != Some("createApp") {
                return false;
            }
            let Ok(obj) = m.object() else { return false; };
            is_vue_namespace_or_import(&obj, model)
        }
        _ => false,
    }
}

142-147: Consider wiring the dedicated options type

If NoVueSetupPropsReactivityLossOptions was added in biome_rule_options, wire it here for parity with other rules. Keeps surface consistent even if empty for now.

-    type Options = ();
+    type Options = biome_rule_options::NoVueSetupPropsReactivityLossOptions;
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5b7688c and 524d895.

📒 Files selected for processing (1)
  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Format all Rust source files before committing (just f)

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
🧠 Learnings (1)
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/crates/biome_js_analyze/lib/src/{lint,assist}/**/*.rs : When banning globals (e.g., `noConsoleLog`), check the semantic model to avoid false positives from locally shadowed bindings

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
🧬 Code graph analysis (1)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (3)
crates/biome_analyze/src/rule.rs (4)
  • domains (584-587)
  • recommended (554-557)
  • sources (569-572)
  • inspired (249-254)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (1)
  • Vue (51-51)
crates/biome_js_syntax/src/expr_ext.rs (1)
  • callee (32-37)
🔇 Additional comments (3)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (3)

409-421: Root-scope check looks solid

The nearest-function ancestor equality check cleanly excludes nested callbacks/render functions.

Please add/keep tests that cover:

  • destructuring inside watch/returned render (not reported),
  • destructuring in nested arrow/function inside setup (not reported).

305-341: Detect setup with identifier member names too

is_member_named_setup misses the very common setup(props) {} and setup: (props) => {} cases because it only checks literal/computed names. Add identifier handling.

 fn is_member_named_setup(name: &biome_js_syntax::AnyJsObjectMemberName) -> bool {
-    match name {
+    match name {
+        biome_js_syntax::AnyJsObjectMemberName::JsIdentifierMemberName(id) => {
+            id.value_token().is_ok_and(|tok| tok.text_trimmed() == "setup")
+        }
         biome_js_syntax::AnyJsObjectMemberName::JsLiteralMemberName(lit) => {
             lit.value().is_ok_and(|tok| {
                 let text = tok.text_trimmed();
                 if (text.starts_with('"') && text.ends_with('"'))
                     || (text.starts_with('\'') && text.ends_with('\''))
                 {
                     let inner = &text[1..text.len() - 1];
                     inner == "setup"
                 } else {
                     text == "setup"
                 }
             })
         }
         biome_js_syntax::AnyJsObjectMemberName::JsComputedMemberName(computed) => {
             computed.expression().is_ok_and(|expr| {
                 let AnyJsExpression::AnyJsLiteralExpression(literal_expr) = expr else {
                     return false;
                 };
                 let Some(string_literal) = literal_expr.as_js_string_literal_expression() else {
                     return false;
                 };
                 string_literal.value_token().is_ok_and(|tok| {
                     let text = tok.text_trimmed();
                     if text.len() >= 2 {
                         let inner = &text[1..text.len() - 1];
                         inner == "setup"
                     } else {
                         false
                     }
                 })
             })
         }
         biome_js_syntax::AnyJsObjectMemberName::JsMetavariable(_) => false,
     }
 }

571-654: Don’t treat toRef as a safe destructuring source

Only toRefs(props) preserves reactivity for object destructuring. Accepting toRef causes missed violations.

     match &callee {
         AnyJsExpression::JsIdentifierExpression(ident_expr) => {
             let Ok(name) = ident_expr.name() else {
                 return false;
             };
             let Ok(token) = name.value_token() else {
                 return false;
             };
             let function_name = token.text_trimmed();
-            if function_name != "toRefs" && function_name != "toRef" {
+            if function_name != "toRefs" {
                 return false;
             }
 
             if let Some(binding) = model.binding(&name) {
                 is_vue_reactive_function_binding(&binding)
             } else {
                 false
             }
         }
         AnyJsExpression::JsStaticMemberExpression(member_expr) => {
             let Ok(member) = member_expr.member() else {
                 return false;
             };
             let Ok(token) = member.value_token() else {
                 return false;
             };
             let property_name = token.text_trimmed();
-            if property_name != "toRefs" && property_name != "toRef" {
+            if property_name != "toRefs" {
                 return false;
             }
 
             let Ok(object) = member_expr.object() else {
                 return false;
             };
 
             is_vue_namespace_or_import(&object, model)
         }
         AnyJsExpression::JsComputedMemberExpression(computed_member_expr) => {
             let Ok(member) = computed_member_expr.member() else {
                 return false;
             };
 
             let is_reactive_function = match &member {
                 AnyJsExpression::AnyJsLiteralExpression(literal_expr) => {
                     let Some(string_literal) = literal_expr.as_js_string_literal_expression()
                     else {
                         return false;
                     };
                     let Ok(token) = string_literal.value_token() else {
                         return false;
                     };
                     let text = token.text_trimmed();
                     if text.len() >= 2 {
                         let property_name = &text[1..text.len() - 1];
-                        property_name == "toRefs" || property_name == "toRef"
+                        property_name == "toRefs"
                     } else {
                         false
                     }
                 }
                 _ => false,
             };

Copy link
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't review all of it, but there's a lot of that that can be improved. Most important things to address:

  • import types from the top of the file and avoid prefixing crates inconsistently
  • documentation of the functions. It's hard to review them without understanding the business logic
  • avoid let-else when it's not needed, and use if-let chains when possible

Comment on lines 423 to 437
fn is_function_like_node(node: &biome_rowan::SyntaxNode<biome_js_syntax::JsLanguage>) -> bool {
use biome_js_syntax::JsSyntaxKind;
matches!(
node.kind(),
JsSyntaxKind::JS_FUNCTION_DECLARATION
| JsSyntaxKind::JS_FUNCTION_EXPRESSION
| JsSyntaxKind::JS_ARROW_FUNCTION_EXPRESSION
| JsSyntaxKind::JS_METHOD_CLASS_MEMBER
| JsSyntaxKind::JS_METHOD_OBJECT_MEMBER
| JsSyntaxKind::JS_GETTER_CLASS_MEMBER
| JsSyntaxKind::JS_GETTER_OBJECT_MEMBER
| JsSyntaxKind::JS_SETTER_CLASS_MEMBER
| JsSyntaxKind::JS_SETTER_OBJECT_MEMBER
)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we already have a node for this, but I might be wrong. Check for AnyFunctionLike or something similar

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked AnyFunctionLike in function_ext. This function does not contain getters and setters, but returning an object with getters from setup in Vue's setup function is valid and sometimes useful.

@AsherDe
Copy link
Author

AsherDe commented Sep 24, 2025

Thanks a lot for the detailed feedback.

Just for context, this is my first time contributing to a large-scale developer tool like Biome, so it's been more complex than I anticipated. I'm still learning my way around the codebase.

I will work on all the points you've raised—improving the imports, adding documentation, and using more idiomatic control flow. I'll study the existing codebase more closely to better understand the project's patterns.

It might take me a bit longer to get this right. I really appreciate your guidance.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (1)

490-493: Critical: toRef is not safe for object destructuring – only toRefs preserves reactivity.

toRef(props, 'key') returns a single Ref<T>, not an object of refs. Object destructuring from it (e.g., const { x } = toRef(props, 'k')) is nonsensical and shouldn't be treated as safe. Past reviews explicitly flagged this: "Only toRefs(props) makes object destructuring safe."

Apply this diff:

 fn is_reactive_function_name(name: &str) -> bool {
-    name == "toRefs" || name == "toRef"
+    name == "toRefs"
 }
🧹 Nitpick comments (2)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (2)

233-233: Import AnyJsParameter at the top rather than using the fully qualified path.

Per past review feedback, import types at the beginning of the file instead of using biome_js_syntax:: prefixes.

Apply this diff:

 use biome_js_syntax::{
     AnyJsArrowFunctionParameters, AnyJsBindingPattern, AnyJsExpression, AnyJsFunction,
     AnyJsObjectMember, AnyJsObjectMemberName, JsAssignmentExpression, JsCallExpression,
     JsExportDefaultExpressionClause, JsIdentifierBinding, JsImport, JsLanguage,
     JsMethodObjectMember, JsObjectMemberList, JsReferenceIdentifier, JsSyntaxKind,
-    JsVariableDeclarator,
+    JsVariableDeclarator, AnyJsParameter, AnyJsAssignmentPattern,
 };

Then update line 233:

 fn extract_binding_from_formal_parameter(
-    param: &biome_js_syntax::AnyJsParameter,
+    param: &AnyJsParameter,
 ) -> Option<AnyJsBindingPattern> {

And line 461:

             match left {
-                biome_js_syntax::AnyJsAssignmentPattern::JsObjectAssignmentPattern(obj_pattern) => {
+                AnyJsAssignmentPattern::JsObjectAssignmentPattern(obj_pattern) => {
                     Some(DestructuringInfo {
                         destructuring_range: obj_pattern.range(),
                         initializer,
                     })
                 }
-                biome_js_syntax::AnyJsAssignmentPattern::JsArrayAssignmentPattern(
+                AnyJsAssignmentPattern::JsArrayAssignmentPattern(
                     array_pattern,
                 ) => Some(DestructuringInfo {

543-543: Import Binding at the top rather than using the fully qualified path.

Per coding guidelines and past review feedback.

Apply this diff:

+use biome_js_semantic::{Binding, SemanticModel};
-use biome_js_semantic::SemanticModel;

Then update line 543:

-fn is_vue_reactive_function_binding(binding: &biome_js_semantic::Binding) -> bool {
+fn is_vue_reactive_function_binding(binding: &Binding) -> bool {
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c6bd511 and 920978c.

📒 Files selected for processing (1)
  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
**/*.{rs,toml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Before committing, format Rust and TOML files (e.g., via just f/just format)

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Document rules, assists, and options via inline rustdoc in Rust source

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
🧬 Code graph analysis (1)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (4)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (2)
  • RuleDomain (904-912)
  • TextRange (9190-9190)
crates/biome_analyze/src/rule.rs (1)
  • inspired (249-254)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (1)
  • Vue (51-51)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
🔇 Additional comments (1)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (1)

224-228: Verify name() handles both identifier and literal member names correctly.

A past review suggested explicitly checking both as_js_identifier_member_name() and as_js_literal_member_name() to handle setup(props) {} (identifier) and "setup": ... (literal). The current implementation uses a single name() call, which may be a convenience method that handles both variants. If name() is guaranteed to work for all AnyJsObjectMemberName variants, this is fine; otherwise, revert to the explicit checks from the past suggestion.

If uncertain, consider the explicit approach:

fn is_member_named_setup(name: &AnyJsObjectMemberName) -> bool {
    // Identifier: setup(props) {}
    if let Some(id) = name.as_js_identifier_member_name() {
        return id.value_token().is_ok_and(|tok| tok.text_trimmed() == "setup");
    }
    // Literal: "setup": ... or 'setup'() {}
    if let Some(lit) = name.as_js_literal_member_name() {
        return lit.name().is_ok_and(|tok| tok.text_trimmed() == "setup");
    }
    false
}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 83e0156 and 9b7f04d.

⛔ Files ignored due to path filters (8)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-local-function.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-namespaced-call.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-typescript.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-unimported-toRefs.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-wrong-package-import.js.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (1)
  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
**/*.{rs,toml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Before committing, format Rust and TOML files (e.g., via just f/just format)

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Document rules, assists, and options via inline rustdoc in Rust source

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
🧬 Code graph analysis (1)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (2)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (2)
  • RuleDomain (904-912)
  • TextRange (9190-9190)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (1)
  • Vue (51-51)

@ematipico
Copy link
Member

No worries @AsherDe

BTW, you're not forced to get the rule 100% at the first PR. You're actually encouraged to descope the rule, and break it down in multiple PRs if you think it's too much work.

@AsherDe AsherDe closed this Oct 6, 2025
@AsherDe AsherDe force-pushed the feat/vue-reactivity-rule branch from e918990 to 7e15df3 Compare October 6, 2025 07:57
@AsherDe AsherDe reopened this Oct 6, 2025
@AsherDe
Copy link
Author

AsherDe commented Oct 6, 2025

@ematipico I was attempting to merge all commit history and accidentally cleared the remote commit log. I apologize, as you may still be reviewing my pull request.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e918990 and 5c36679.

⛔ Files ignored due to path filters (19)
  • crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs is excluded by !**/migrate/eslint_any_rule_to_biome.rs and included by **
  • crates/biome_configuration/src/analyzer/linter/rules.rs is excluded by !**/rules.rs and included by **
  • crates/biome_diagnostics_categories/src/categories.rs is excluded by !**/categories.rs and included by **
  • crates/biome_js_analyze/src/lint/nursery.rs is excluded by !**/nursery.rs and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-local-function.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-namespaced-call.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-typescript.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-unimported-toRefs.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-wrong-package-import.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-nested-contexts.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-typescript.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid.js.snap is excluded by !**/*.snap and included by **
  • packages/@biomejs/backend-jsonrpc/src/workspace.ts is excluded by !**/backend-jsonrpc/src/workspace.ts and included by **
📒 Files selected for processing (18)
  • .changeset/yummy-melons-serve.md (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-local-function.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-namespaced-call.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-typescript.ts (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-unimported-toRefs.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-wrong-package-import.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-nested-contexts.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-typescript.ts (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid.js (1 hunks)
  • crates/biome_rule_options/src/lib.rs (1 hunks)
  • crates/biome_rule_options/src/no_vue_setup_props_reactivity_loss.rs (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid.js
🚧 Files skipped from review as they are similar to previous changes (5)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-local-function.js
  • crates/biome_rule_options/src/no_vue_setup_props_reactivity_loss.rs
  • crates/biome_rule_options/src/lib.rs
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-typescript.ts
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js
🧰 Additional context used
📓 Path-based instructions (6)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-unimported-toRefs.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-namespaced-call.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js
  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-typescript.ts
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-wrong-package-import.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-nested-contexts.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-unimported-toRefs.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-namespaced-call.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js
  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-typescript.ts
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-wrong-package-import.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-nested-contexts.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js
**/tests/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place test files under a tests/ directory in each crate

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-unimported-toRefs.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-namespaced-call.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-typescript.ts
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-wrong-package-import.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-nested-contexts.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js
**/*.{rs,toml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Before committing, format Rust and TOML files (e.g., via just f/just format)

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Document rules, assists, and options via inline rustdoc in Rust source

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
.changeset/**/*.md

📄 CodeRabbit inference engine (CONTRIBUTING.md)

.changeset/**/*.md: Create changesets using the just new-changeset command; do not author them manually
In changeset markdown, only use headers #### or #####
Changeset descriptions must end every sentence with a full stop (.)
For bug fixes, start the changeset description with a linked issue reference like “Fixed #1234
Prefer past tense for what was done and present tense for current behavior in changesets

Files:

  • .changeset/yummy-melons-serve.md
🧬 Code graph analysis (9)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-unimported-toRefs.js (5)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js (4)
  • props (7-7)
  • props (18-18)
  • props (31-31)
  • props (44-44)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js (4)
  • props (6-6)
  • props (14-14)
  • props (39-39)
  • foo (30-30)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-local-function.js (1)
  • toRefs (8-8)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-wrong-package-import.js (1)
  • toRefs (6-6)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (3)
  • toRefs (17-17)
  • foo (25-25)
  • foo (41-41)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-namespaced-call.js (1)
crates/biome_js_analyze/tests/specs/suspicious/noRedeclare/valid-declaration-merging.ts (1)
  • h (131-133)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js (3)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js (4)
  • props (6-6)
  • props (14-14)
  • props (39-39)
  • foo (30-30)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js (2)
  • foo (21-21)
  • foo (35-35)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (3)
  • foo (25-25)
  • foo (41-41)
  • bar (52-52)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (2)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (2)
  • RuleDomain (904-912)
  • TextRange (9283-9283)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-typescript.ts (7)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js (4)
  • props (7-7)
  • props (18-18)
  • props (31-31)
  • props (44-44)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js (4)
  • props (6-6)
  • props (14-14)
  • props (39-39)
  • foo (30-30)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-local-function.js (1)
  • toRefs (8-8)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-unimported-toRefs.js (1)
  • toRefs (4-4)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-wrong-package-import.js (1)
  • toRefs (6-6)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (4)
  • toRefs (17-17)
  • foo (25-25)
  • foo (41-41)
  • reactiveProps (33-33)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js (2)
  • foo (21-21)
  • foo (35-35)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (5)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js (4)
  • props (7-7)
  • props (18-18)
  • props (31-31)
  • props (44-44)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js (4)
  • props (6-6)
  • props (14-14)
  • props (39-39)
  • foo (30-30)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-local-function.js (1)
  • toRefs (8-8)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-unimported-toRefs.js (1)
  • toRefs (4-4)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-wrong-package-import.js (1)
  • toRefs (6-6)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-wrong-package-import.js (3)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-local-function.js (1)
  • toRefs (8-8)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-unimported-toRefs.js (1)
  • toRefs (4-4)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (2)
  • toRefs (17-17)
  • bar (52-52)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-nested-contexts.js (3)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-conditional-destructuring.js (4)
  • props (7-7)
  • props (18-18)
  • props (31-31)
  • props (44-44)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-variable-destructuring.js (4)
  • props (6-6)
  • props (14-14)
  • props (39-39)
  • foo (30-30)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-safe-patterns.js (3)
  • foo (25-25)
  • foo (41-41)
  • bar (52-52)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js (1)
crates/biome_js_analyze/tests/specs/suspicious/noRedeclare/valid-declaration-merging.ts (1)
  • h (131-133)
🔇 Additional comments (5)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-namespaced-call.js (1)

1-7: Great addition covering namespaced helpers. Nice to see the tricky SomeLib.toRefs scenario locked in.

.changeset/yummy-melons-serve.md (1)

1-18: Concise changeset. Reads cleanly and ticks the guideline boxes.

crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-unimported-toRefs.js (1)

1-7: Unimported helper case captured. This should stop regressions right where they like to hide.

crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js (1)

1-37: Solid guardrail for non-Vue code. Appreciate the mix of shapes to prove we stay quiet in plain JS land.

crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-nested-contexts.js (1)

1-55: Nested-context coverage looks spot on. This should keep the rule honest around callback scopes.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (1)

235-237: Quoted setup keys still not handled properly.

This was flagged in previous reviews. Using .name().text() doesn't handle quoted object keys like "setup": function() {} or 'setup': function() {}. You need to explicitly check both identifier and literal member name variants and use .text_trimmed() to strip quotes.

Apply the fix from the previous review:

 fn is_member_named_setup(name: &AnyJsObjectMemberName) -> bool {
-    name.name().is_some_and(|text| text.text() == "setup")
+    if let Some(identifier) = name.as_js_identifier_member_name() {
+        return identifier
+            .value_token()
+            .is_ok_and(|token| token.text_trimmed() == "setup");
+    }
+
+    if let Some(literal) = name.as_js_literal_member_name() {
+        return literal
+            .name()
+            .is_ok_and(|token| token.text_trimmed() == "setup");
+    }
+
+    false
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5c36679 and 034125e.

⛔ Files ignored due to path filters (3)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid.js.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (3)
  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid.js
🧰 Additional context used
📓 Path-based instructions (5)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid.js
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid.js
**/*.{rs,toml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Before committing, format Rust and TOML files (e.g., via just f/just format)

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Document rules, assists, and options via inline rustdoc in Rust source

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
**/tests/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place test files under a tests/ directory in each crate

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid.js
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-02T12:57:33.209Z
Learning: Applies to crates/biome_analyze/crates/biome_js_analyze/lib/src/lint/nursery/*.rs : Place all new JavaScript lint rules in the nursery group under biome_js_analyze/lib/src/lint/nursery/<rule_name>.rs
📚 Learning: 2025-10-02T12:57:33.209Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-02T12:57:33.209Z
Learning: Applies to crates/biome_analyze/crates/biome_js_analyze/tests/specs/nursery/** : Place snapshot tests for new rules under tests/specs/nursery/<ruleName>/ with files prefixed by invalid* and valid*

Applied to files:

  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid.js
🧬 Code graph analysis (1)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (2)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (2)
  • RuleDomain (904-912)
  • TextRange (9283-9283)
crates/biome_analyze/src/rule.rs (4)
  • domains (591-594)
  • recommended (561-564)
  • sources (576-579)
  • inspired (252-257)

@AsherDe
Copy link
Author

AsherDe commented Oct 6, 2025

I have now updated this current PR to serve as the first step in a three-part series to implement the noVueSetupPropsReactivityLoss rule.

The scope of this PR is now limited to:

The basic scaffolding and declaration for the new rule.

The logic to reliably find the setup function within different Vue component patterns.

The core check for the first type of violation: destructuring props directly in the setup function's parameters (e.g., setup({ count })).

My plan for the follow-up PRs is as follows:

Part 2: I will add support for detecting props destructuring in the root scope of the setup function body (e.g., const { count } = props;).

Part 3: Finally, I will introduce the logic to correctly handle the toRefs and toRef exceptions to prevent false positives.

@AsherDe AsherDe requested a review from ematipico October 10, 2025 03:34
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 034125e and dbf6a8b.

📒 Files selected for processing (1)
  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
**/*.{rs,toml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Before committing, format Rust and TOML files (e.g., via just f/just format)

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Document rules, assists, and options via inline rustdoc in Rust source

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
🧬 Code graph analysis (1)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (2)
  • RuleDomain (904-912)
  • TextRange (9283-9283)

@AsherDe AsherDe force-pushed the feat/vue-reactivity-rule branch from dbf6a8b to 01e337b Compare October 10, 2025 03:43
Copy link
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left some nits, but the rule looks good 😊

@AsherDe AsherDe force-pushed the feat/vue-reactivity-rule branch from 5c29996 to 745084d Compare October 12, 2025 04:07
…cturing only

Foundation version with:
- Vue component discovery (export default, defineComponent)
- setup function detection (method & property forms)
- Parameter destructuring check (setup({ foo }))

Removed:
- Root scope destructuring
- toRefs/toRef validation
- Nested scope checking
- TypeScript tests

~604 lines → ~245 lines
@AsherDe AsherDe force-pushed the feat/vue-reactivity-rule branch from 745084d to 9450bfe Compare October 12, 2025 04:07
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5c29996 and 9450bfe.

⛔ Files ignored due to path filters (10)
  • crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs is excluded by !**/migrate/eslint_any_rule_to_biome.rs and included by **
  • crates/biome_configuration/src/analyzer/linter/rules.rs is excluded by !**/rules.rs and included by **
  • crates/biome_diagnostics_categories/src/categories.rs is excluded by !**/categories.rs and included by **
  • crates/biome_js_analyze/src/lint/nursery.rs is excluded by !**/nursery.rs and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid.js.snap is excluded by !**/*.snap and included by **
  • packages/@biomejs/backend-jsonrpc/src/workspace.ts is excluded by !**/backend-jsonrpc/src/workspace.ts and included by **
  • packages/@biomejs/biome/configuration_schema.json is excluded by !**/configuration_schema.json and included by **
📒 Files selected for processing (8)
  • .changeset/yummy-melons-serve.md (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid.js (1 hunks)
  • crates/biome_rule_options/src/lib.rs (1 hunks)
  • crates/biome_rule_options/src/no_vue_setup_props_reactivity_loss.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • crates/biome_rule_options/src/lib.rs
  • .changeset/yummy-melons-serve.md
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid-parameter-destructuring.js
🧰 Additional context used
📓 Path-based instructions (5)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js
  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid.js
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js
  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
  • crates/biome_rule_options/src/no_vue_setup_props_reactivity_loss.rs
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid.js
**/tests/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place test files under a tests/ directory in each crate

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/valid-non-vue-contexts.js
  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid.js
**/*.{rs,toml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Before committing, format Rust and TOML files (e.g., via just f/just format)

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
  • crates/biome_rule_options/src/no_vue_setup_props_reactivity_loss.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Document rules, assists, and options via inline rustdoc in Rust source

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs
  • crates/biome_rule_options/src/no_vue_setup_props_reactivity_loss.rs
🧠 Learnings (1)
📚 Learning: 2025-10-02T12:57:33.228Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-02T12:57:33.228Z
Learning: Applies to crates/biome_analyze/crates/biome_js_analyze/tests/specs/nursery/** : Place snapshot tests for new rules under tests/specs/nursery/<ruleName>/ with files prefixed by invalid* and valid*

Applied to files:

  • crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid.js
🧬 Code graph analysis (3)
crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (2)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • RuleDomain (904-912)
crates/biome_analyze/src/rule.rs (4)
  • domains (591-594)
  • recommended (561-564)
  • sources (576-579)
  • inspired (252-257)
crates/biome_rule_options/src/no_vue_setup_props_reactivity_loss.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • NoVueSetupPropsReactivityLossOptions (8248-8248)
crates/biome_js_analyze/tests/specs/nursery/noVueSetupPropsReactivityLoss/invalid.js (1)
crates/biome_js_analyze/tests/specs/suspicious/noRedeclare/valid-declaration-merging.ts (1)
  • h (131-133)
🔇 Additional comments (9)
crates/biome_rule_options/src/no_vue_setup_props_reactivity_loss.rs (1)

1-6: LGTM!

Empty options struct is correctly scaffolded with appropriate derives and serde configuration.

crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs (8)

6-11: Imports look good.

All use statements are at the beginning of the file and types are imported without redundant crate prefixes.


13-50: Rule declaration is well-structured.

Documentation clearly describes the parameter destructuring pattern this PR implements, with appropriate invalid and valid examples. Metadata (version, domain, source) is correct.


52-58: LGTM!

Simple Violation(TextRange) struct follows maintainer feedback.


60-63: LGTM!

Enum cleanly handles both method and function property forms.


65-104: Implementation correctly uses VueComponentQuery.

The rule properly leverages existing Vue component detection and generates clear diagnostics.


106-125: Helper functions are clean and focused.

check_call_expression_setup and check_object_members use if-let chains effectively and follow single-responsibility principle.


127-169: Parameter checking logic is sound.

The code correctly identifies destructuring patterns (object/array) and appropriately ignores simple identifier parameters. Arrow function handling covers both parameter forms.


171-217: Helper functions are well-structured.

find_setup_function, get_function_from_expression, is_named_setup, and get_first_binding_from_params all follow clear, single-purpose patterns. The name checking logic correctly handles both identifier and literal member names via the name() method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-CLI Area: CLI A-Diagnostic Area: diagnostocis A-Linter Area: linter A-Project Area: project L-JavaScript Language: JavaScript and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

📎 Port vue/no-setup-props-reactivity-loss from eslint-plugin-vue

3 participants