@@ -6,8 +6,8 @@ use biome_console::markup;
66use biome_js_semantic:: SemanticModel ;
77use 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} ;
1212use 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 ) ]
5553pub 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+
6265enum 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