Skip to content

fix(init): fix InkUI loading in dev mode (bun run src/bin.ts)#931

Closed
betegon wants to merge 5 commits intomainfrom
fix/init-ink-dev-mode
Closed

fix(init): fix InkUI loading in dev mode (bun run src/bin.ts)#931
betegon wants to merge 5 commits intomainfrom
fix/init-ink-dev-mode

Conversation

@betegon
Copy link
Copy Markdown
Member

@betegon betegon commented May 8, 2026

Summary

bun run src/bin.ts init always fell through to LoggingUI with "The interactive UI failed to load", making it impossible to test the Ink wizard without compiling a full binary.

Root cause: with { type: "file" } is a top-level static import that runs at module load time. In Bun dev mode it places ink-app.tsx's absolute path in the module cache as a file-resource entry. Any subsequent import() that resolves to the same path (including via .js.tsx extension fallback) gets the cached path string back instead of the module, so mountApp comes back undefined and the render throws.

Fix

Move the with { type: "file" } import into a dedicated sidecar module (ink-ui-sidecar.ts) that is only dynamically imported inside the compiled binary. We detect the compiled binary by checking import.meta.url.includes("/$bunfs/") — virtual-FS paths always contain that prefix; real-fs dev paths don't. In dev mode the sidecar is never loaded, so the module cache stays clean and import("./ink-app.js") resolves normally.

Test Plan

# Dev mode — should render the Ink welcome screen, not LoggingUI fallback
bun run src/bin.ts init

# Compiled binary (CI builds) — still works as before
bun run build --single
./dist-bin/sentry-darwin-arm64 init

betegon and others added 2 commits May 8, 2026 12:37
In dev mode, `with { type: "file" }` places ink-app.tsx's path in
Bun's module cache as a file resource. Any subsequent import() of
that path gets the path string back instead of the module, so
mountApp is undefined and the wizard falls back to LoggingUI.

Fix: isolate the `with { type: "file" }` import in a separate sidecar
module (ink-ui-sidecar.ts) that is only dynamically imported inside
the compiled binary. We detect the compiled binary by checking whether
import.meta.url contains the /$bunfs/ virtual-filesystem prefix. In
dev mode the check is false so the sidecar is never loaded and
ink-app.tsx is imported directly without any cache collision.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 8, 2026

PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://cli.sentry.dev/_preview/pr-931/

Built to branch gh-pages at 2026-05-08 14:13 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 8, 2026

Codecov Results 📊

6836 passed | Total: 6836 | Pass Rate: 100% | Execution Time: 0ms

📊 Comparison with Base Branch

Metric Change
Total Tests
Passed Tests
Failed Tests
Skipped Tests

✨ No test changes detected

All tests are passing successfully.

✅ Patch coverage is 100.00%. Project has 13923 uncovered lines.
❌ Project coverage is 77.03%. Comparing base (base) to head (head).

Coverage diff
@@            Coverage Diff             @@
##          main       #PR       +/-##
==========================================
- Coverage    77.04%    77.03%    -0.01%
==========================================
  Files          317       317         —
  Lines        60600     60610       +10
  Branches         0         0         —
==========================================
+ Hits         46687     46687         —
- Misses       13913     13923       +10
- Partials         0         0         —

Generated by Codecov Action

esbuild wraps CJS packages (signal-exit, parse-keypress, etc.) in
__commonJS helpers. When Bun.compile then embeds the resulting file
as a with { type: "file" } asset, it injects __promiseAll helpers at
wrong positions inside those wrappers, causing:

  SyntaxError: Unexpected identifier '__promiseAll'
  at parse-keypress.js:420:1

on all platforms. Switching to Bun.build produces output that
Bun.compile recognises natively and processes without the mis-injection.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@betegon betegon force-pushed the fix/init-ink-dev-mode branch from eced0a5 to eaaf42f Compare May 8, 2026 13:20
@betegon betegon marked this pull request as ready for review May 8, 2026 13:36
betegon and others added 2 commits May 8, 2026 15:41
Two fixes in one:

1. Dev mode (bun run src/bin.ts): the file:// URL approach to bypass
   the module cache collision doesn't work — Bun normalises file:///
   paths to plain paths before cache lookup, so it hits the same
   poisoned entry. Restoring the sidecar module (ink-ui-sidecar.ts)
   is the only reliable fix: the with { type: "file" } static import
   only runs inside the compiled binary (detected via /$bunfs/ in
   import.meta.url), so the cache is never poisoned in dev mode.

2. Binary CI (__promiseAll on Linux): esbuild wraps CJS packages in
   __commonJS helpers; Bun.compile injects __promiseAll at wrong
   positions inside those wrappers. Switching text-import-plugin.ts
   to use Bun.build instead of esbuild avoids the mis-injection.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
`import("./ink-app.js")` (static literal) in ink-ui.ts caused esbuild
to inline all of ink-app.tsx — including ink's CJS modules — into the
main bundle (bin.js). Bun.compile then processes those modules and
injects __promiseAll at wrong positions inside esbuild's __commonJS
wrappers, producing:

  SyntaxError: Unexpected identifier '__promiseAll'

even for commands like --help that never touch the Ink UI.

Fix: use a variable for the dev-mode import path so esbuild cannot
statically analyse it and declines to bundle ink-app.tsx into bin.js.
esbuild only inlines static string literals; a variable (even one
whose value is a constant) blocks the inlining. In the compiled binary
the dev-mode branch is never reached (import.meta.url contains
/$bunfs/), so the variable import is dead code and no resolution is
attempted. In dev mode (bun run src/bin.ts) Bun resolves the variable
path at runtime without any bundler involvement.

Also reverts text-import-plugin.ts to the clean esbuild-based approach
(no Bun.build, no ink-dev-strip plugin) now that the root cause is
addressed in ink-ui.ts instead.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@betegon betegon closed this May 8, 2026
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