fix(init): fix InkUI loading in dev mode (bun run src/bin.ts)#931
Closed
fix(init): fix InkUI loading in dev mode (bun run src/bin.ts)#931
Conversation
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>
Contributor
|
Contributor
Codecov Results 📊✅ 6836 passed | Total: 6836 | Pass Rate: 100% | Execution Time: 0ms 📊 Comparison with Base Branch
✨ No test changes detected All tests are passing successfully. ✅ Patch coverage is 100.00%. Project has 13923 uncovered lines. 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>
eced0a5 to
eaaf42f
Compare
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
bun run src/bin.ts initalways 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 subsequentimport()that resolves to the same path (including via.js→.tsxextension fallback) gets the cached path string back instead of the module, somountAppcomes backundefinedand 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 checkingimport.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 andimport("./ink-app.js")resolves normally.Test Plan