Skip to content

Navigation Menu

Sign in
Sign up

[JSC] Module linking: name the binding and the module when an indirect export does not resolve - #581

Open
robobun wants to merge 1 commit into
main from
robobun/3b9d0a79/indirect-export-diagnostics
Open

[JSC] Module linking: name the binding and the module when an indirect export does not resolve #581
robobun wants to merge 1 commit into
main from
robobun/3b9d0a79/indirect-export-diagnostics

Conversation

@robobun

@robobun robobun commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

Problem

  • For export { default } from "./p.mjs" (or export { default as x } from) where p.mjs has no default export, bun prints SyntaxError: export default cannot be used with export *. No module has an export *, and the message names neither the binding nor the module. Node prints The requested module './p.mjs' does not provide an export named 'default'.
  • The cause is the IndirectExportEntries check in CyclicModuleRecord::initializeEnvironment (runtime/CyclicModuleRecord.cpp:109-132, BUN_JSC_ADDITIONS branch). resolveExport answers Error for any unresolvable default (an export * never provides one), and the branch had a fixed string for it. The NotFound and Ambiguous messages in the same switch printed e.exportName (the alias) instead of e.importName: export { nope as y } from "./p.mjs" said export 'y' not found in './p.mjs', and a conflicting export * said Cannot export 'z' multiple times in './root.mjs'.

Fix

  • Use the same three messages as the ImportEntries check in step 7 of the same function. They name the requested binding (importName) and the requested module by its key:
    • Export named 'nope' not found in module '/abs/p.mjs'.
    • Missing 'default' export in module '/abs/p.mjs'.
    • Export named 'dup' cannot be resolved due to ambiguous multiple bindings in module '/abs/root.mjs'.
  • The module key comes from hostResolveImportedModule, a map lookup that resolveExport already did for this entry. If the lookup misses, the raw specifier is printed.
  • The m_isTypeScript leniency for NotFound and the non-Bun messages do not change. Failing is still correct in every case. Only the text changes.
  • Verified in bun with the companion PR's tests (test/js/bun/typescript/type-export.test.ts), which cover the three shapes above plus the existing through-export cases.

...t export does not resolve
The InitializeEnvironment check for IndirectExportEntries (export { a as b } from
"m", or import { a } from "m"; export { a as b }) printed the export alias and
the raw specifier, and for an unresolvable 'default' it printed "export default
cannot be used with export *" even when no module has an export *.
Use the same messages as the ImportEntries check in step 7: name the requested
binding (importName) and the requested module by its key.
 export { nope as y } from "./p.mjs"
 before: export 'y' not found in './p.mjs'
 after: Export named 'nope' not found in module '/abs/p.mjs'.
 export { default } from "./p.mjs" (p.mjs has no default)
 before: export default cannot be used with export *
 after: Missing 'default' export in module '/abs/p.mjs'.
 export { dup as z } from "./root.mjs" (two export * in root provide dup)
 before: Cannot export 'z' multiple times in './root.mjs'
 after: Export named 'dup' cannot be resolved due to ambiguous multiple
 bindings in module '/abs/root.mjs'.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM — Bun-only diagnostic text change that mirrors the existing step-7 ImportEntry messages in the same function.

What was reviewed:

  • Confirmed ExportEntry carries importName, moduleName, and moduleRequestType, so the new makeString calls and the hostResolveImportedModule lookup compile against the existing struct.
  • Verified hostResolveImportedModule is a pure m_loadedModules map lookup returning nullptr on miss — no exceptions, so the lambda's fallback to e.moduleName.string() is safe and no RETURN_IF_EXCEPTION is needed on the error path.
  • Control flow is unchanged: each case still returns after throwing, and the m_isTypeScript NotFound leniency is preserved.
Extended reasoning...

Overview

