@@ -73,70 +73,104 @@ declare_oxc_lint!(
7373
7474impl Rule for NoRequiredPropWithDefault {
7575 fn run < ' a > ( & self , node : & AstNode < ' a > , ctx : & LintContext < ' a > ) {
76- match node. kind ( ) {
77- AstKind :: CallExpression ( call_expr) => {
78- let Expression :: Identifier ( ident) = & call_expr. callee else {
76+ let is_vue = ctx. file_path ( ) . extension ( ) . is_some_and ( |ext| ext == "vue" ) ;
77+ if is_vue {
78+ self . run_on_vue ( node, ctx) ;
79+ } else {
80+ self . check_define_component ( node, ctx) ;
81+ }
82+ }
83+ }
84+
85+ impl NoRequiredPropWithDefault {
86+ fn run_on_vue < ' a > ( & self , node : & AstNode < ' a > , ctx : & LintContext < ' a > ) {
87+ if ctx. frameworks_options ( ) == FrameworkOptions :: VueSetup {
88+ self . run_on_setup ( node, ctx) ;
89+ } else {
90+ self . run_on_composition ( node, ctx) ;
91+ }
92+ }
93+
94+ #[ expect( clippy:: unused_self) ]
95+ fn check_define_component ( & self , node : & AstNode < ' _ > , ctx : & LintContext < ' _ > ) {
96+ // only check `defineComponent` method
97+ // e.g. `let component = defineComponent({ props: { name: { required: true, default: 'a' } } })`
98+ let AstKind :: CallExpression ( call_expr) = node. kind ( ) else {
99+ return ;
100+ } ;
101+ let Some ( ident) = call_expr. callee . get_identifier_reference ( ) else {
102+ return ;
103+ } ;
104+ if ident. name . as_str ( ) == "defineComponent" && call_expr. arguments . len ( ) == 1 {
105+ let arg = & call_expr. arguments [ 0 ] ;
106+ let Some ( Expression :: ObjectExpression ( obj) ) = arg. as_expression ( ) else {
107+ return ;
108+ } ;
109+ handle_object_expression ( ctx, obj) ;
110+ }
111+ }
112+
113+ fn run_on_setup < ' a > ( & self , node : & AstNode < ' a > , ctx : & LintContext < ' a > ) {
114+ let AstKind :: CallExpression ( call_expr) = node. kind ( ) else {
115+ return ;
116+ } ;
117+ let Some ( ident) = call_expr. callee . get_identifier_reference ( ) else {
118+ return ;
119+ } ;
120+
121+ match ident. name . as_str ( ) {
122+ "defineProps" => {
123+ if let Some ( arge) = call_expr. arguments . first ( ) {
124+ let Some ( Expression :: ObjectExpression ( obj) ) = arge. as_expression ( ) else {
125+ return ;
126+ } ;
127+ // Here we need to consider the following two examples
128+ // 1. const props = defineProps({ name: { required: true, default: 'a' } })
129+ // 2. const { name = 'a' } = defineProps({ name: { required: true } })
130+ let key_hash =
131+ collect_hash_from_variable_declarator ( ctx, node) . unwrap_or_default ( ) ;
132+ handle_prop_object ( ctx, obj, Some ( & key_hash) ) ;
133+ }
134+ if call_expr. arguments . is_empty ( ) {
135+ // if `defineProps` is used without arguments, we need to check the type arguments
136+ // e.g. `const { name = 'a' } = defineProps<IProp>()`
137+ let Some ( type_args) = & call_expr. type_arguments else {
138+ return ;
139+ } ;
140+ let Some ( first_type_argument) = type_args. params . first ( ) else {
141+ return ;
142+ } ;
143+ if let Some ( key_hash) = collect_hash_from_variable_declarator ( ctx, node) {
144+ handle_type_argument ( ctx, first_type_argument, & key_hash) ;
145+ }
146+ }
147+ }
148+ "withDefaults" if call_expr. arguments . len ( ) == 2 => {
149+ let [ first_arg, second_arg] = call_expr. arguments . as_slice ( ) else {
79150 return ;
80151 } ;
81- let is_vue_setup = ctx. frameworks_options ( ) == FrameworkOptions :: VueSetup ;
82- match ident. name . as_str ( ) {
83- "defineProps" if is_vue_setup => {
84- if let Some ( arge) = call_expr. arguments . first ( ) {
85- let Some ( Expression :: ObjectExpression ( obj) ) = arge. as_expression ( )
86- else {
87- return ;
88- } ;
89- // Here we need to consider the following two examples
90- // 1. const props = defineProps({ name: { required: true, default: 'a' } })
91- // 2. const { name = 'a' } = defineProps({ name: { required: true } })
92- let key_hash = collect_hash_from_variable_declarator ( ctx, node)
93- . unwrap_or_default ( ) ;
94- handle_prop_object ( ctx, obj, Some ( & key_hash) ) ;
95- }
96- if call_expr. arguments . is_empty ( ) {
97- // if `defineProps` is used without arguments, we need to check the type arguments
98- // e.g. `const { name = 'a' } = defineProps<IProp>()`
99- let Some ( type_args) = & call_expr. type_arguments else {
100- return ;
101- } ;
102- let Some ( first_type_argument) = type_args. params . first ( ) else {
103- return ;
104- } ;
105- if let Some ( key_hash) = collect_hash_from_variable_declarator ( ctx, node)
106- {
107- handle_type_argument ( ctx, first_type_argument, & key_hash) ;
108- }
109- }
110- }
111- "defineComponent" if call_expr. arguments . len ( ) == 1 => {
112- let arg = & call_expr. arguments [ 0 ] ;
113- let Some ( Expression :: ObjectExpression ( obj) ) = arg. as_expression ( ) else {
114- return ;
115- } ;
116- handle_object_expression ( ctx, obj) ;
117- }
118- "withDefaults" if call_expr. arguments . len ( ) == 2 && is_vue_setup => {
119- let [ first_arg, second_arg] = call_expr. arguments . as_slice ( ) else {
120- return ;
121- } ;
122- if let ( Some ( first_arg_expr) , Some ( second_arg_expr) ) =
123- ( first_arg. as_expression ( ) , second_arg. as_expression ( ) )
124- {
125- let Expression :: ObjectExpression ( second_obj_expr) =
126- second_arg_expr. get_inner_expression ( )
127- else {
128- return ;
129- } ;
130- let Some ( key_hash) = collect_hash_from_object_expr ( second_obj_expr)
131- else {
132- return ;
133- } ;
134- process_define_props_call ( ctx, first_arg_expr, & key_hash) ;
135- }
136- }
137- _ => { }
152+ if let ( Some ( first_arg_expr) , Some ( second_arg_expr) ) =
153+ ( first_arg. as_expression ( ) , second_arg. as_expression ( ) )
154+ {
155+ let Expression :: ObjectExpression ( second_obj_expr) =
156+ second_arg_expr. get_inner_expression ( )
157+ else {
158+ return ;
159+ } ;
160+ let Some ( key_hash) = collect_hash_from_object_expr ( second_obj_expr) else {
161+ return ;
162+ } ;
163+ process_define_props_call ( ctx, first_arg_expr, & key_hash) ;
138164 }
139165 }
166+ _ => {
167+ self . check_define_component ( node, ctx) ;
168+ }
169+ }
170+ }
171+
172+ fn run_on_composition < ' a > ( & self , node : & AstNode < ' a > , ctx : & LintContext < ' a > ) {
173+ match node. kind ( ) {
140174 AstKind :: ExportDefaultDeclaration ( export_default_decl) => {
141175 let ExportDefaultDeclarationKind :: ObjectExpression ( obj_expr) =
142176 & export_default_decl. declaration
@@ -145,13 +179,11 @@ impl Rule for NoRequiredPropWithDefault {
145179 } ;
146180 handle_object_expression ( ctx, obj_expr) ;
147181 }
148- _ => { }
182+ _ => {
183+ self . check_define_component ( node, ctx) ;
184+ }
149185 }
150186 }
151-
152- fn should_run ( & self , ctx : & crate :: context:: ContextHost ) -> bool {
153- ctx. file_path ( ) . extension ( ) . is_some_and ( |ext| ext == "vue" )
154- }
155187}
156188
157189fn collect_hash_from_object_expr ( obj : & ObjectExpression ) -> Option < FxHashSet < String > > {
@@ -577,6 +609,21 @@ fn test() {
577609 ] ;
578610
579611 let fail = vec ! [
612+ (
613+ "
614+ const a = defineComponent({
615+ props: {
616+ 'name': {
617+ required: true,
618+ default: 'Hello'
619+ }
620+ }
621+ })
622+ " ,
623+ None ,
624+ None ,
625+ Some ( PathBuf :: from( "test.ts" ) ) ,
626+ ) ,
580627 (
581628 r#"
582629 <script setup lang="ts">
0 commit comments