1- use oxc_ast:: {
2- AstKind ,
3- ast:: {
4- CallExpression , ExportDefaultDeclarationKind , Expression , IdentifierReference ,
5- ObjectPropertyKind ,
6- } ,
7- } ;
1+ use oxc_ast:: AstKind ;
82use oxc_diagnostics:: OxcDiagnostic ;
93use oxc_macros:: declare_oxc_lint;
104use oxc_span:: Span ;
115
12- use crate :: { ContextSubHost , context:: LintContext , frameworks:: FrameworkOptions , rule:: Rule } ;
6+ use crate :: {
7+ context:: LintContext ,
8+ frameworks:: FrameworkOptions ,
9+ rule:: Rule ,
10+ utils:: { DefineMacroProblem , check_define_macro_call_expression, has_default_exports_property} ,
11+ } ;
1312
1413fn has_type_and_arguments_diagnostic ( span : Span ) -> OxcDiagnostic {
1514 OxcDiagnostic :: warn ( "`defineEmits` has both a type-only emit and an argument." )
@@ -140,7 +139,7 @@ impl Rule for ValidDefineEmits {
140139 fn run_once ( & self , ctx : & LintContext ) {
141140 let mut found: Option < Span > = None ;
142141
143- let has_other_script_emits = has_default_emits_exports ( & ctx. other_file_hosts ( ) ) ;
142+ let has_other_script_emits = has_default_exports_property ( & ctx. other_file_hosts ( ) , "emits" ) ;
144143 for node in ctx. nodes ( ) {
145144 let AstKind :: CallExpression ( call_expr) = node. kind ( ) else {
146145 continue ;
@@ -161,105 +160,27 @@ impl Rule for ValidDefineEmits {
161160 }
162161 found = Some ( call_expr. span ) ;
163162
164- handle_call_expression ( call_expr, ctx, has_other_script_emits) ;
165- }
166- }
167-
168- fn should_run ( & self , ctx : & crate :: context:: ContextHost ) -> bool {
169- ctx. frameworks_options ( ) == FrameworkOptions :: VueSetup
170- }
171- }
172-
173- fn handle_call_expression (
174- call_expr : & CallExpression ,
175- ctx : & LintContext ,
176- has_other_script_emits : bool ,
177- ) {
178- let has_type_args = call_expr. type_arguments . is_some ( ) ;
179-
180- if has_type_args && has_other_script_emits {
181- ctx. diagnostic ( define_in_both ( call_expr. span ) ) ;
182- return ;
183- }
184-
185- // `defineEmits` has type arguments and js arguments. Vue Compiler allows only one of them.
186- if has_type_args && !call_expr. arguments . is_empty ( ) {
187- ctx. diagnostic ( has_type_and_arguments_diagnostic ( call_expr. span ) ) ;
188- return ; // Skip if there are type arguments
189- }
190-
191- if has_type_args {
192- // If there are type arguments, we don't need to check the arguments.
193- return ;
194- }
195-
196- let Some ( expression) = call_expr. arguments . first ( ) . and_then ( |first| first. as_expression ( ) )
197- else {
198- // `defineEmits();` is valid when `export default { emits: [] }` is defined
199- if !has_other_script_emits {
200- ctx. diagnostic ( events_not_defined ( call_expr. span ) ) ;
201- }
202- return ;
203- } ;
204-
205- if has_other_script_emits {
206- ctx. diagnostic ( define_in_both ( call_expr. span ) ) ;
207- return ;
208- }
209-
210- match expression {
211- Expression :: ArrayExpression ( _) | Expression :: ObjectExpression ( _) => { }
212- Expression :: Identifier ( identifier) => {
213- if !is_non_local_reference ( identifier, ctx) {
214- ctx. diagnostic ( referencing_locally ( call_expr. span ) ) ;
215- }
216- }
217- _ => {
218- ctx. diagnostic ( referencing_locally ( call_expr. span ) ) ;
219- }
220- }
221- }
222-
223- pub fn is_non_local_reference ( identifier : & IdentifierReference , ctx : & LintContext < ' _ > ) -> bool {
224- if let Some ( symbol_id) = ctx. semantic ( ) . scoping ( ) . get_root_binding ( & identifier. name ) {
225- return matches ! (
226- ctx. semantic( ) . symbol_declaration( symbol_id) . kind( ) ,
227- AstKind :: ImportSpecifier ( _)
228- ) ;
229- }
230-
231- // variables outside the current `<script>` block are valid.
232- // This is the same for unresolved variables.
233- true
234- }
235-
236- fn has_default_emits_exports ( others : & Vec < & ContextSubHost < ' _ > > ) -> bool {
237- for host in others {
238- for other_node in host. semantic ( ) . nodes ( ) {
239- let AstKind :: ExportDefaultDeclaration ( export) = other_node. kind ( ) else {
240- continue ;
241- } ;
242-
243- let ExportDefaultDeclarationKind :: ObjectExpression ( export_obj) = & export. declaration
163+ let Some ( problem) =
164+ check_define_macro_call_expression ( call_expr, ctx, has_other_script_emits)
244165 else {
245166 continue ;
246167 } ;
247168
248- let has_emits_exports = export_obj. properties . iter ( ) . any ( |property| {
249- let ObjectPropertyKind :: ObjectProperty ( property) = property else {
250- return false ;
251- } ;
252-
253- property. key . name ( ) . is_some_and ( |name| name == "emits" )
254- } ) ;
255-
256- if has_emits_exports {
257- return true ;
258- }
169+ let diagnostic = match problem {
170+ DefineMacroProblem :: DefineInBoth => define_in_both ( call_expr. span ) ,
171+ DefineMacroProblem :: HasTypeAndArguments => {
172+ has_type_and_arguments_diagnostic ( call_expr. span )
173+ }
174+ DefineMacroProblem :: EventsNotDefined => events_not_defined ( call_expr. span ) ,
175+ DefineMacroProblem :: ReferencingLocally => referencing_locally ( call_expr. span ) ,
176+ } ;
177+ ctx. diagnostic ( diagnostic) ;
259178 }
260179 }
261180
262- false
181+ fn should_run ( & self , ctx : & crate :: context:: ContextHost ) -> bool {
182+ ctx. frameworks_options ( ) == FrameworkOptions :: VueSetup
183+ }
263184}
264185
265186#[ test]
0 commit comments