Skip to content

fix: add .js extensions to relative imports so the plugin loads under ESM#15

Open
Robespierred0p wants to merge 2 commits into
doza62:masterfrom
Robespierred0p:fix/esm-import-extensions
Open

fix: add .js extensions to relative imports so the plugin loads under ESM#15
Robespierred0p wants to merge 2 commits into
doza62:masterfrom
Robespierred0p:fix/esm-import-extensions

Conversation

@Robespierred0p

Copy link
Copy Markdown

Problem

On OpenCode 1.14.33, the published opencode-mobile plugin fails to load and never starts a tunnel. The OpenCode log shows:

ERROR service=plugin path=opencode-mobile@latest
  error=Cannot find module '.../dist/src/push/formatter'
  imported from '.../dist/index.js'
  failed to load plugin

This is why users see No tunnel URL found from /mobile — the plugin never actually loads.

Root cause

tsconfig.json uses "moduleResolution": "bundler", which allows the source to omit file extensions on relative imports and emits them unchanged (e.g. import { formatNotification } from "./src/push/formatter"). OpenCode loads plugins with strict ESM resolution, which requires explicit .js extensions, so every extensionless relative specifier throws ERR_MODULE_NOT_FOUND and the whole plugin fails to load.

dist/src/push/formatter.js exists in the published package — it just can't be resolved without the extension.

The src/cli/* files already use .js extensions; only the plugin runtime graph (reachable from index.ts) was affected.

Fix

Add .js extensions to all relative imports in the plugin runtime graph:

  • index.ts
  • src/push/{index,formatter,sender,token-store}.ts
  • src/tunnel/{index,cloudflare,localtunnel,ngrok}.ts
  • src/utils/index.ts
  • src/proxy/index.ts

11 files, 30 lines. No behavior changes — only import specifiers.

Verification

After npm run build, loaded the built plugin into OpenCode 1.14.33 via the local plugins dir. The plugin now initializes cleanly (no module error), the serve-mode gate passes, the Cloudflare tunnel starts, and the QR/URL are generated:

[opencode-mobile] v1.4.0
[PushPlugin][Mobile] Initialized
[PushPlugin] isServeMode: true
[Tunnel] Auto-selecting Cloudflare (most secure)...
[Cloudflared] URL: https://<...>.trycloudflare.com

Suggested follow-up (not in this PR)

Consider switching tsconfig.json to "moduleResolution": "nodenext" so extensionless relative imports become compile-time errors and this class of bug can't regress.

🤖 Generated with Claude Code

Robespierred0p and others added 2 commits June 2, 2026 21:03
… ESM

The compiled dist/index.js used extensionless relative imports (e.g.
"./src/push/formatter") because tsconfig uses moduleResolution "bundler".
OpenCode's strict ESM plugin loader requires explicit extensions and throws
"Cannot find module", so the plugin failed to load entirely. Add .js to all
relative imports in the plugin runtime graph (index.ts, src/push/*,
src/tunnel/*, src/utils, src/proxy). The src/cli/* files already used .js.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

1 participant