The PR touches a single file, Source/JavaScriptCore/runtime/CyclicModuleRecord.cpp, and only within #if USE(BUN_JSC_ADDITIONS) blocks. It rewrites the SyntaxError message text for the three failure cases (NotFound, Ambiguous, Error) when resolving IndirectExportEntries in initializeEnvironment. A small helper lambda requestedModuleKey is added that calls hostResolveImportedModule (a m_loadedModules HashMap lookup, per AbstractModuleRecord.cpp:230-235) to resolve the specifier to its module key, falling back to the raw specifier on a miss. The messages now use e.importName (the binding requested from the target module) instead of e.exportName (the local alias), and the resolved module key instead of the raw specifier — matching the ImportEntry messages at lines 221-260 of the same function.

Security risks

None. This is diagnostic string construction on an already-failing path that is about to throw a SyntaxError and return. No parsing, no allocation pattern changes beyond makeString, no new inputs from untrusted sources beyond what was already being formatted into these messages. hostResolveImportedModule takes a JSGlobalObject* but ignores it and cannot throw, so there is no exception-handling gap introduced by calling it inside the message construction.

Level of scrutiny

Low. The change is guarded to Bun builds, alters only user-facing error text, and copies its shape directly from the ImportEntry handling ~100 lines below in the same function (which already calls hostResolveImportedModule and formats importedModule->moduleKey().string() into identical message templates). The Error branch's e.importName == vm.propertyNames->defaultKeyword split is a reasonable refinement: when the direct request was default, it says "Missing 'default' export"; when the failure came from deeper in a re-export chain, it falls back to the generic not-found phrasing — either way strictly better than the previous fixed "export default cannot be used with export *" string.

Other factors

The repo's .github/CODEOWNERS lists /Source/JavaScriptCore @ WebKit/jsc-reviewers, but the file's own header states it does not confer ownership and exists to auto-add upstream WebKit reviewers — not a gate in this Bun fork. The PR description references companion Bun-side tests (test/js/bun/typescript/type-export.test.ts) covering these shapes. No prior reviews or objections appear in the timeline, and the bug hunt exited on dry_streak with no candidates.

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

i️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Essentials

Run ID: 763da236-524f-472e-a86a-6c50bb8b7440

📥 Commits

Reviewing files that changed from the base of the PR and between 983055f and da1219b.

📒 Files selected for processing (1)
  • Source/JavaScriptCore/runtime/CyclicModuleRecord.cpp

Included review availability: Your plan provides up to 5 included reviews per hour; 0 remain after this review.


Walkthrough

Changes

Indirect export diagnostics

Layer / File(s) Summary
Resolve module keys and report export errors
Source/JavaScriptCore/runtime/CyclicModuleRecord.cpp
Bun-specific indirect export handling resolves each requested module key, falls back to the original name when needed, and reports distinct messages for missing, ambiguous, and unresolved default exports.

Merge Risk: ⚪ Minimal · up to da121

Indirect-export diagnostics now identify the requested binding and resolved module path without an evidenced remaining merge-readiness risk.

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the problem, implementation, preserved behavior, and test coverage. It does not follow the repository template because it omits a bug title, Bugzilla link, review status, and ... Add the required template sections: bug title, Bugzilla URL, "Reviewed by NOBODY (OOPS!).", a concise explanation of the fix, and the changed path/function list. Include the relevant bug reference and required pull request metadata.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: improved module-linking diagnostics for unresolved indirect exports. It is specific and concise enough for history scanning.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description explains the problem, implementation, preserved behavior, and test coverage. It does not follow the repository template because it omits a bug title, Bugzilla link, review status, and the required changed-file and function list.

  • Fix all pre-merge checks with AI

Warning

Git: CodeRabbit could not clone the repository, so clone-backed analysis was skipped and this review may be incomplete. Verify repository clone access, such as SSH credentials, before requesting another full review. If clone access is intentionally unavailable, use path_filters to narrow the review scope.


Comment @coderabbitai help to get the list of available commands.

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

Preview Builds

Commit Release Date
da1219be autobuild-preview-pr-581-da1219be 2026年09月07日 05:46:41 UTC

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

Reviewers

@claude claude[bot] claude[bot] left review comments

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

1 participant

AltStyle によって変換されたページ (->オリジナル) /