1
- use oxc_ast:: {
2
- AstKind ,
3
- ast:: {
4
- CallExpression , ExportDefaultDeclarationKind , Expression , IdentifierReference ,
5
- ObjectPropertyKind ,
6
- } ,
7
- } ;
1
+ use oxc_ast:: AstKind ;
8
2
use oxc_diagnostics:: OxcDiagnostic ;
9
3
use oxc_macros:: declare_oxc_lint;
10
4
use oxc_span:: Span ;
11
5
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
+ } ;
13
12
14
13
fn has_type_and_arguments_diagnostic ( span : Span ) -> OxcDiagnostic {
15
14
OxcDiagnostic :: warn ( "`defineEmits` has both a type-only emit and an argument." )
@@ -140,7 +139,7 @@ impl Rule for ValidDefineEmits {
140
139
fn run_once ( & self , ctx : & LintContext ) {
141
140
let mut found: Option < Span > = None ;
142
141
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" ) ;
144
143
for node in ctx. nodes ( ) {
145
144
let AstKind :: CallExpression ( call_expr) = node. kind ( ) else {
146
145
continue ;
@@ -161,105 +160,27 @@ impl Rule for ValidDefineEmits {
161
160
}
162
161
found = Some ( call_expr. span ) ;
163
162
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)
244
165
else {
245
166
continue ;
246
167
} ;
247
168
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) ;
259
178
}
260
179
}
261
180
262
- false
181
+ fn should_run ( & self , ctx : & crate :: context:: ContextHost ) -> bool {
182
+ ctx. frameworks_options ( ) == FrameworkOptions :: VueSetup
183
+ }
263
184
}
264
185
265
186
#[ test]
0 commit comments