Skip to content

Commit dbf6a8b

Browse files
committed
Overall improvements
1 parent 034125e commit dbf6a8b

File tree

1 file changed

+97
-122
lines changed

1 file changed

+97
-122
lines changed

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

Lines changed: 97 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use biome_console::markup;
66
use biome_js_semantic::SemanticModel;
77
use biome_js_syntax::{
88
AnyJsArrowFunctionParameters, AnyJsBindingPattern, AnyJsExpression, AnyJsFunction,
9-
AnyJsObjectMember, AnyJsObjectMemberName, JsCallExpression, JsExportDefaultExpressionClause,
10-
JsMethodObjectMember, JsObjectMemberList, JsParameters,
9+
AnyJsObjectMember, AnyJsObjectMemberName, JsCallExpression, JsMethodObjectMember,
10+
JsObjectMemberList, JsParameters,
1111
};
1212
use biome_rowan::{AstNode, AstSeparatedList, TextRange};
1313

@@ -41,7 +41,7 @@ declare_lint_rule! {
4141
/// ```
4242
///
4343
pub NoVueSetupPropsReactivityLoss {
44-
version: "next",
44+
version: "2.2.6",
4545
name: "noVueSetupPropsReactivityLoss",
4646
language: "js",
4747
domains: &[RuleDomain::Vue],
@@ -50,15 +50,18 @@ declare_lint_rule! {
5050
}
5151
}
5252

53-
/// Represents the specific reason why the rule was triggered.
54-
#[derive(Debug)]
5553
pub enum Violation {
56-
/// `props` were destructured directly in the function's parameters.
5754
ParameterDestructuring(TextRange),
5855
}
5956

60-
/// An abstraction to handle both function expressions and method declarations for `setup`.
61-
#[derive(Debug)]
57+
impl Violation {
58+
fn range(&self) -> TextRange {
59+
match self {
60+
Self::ParameterDestructuring(range) => *range,
61+
}
62+
}
63+
}
64+
6265
enum SetupFunction {
6366
Function(AnyJsFunction),
6467
Method(JsMethodObjectMember),
@@ -71,15 +74,28 @@ impl Rule for NoVueSetupPropsReactivityLoss {
7174
type Options = ();
7275

7376
fn run(ctx: &RuleContext<Self>) -> Self::Signals {
74-
analyze_vue_component(ctx.query(), ctx.model())
77+
match ctx.query() {
78+
// Case: export default { setup(props) { ... } }
79+
AnyPotentialVueComponent::JsExportDefaultExpressionClause(export) => {
80+
export
81+
.expression()
82+
.ok()
83+
.and_then(|expr| expr.as_js_object_expression())
84+
.map(|obj_expr| check_object_members(&obj_expr.members()))
85+
.unwrap_or_default()
86+
}
87+
// Case: export default defineComponent({ setup(props) { ... } })
88+
AnyPotentialVueComponent::JsCallExpression(call_expr) => {
89+
check_call_expression_setup(call_expr)
90+
}
91+
}
7592
}
7693

7794
fn diagnostic(_ctx: &RuleContext<Self>, state: &Self::State) -> Option<RuleDiagnostic> {
78-
let Violation::ParameterDestructuring(range) = state;
7995
Some(
8096
RuleDiagnostic::new(
8197
rule_category!(),
82-
*range,
98+
state.range(),
8399
markup! {
84100
"Destructuring `props` in the `setup` function parameters loses reactivity."
85101
},
@@ -91,155 +107,114 @@ impl Rule for NoVueSetupPropsReactivityLoss {
91107
}
92108
}
93109

94-
fn analyze_vue_component(
95-
potential_component: &AnyPotentialVueComponent,
96-
model: &SemanticModel,
97-
) -> Vec<Violation> {
98-
match potential_component {
99-
AnyPotentialVueComponent::JsExportDefaultExpressionClause(export_clause) => {
100-
extract_setup_from_export_default(export_clause, model)
101-
}
102-
AnyPotentialVueComponent::JsCallExpression(call_expr) => {
103-
extract_setup_from_call_expression(call_expr, model)
104-
}
105-
}
106-
}
107-
108-
fn extract_setup_from_export_default(
109-
export_clause: &JsExportDefaultExpressionClause,
110-
_model: &SemanticModel,
111-
) -> Vec<Violation> {
112-
export_clause
113-
.expression()
114-
.ok()
115-
.and_then(|expr| {
116-
expr.as_js_object_expression()
117-
.map(|obj_expr| extract_setup_violations_from_members(&obj_expr.members()))
118-
})
119-
.unwrap_or_default()
120-
}
121-
122-
fn extract_setup_from_call_expression(
123-
call_expr: &JsCallExpression,
124-
_model: &SemanticModel,
125-
) -> Vec<Violation> {
110+
fn check_call_expression_setup(call_expr: &JsCallExpression) -> Vec<Violation> {
126111
call_expr
127112
.arguments()
128113
.ok()
129-
.and_then(|args| args.args().iter().next().and_then(|arg| arg.ok()))
130-
.and_then(|first_arg| {
131-
first_arg
132-
.as_any_js_expression()
133-
.and_then(|e| e.as_js_object_expression())
134-
.map(|obj_expr| extract_setup_violations_from_members(&obj_expr.members()))
135-
})
114+
.and_then(|args| args.args().iter().next()?.ok())
115+
.and_then(|arg| arg.as_any_js_expression())
116+
.and_then(|expr| expr.as_js_object_expression())
117+
.map(|obj_expr| check_object_members(&obj_expr.members()))
136118
.unwrap_or_default()
137119
}
138120

139-
fn extract_setup_violations_from_members(members: &JsObjectMemberList) -> Vec<Violation> {
121+
fn check_object_members(members: &JsObjectMemberList) -> Vec<Violation> {
140122
members
141123
.iter()
142-
.filter_map(|member| member.ok())
143-
.filter_map(|member| extract_setup_from_object_member(&member))
144-
.flat_map(|setup_fn| check_setup_function(&setup_fn))
124+
.filter_map(|m| m.ok())
125+
.filter_map(find_setup_function)
126+
.filter_map(|setup| check_setup_params(&setup))
145127
.collect()
146128
}
147129

148-
fn check_setup_function(setup_fn: &SetupFunction) -> Vec<Violation> {
149-
let Some(first_param) = extract_setup_first_parameter(setup_fn) else {
150-
return vec![];
151-
};
130+
fn check_setup_params(setup_fn: &SetupFunction) -> Option<Violation> {
131+
let first_param = get_first_parameter(setup_fn)?;
152132

153133
match first_param {
154-
// Case: `setup({ count })` - parameter destructuring.
155-
AnyJsBindingPattern::JsObjectBindingPattern(obj_pattern) => {
156-
vec![Violation::ParameterDestructuring(obj_pattern.range())]
134+
AnyJsBindingPattern::JsObjectBindingPattern(obj) => {
135+
Some(Violation::ParameterDestructuring(obj.range()))
157136
}
158-
AnyJsBindingPattern::JsArrayBindingPattern(array_pattern) => {
159-
vec![Violation::ParameterDestructuring(array_pattern.range())]
137+
AnyJsBindingPattern::JsArrayBindingPattern(arr) => {
138+
Some(Violation::ParameterDestructuring(arr.range()))
160139
}
161-
// Case: `setup(props)` - no violation for now
162-
AnyJsBindingPattern::AnyJsBinding(_) => vec![],
140+
AnyJsBindingPattern::AnyJsBinding(_) => None,
163141
}
164142
}
165143

166-
fn extract_setup_from_object_member(member: &AnyJsObjectMember) -> Option<SetupFunction> {
167-
match member {
168-
AnyJsObjectMember::JsMethodObjectMember(method) => method
169-
.name()
170-
.ok()
171-
.filter(is_member_named_setup)
172-
.map(|_| SetupFunction::Method(method.clone())),
173-
AnyJsObjectMember::JsPropertyObjectMember(property) => property
174-
.name()
175-
.ok()
176-
.filter(is_member_named_setup)
177-
.and_then(|_| property.value().ok())
178-
.and_then(|value| extract_function_from_expression(&value))
179-
.map(SetupFunction::Function),
180-
_ => None,
181-
}
182-
}
183-
184-
fn extract_setup_first_parameter(func: &SetupFunction) -> Option<AnyJsBindingPattern> {
185-
match func {
186-
SetupFunction::Function(any_func) => extract_js_function_first_parameter(any_func),
187-
SetupFunction::Method(method) => method
188-
.parameters()
189-
.ok()
190-
.as_ref()
191-
.and_then(get_first_parameter_binding),
144+
fn get_first_parameter(setup_fn: &SetupFunction) -> Option<AnyJsBindingPattern> {
145+
match setup_fn {
146+
SetupFunction::Method(method) => {
147+
let params = method.parameters().ok()?;
148+
get_first_binding_from_params(&params)
149+
}
150+
SetupFunction::Function(func) => get_function_first_parameter(func),
192151
}
193152
}
194153

195-
fn extract_js_function_first_parameter(func: &AnyJsFunction) -> Option<AnyJsBindingPattern> {
154+
fn get_function_first_parameter(func: &AnyJsFunction) -> Option<AnyJsBindingPattern> {
196155
match func {
197-
AnyJsFunction::JsFunctionDeclaration(decl) => decl
198-
.parameters()
199-
.ok()
200-
.as_ref()
201-
.and_then(get_first_parameter_binding),
202-
AnyJsFunction::JsFunctionExpression(expr) => expr
203-
.parameters()
204-
.ok()
205-
.as_ref()
206-
.and_then(get_first_parameter_binding),
207156
AnyJsFunction::JsArrowFunctionExpression(arrow) => {
208-
arrow.parameters().ok().and_then(|params| match params {
209-
// e.g., `(props) => {}`
210-
AnyJsArrowFunctionParameters::JsParameters(js_params) => {
211-
get_first_parameter_binding(&js_params)
212-
}
213-
// e.g., `props => {}`
157+
match arrow.parameters().ok()? {
214158
AnyJsArrowFunctionParameters::AnyJsBinding(binding) => {
215159
Some(AnyJsBindingPattern::AnyJsBinding(binding))
216160
}
217-
})
161+
AnyJsArrowFunctionParameters::JsParameters(params) => {
162+
get_first_binding_from_params(&params)
163+
}
164+
}
165+
}
166+
AnyJsFunction::JsFunctionDeclaration(decl) => {
167+
get_first_binding_from_params(&decl.parameters().ok()?)
168+
}
169+
AnyJsFunction::JsFunctionExpression(expr) => {
170+
get_first_binding_from_params(&expr.parameters().ok()?)
218171
}
219172
_ => None,
220173
}
221174
}
222175

223-
fn extract_function_from_expression(expr: &AnyJsExpression) -> Option<AnyJsFunction> {
176+
fn find_setup_function(member: &AnyJsObjectMember) -> Option<SetupFunction> {
177+
match member {
178+
AnyJsObjectMember::JsMethodObjectMember(method) => {
179+
is_named_setup(&method.name().ok()?)
180+
.then(|| SetupFunction::Method(method.clone()))
181+
}
182+
AnyJsObjectMember::JsPropertyObjectMember(property) => {
183+
is_named_setup(&property.name().ok()?)?;
184+
let value = property.value().ok()?;
185+
let func = get_function_from_expression(&value)?;
186+
Some(SetupFunction::Function(func))
187+
}
188+
_ => None,
189+
}
190+
}
191+
192+
fn get_function_from_expression(expr: &AnyJsExpression) -> Option<AnyJsFunction> {
224193
match expr {
225-
AnyJsExpression::JsFunctionExpression(func_expr) => {
226-
Some(AnyJsFunction::JsFunctionExpression(func_expr.clone()))
194+
AnyJsExpression::JsFunctionExpression(func) => {
195+
Some(AnyJsFunction::JsFunctionExpression(func.clone()))
227196
}
228-
AnyJsExpression::JsArrowFunctionExpression(arrow_func) => {
229-
Some(AnyJsFunction::JsArrowFunctionExpression(arrow_func.clone()))
197+
AnyJsExpression::JsArrowFunctionExpression(arrow) => {
198+
Some(AnyJsFunction::JsArrowFunctionExpression(arrow.clone()))
230199
}
231200
_ => None,
232201
}
233202
}
234203

235-
fn is_member_named_setup(name: &AnyJsObjectMemberName) -> bool {
236-
name.name().is_some_and(|text| text.text() == "setup")
204+
fn is_named_setup(name: &AnyJsObjectMemberName) -> Option<()> {
205+
name.name()
206+
.filter(|text| text.text() == "setup")
207+
.map(|_| ())
237208
}
238209

239-
fn get_first_parameter_binding(params: &JsParameters) -> Option<AnyJsBindingPattern> {
240-
let param = params.items().iter().next()?.ok()?;
241-
let formal_param = param
210+
fn get_first_binding_from_params(params: &JsParameters) -> Option<AnyJsBindingPattern> {
211+
params
212+
.items()
213+
.iter()
214+
.next()?
215+
.ok()?
242216
.as_any_js_formal_parameter()?
243-
.as_js_formal_parameter()?;
244-
formal_param.binding().ok()
245-
}
217+
.as_js_formal_parameter()?
218+
.binding()
219+
.ok()
220+
}

0 commit comments

Comments
 (0)