Skip to content

fix(node): full re-export fallback for unresolvable member re-exports#34689

Merged
bartlomieju merged 1 commit into
mainfrom
fix/cjs-member-reexport-umd-fallback
Jun 2, 2026
Merged

fix(node): full re-export fallback for unresolvable member re-exports#34689
bartlomieju merged 1 commit into
mainfrom
fix/cjs-member-reexport-umd-fallback

Conversation

@bartlomieju

@bartlomieju bartlomieju commented Jun 1, 2026

Copy link
Copy Markdown
Member

Importing a named export from graphql-tag still failed with "does not
provide an export named 'gql'", even though #34363 added handling for the
module.exports = require("X").MEMBER entry shape that graphql-tag@2
uses. That fix narrows the wrapper's named exports to the names
statically attached to MEMBER inside X, which works when the attachments
are top-level but not for graphql-tag's actual inner file: a UMD bundle
that performs its exports.gql = ... and gql.* = ... assignments inside
the factory IIFE and builds the value through a namespace alias. Static
narrowing finds nothing there, so the wrapper advertised no names at all.

When the member's attached names cannot be determined statically, fall
back to re-exporting the inner module wholesale rather than advertising
nothing. This matches Node, which over-approximates require(X).Y to the
inner module's full set of named exports. The narrow path is unchanged
for the cases where the attachments are statically known, so unrelated
names still do not leak through there.

Closes #16708
Closes #25311

…orts

PR #34363 taught the CJS analyzer to handle the
`module.exports = require("X").MEMBER` shape by narrowing the wrapper's
named exports to those statically attached to MEMBER inside X. That
works when the attachments are top-level, but graphql-tag@2 (the
canonical motivating package) ships a UMD bundle that wraps its
`exports.gql = …` / `gql.* = …` assignments inside the factory IIFE and
builds the value through a namespace alias. Static narrowing finds
nothing, so `import { gql } from "npm:graphql-tag"` still failed with
"does not provide an export named 'gql'".

When the member's attached names can't be determined statically,
re-export the inner module wholesale instead of advertising nothing.
This matches Node, which over-approximates `require(X).Y` to all of X's
named exports. The narrow path is unchanged for the cases where the
attachments are statically known, so unrelated names still don't leak
through there.

Closes #16708.

@bartlomieju bartlomieju left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Reviewed the implementation in context. Small, well-targeted change with a genuinely discriminating test. Overall LGTM pending green CI.

Correctness

  • The Ok(None) branch in resolve_member_reexports now collects the inner specifier instead of silently dropping it, and both call sites feed those into the existing wholesale re-export path. The narrow path (Ok(Some(props))) is untouched, so statically-resolvable members still don't leak unrelated names.
  • No infinite-loop risk: each analyze_reexports call seeds handled_reexports with the entry and dedups inner specifiers.
  • default is consistently filtered on both the member path and the wholesale path, so it won't leak as a named export.

Test

  • Importing all three of gql, resetCaches, disableFragmentWarnings (not just gql) is what makes this discriminating: if static narrowing had partially resolved to only gql, the other two would fail, so this actually exercises the new fallback. It also asserts runtime values, not just that imports resolve, covering the cjs-to-esm value synthesis. The fixture faithfully mirrors graphql-tag@2's UMD-inside-IIFE-via-namespace-alias shape.

Minor (non-blocking)

  • nit: the "resolve members -> if fallback non-empty, re-export wholesale" block is duplicated across analyze_all_exports and the analyze_reexports loop. Fine to leave, since the two call sites dispatch differently.
  • The body says Closes #16708, but this also fixes #25311 (the original graphql-tag report). Worth adding #25311 to the closes list so it auto-closes.

@bartlomieju

Copy link
Copy Markdown
Member Author

Thanks for the review.

@bartlomieju
bartlomieju merged commit ec2b4bf into main Jun 2, 2026
137 checks passed
@bartlomieju
bartlomieju deleted the fix/cjs-member-reexport-umd-fallback branch June 2, 2026 07:27
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.

Bug: No named export gql in graphq-tag found 'npm:graphql-tag' does not provide an export named 'gql'

1 participant