1+ use oxc_ast:: AstKind ;
12use oxc_diagnostics:: OxcDiagnostic ;
23use oxc_macros:: declare_oxc_lint;
34use oxc_span:: Span ;
45
5- use crate :: { context:: LintContext , rule:: Rule , utils:: is_jest_file} ;
6+ use crate :: context:: LintContext ;
7+ use crate :: rule:: Rule ;
8+ use crate :: utils:: {
9+ JestFnKind , JestGeneralFnKind , collect_possible_jest_call_node, is_jest_file,
10+ parse_general_jest_fn_call,
11+ } ;
612
713fn no_export_diagnostic ( span : Span ) -> OxcDiagnostic {
814 OxcDiagnostic :: warn ( "Do not export from a test file." )
@@ -38,19 +44,38 @@ declare_oxc_lint!(
3844 correctness
3945) ;
4046
47+ // Emits a diagnostic if the file matches all of these criteria:
48+ // 1. name ends with test.ts, spec.ts, or similar
49+ // 2. at least one test fn call: it, test, etc.
50+ // 3. at least one export
4151impl Rule for NoExport {
4252 fn run_once ( & self , ctx : & LintContext ) {
4353 // only used in jest files
4454 if !is_jest_file ( ctx) {
4555 return ;
4656 }
4757
48- for span in ctx. module_record ( ) . exported_bindings . values ( ) {
49- ctx. diagnostic ( no_export_diagnostic ( * span) ) ;
50- }
58+ // `collect_possible_jest_call_node` yields uses of `JEST_METHOD_NAMES`.
59+ // We focus on "fit", "it", "test", "xit", and "xtest" (JestGeneralFnKind::Test).
60+ // Presence of any of these is taken to mean the module has tests, and the rule should enforce no exports.
61+ let has_tests = collect_possible_jest_call_node ( ctx) . into_iter ( ) . any ( |possible_node| {
62+ let AstKind :: CallExpression ( call_expr) = possible_node. node . kind ( ) else {
63+ return false ;
64+ } ;
65+ let Some ( general) = parse_general_jest_fn_call ( call_expr, & possible_node, ctx) else {
66+ return false ;
67+ } ;
68+ matches ! ( general. kind, JestFnKind :: General ( JestGeneralFnKind :: Test ) )
69+ } ) ;
70+
71+ if has_tests {
72+ for span in ctx. module_record ( ) . exported_bindings . values ( ) {
73+ ctx. diagnostic ( no_export_diagnostic ( * span) ) ;
74+ }
5175
52- if let Some ( span) = ctx. module_record ( ) . export_default {
53- ctx. diagnostic ( no_export_diagnostic ( span) ) ;
76+ if let Some ( span) = ctx. module_record ( ) . export_default {
77+ ctx. diagnostic ( no_export_diagnostic ( span) ) ;
78+ }
5479 }
5580 }
5681}
@@ -70,6 +95,7 @@ fn test() {
7095 ) ,
7196 ( "window.location = 'valid'" , None , None , None ) ,
7297 ( "module.somethingElse = 'foo';" , None , None , None ) ,
98+ ( "export const myThing = 'valid'" , None , None , Some ( PathBuf :: from( "foo.test.js" ) ) ) ,
7399 ( "export const myThing = 'valid'" , None , None , Some ( PathBuf :: from( "foo.js" ) ) ) ,
74100 ( "export default function () {}" , None , None , Some ( PathBuf :: from( "foo.js" ) ) ) ,
75101 ( "module.exports = function(){}" , None , None , None ) ,
0 commit comments