Skip to content

fix: add explicit .js extensions to relative imports so declarations resolve under NodeNext/Node16#705

Open
ken-jo wants to merge 1 commit into
modelcontextprotocol:mainfrom
ken-jo:fix/nodenext-extensionless-relative-imports
Open

fix: add explicit .js extensions to relative imports so declarations resolve under NodeNext/Node16#705
ken-jo wants to merge 1 commit into
modelcontextprotocol:mainfrom
ken-jo:fix/nodenext-extensionless-relative-imports

Conversation

@ken-jo

@ken-jo ken-jo commented Jul 11, 2026

Copy link
Copy Markdown

Summary

Fixes #704.

The published type declarations use extensionless relative import specifiers (e.g. import { ProtocolWithEvents } from "./events"; in dist/src/app-bridge.d.ts). tsc (emitDeclarationOnly) copies specifiers verbatim from src, and under Node16/NodeNext module resolution a relative specifier must carry an explicit extension — so these imports don't resolve for downstream consumers. Any type that transitively depends on one loses its members; most visibly, the recommended bridge.addEventListener("sandboxready", …) API (inherited from ProtocolWithEvents via ./events) becomes invisible, producing TS2339.

This adds explicit .js extensions to every relative import/export specifier across src. It is types-only — runtime JavaScript is unaffected, since the .js bundles are produced by Bun.build, which inlines relative modules (no extensionless relative import survives at runtime). The change matches the existing external-SDK import style (e.g. @modelcontextprotocol/sdk/shared/protocol.js), which already uses explicit .js; only the relative imports were inconsistent. It is compatible with both the repo's bundler-mode type-check and Bun.build (both resolve ./x.js./x.ts). No bare package specifiers were touched.

Verification

  • NodeNext repro from the issue — a consumer tsconfig with moduleResolution: NodeNext and index.ts doing bridge.addEventListener("sandboxready", () => {}), type-checked against a freshly built dist:
    • Before: index.ts: error TS2339: Property 'addEventListener' does not exist on type 'AppBridge'. (tsc --noEmit exits 2)
    • After: tsc --noEmit exits 0.
  • npm run prettier — the changed files are clean.
  • npm test373 pass, 2 skip, 0 fail (types-only change; no test behavior affected).

Scope: 13 files, 35 relative specifiers under src/** (including the *.test.ts relative imports, for consistency).

…resolve under NodeNext/Node16

The emitted .d.ts copy relative specifiers verbatim, and the extensionless form
(e.g. `from "./events"`) doesn't resolve under Node16/NodeNext, hiding inherited
members such as `addEventListener` (TS2339). Appending `.js` matches the existing
SDK-import style and works with both bundler-mode type-checking and Bun.build.
Types-only; runtime is unaffected.

Fixes modelcontextprotocol#704
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.

Emitted .d.ts use extensionless relative imports — types break under NodeNext/Node16 (addEventListener invisible, TS2339)

1 participant