Skip to content

fix(no-export): detect more CommonJS export assignment patterns#1977

Open
eryue0220 wants to merge 8 commits into
jest-community:mainfrom
eryue0220:fix/no-export-false-negatives
Open

fix(no-export): detect more CommonJS export assignment patterns#1977
eryue0220 wants to merge 8 commits into
jest-community:mainfrom
eryue0220:fix/no-export-false-negatives

Conversation

@eryue0220

Copy link
Copy Markdown
Contributor

Based on #1976, fixes false negatives in jest/no-export for CommonJS export assignments in test files.

  • Previously, the rule only inspected a single member level (AssignmentExpression > MemberExpression), so several valid export patterns were missed:
  • module["exports"] = … — computed property access with a string literal
    Nested assignments such as module.exports.foo.bar = … — only the outermost member was checked

eryue0220 and others added 3 commits June 24, 2026 14:48
Require the exact exports property name instead of matching export typos,
and ignore assignments when module or exports refers to a local binding.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add a test case for module[exports] assignments when exports refers to
the global CommonJS binding, satisfying the 100% coverage requirement.

Co-authored-by: Cursor <cursoragent@cursor.com>
Refactor assignment detection to walk the full member chain and report
module["exports"], exports =, exports.foo =, and nested module.exports
assignments that were previously missed.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread src/rules/no-export.ts
resolveScope,
} from './utils';

const isGlobalModuleIdentifier = (

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would make more sense to have a single isGlobalIdentifier function that you can pass the identifier name into rather than two slightly different functions

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Comment thread src/rules/__tests__/no-export.test.ts Outdated
{
code: 'module.export.invalid = function() {}; ; test("a test", () => { expect(1).toBe(1);});',
errors: [{ endColumn: 22, column: 1, messageId: 'unexpectedExport' }],
code: 'module[exports] = function() {}; test("a test", () => { expect(1).toBe(1);});',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we cannot assume that a variable has a specific value, and frankly I don't think there's any valid reason for someone to do this so it's fine to not support it.

on the other hand, there's really no reason to do any other assignment to module so it might be simpler still to just check for an assignment to anything on global module - that would simplify our logic to:

  1. is this a global?
  2. is this either module or exports?
  3. is this on the left hand side of an assignment?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — I've simplified the logic along those lines and dropped the module[exports] test case.

The check now boils down to:

  1. Is this a global?isGlobalIdentifier uses resolveScope(...) === null, so locally shadowed module/exports bindings are ignored.
  2. Is this either module or exports? — for a direct assignment we check the LHS identifier; for member expressions we walk to the root identifier and check whether it's global module or exports (so any assignment on global module is caught, not just module.exports).
  3. Is this on the left-hand side of an assignment? — we only handle this in the AssignmentExpression visitor, inspecting node.left.

No longer special-case computed properties like module[exports] — I agree there's no good reason to support that pattern, and the simplified root-identifier check covers the cases we care about without assuming anything about variable values.

@G-Rath G-Rath left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we now pull out adding support for exports? that feels like it'll take a huge chunk out of this diff.

also can we not drop support for export for now? I know it's technically not valid but it's been there from the start and for now it'll also keep the diff down

@eryue0220

Copy link
Copy Markdown
Contributor Author

can we now pull out adding support for exports? that feels like it'll take a huge chunk out of this diff.

also can we not drop support for export for now? I know it's technically not valid but it's been there from the start and for now it'll also keep the diff down

Okay updated.

@G-Rath

G-Rath commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Okay updated.

No you have not - I'm wanting a PR that just adds support for exports like what we currently have for modules, not adding anything further at this point

@eryue0220

Copy link
Copy Markdown
Contributor Author

Okay updated.

No you have not - I'm wanting a PR that just adds support for exports like what we currently have for modules, not adding anything further at this point

another pr about exports: #1986

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants