Skip to content

Commit 6d45871

Browse files
committed
fix(linter): improve jest/no_export heuristic
1 parent 9cd7a67 commit 6d45871

File tree

1 file changed

+32
-6
lines changed

1 file changed

+32
-6
lines changed

crates/oxc_linter/src/rules/jest/no_export.rs

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1+
use oxc_ast::AstKind;
12
use oxc_diagnostics::OxcDiagnostic;
23
use oxc_macros::declare_oxc_lint;
34
use 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

713
fn 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
4151
impl 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

Comments
 (0)