From 18297899d7088e291e4ca32a99c9c3d6e9abc7c1 Mon Sep 17 00:00:00 2001 From: xxxigm Date: Sun, 24 May 2026 08:45:22 +0700 Subject: [PATCH] fix(tui): drop ink-text-input re-export from @hermes/ink entry-exports (#31227) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dashboard TUI bundle hung at startup with only 141 bytes of ANSI reset sequences and a blank screen forever. Root cause: esbuild's lightweight `__esm` helper at the top of `dist/entry.js` does not await nested async init, so a circular async cycle in the module graph never resolves. The cycle came from re-exporting ``TextInput`/`UncontrolledTextInput`` from `'ink-text-input'` here — that npm package depends on the upstream `ink` package, whose graph loops back through React + our in-tree `@hermes/ink` ink fork. The result: `init_entry_exports` was emitted as `async … await init_build4()` (where `build4` is `node_modules/ink-text-input/build`), and the top-level `await Promise.all([init_entry_exports().then(...)])` in `src/entry.tsx` deadlocked waiting on the dangling Promise. Nobody in `ui-tui/` actually imports `TextInput` from `@hermes/ink` — the composer uses the in-tree `src/components/textInput.tsx` widget instead. Drop the re-export from the source so the bundle no longer inlines the upstream ink graph at all. Callers that legitimately want the upstream widget can still import it from the dedicated `@hermes/ink/text-input` subpath, which sits outside `entry-exports` and so does not get inlined into consumers' bundles. After the fix: * `dist/entry.js` shrinks from 2.9MB → 2.4MB (~11.5k fewer bundled lines) with zero `async __esm` wrappers remaining. * `init_entry_exports` is now a synchronous `__esm` module. * The bundle's top-level await chain resolves in ~30ms instead of hanging. --- ui-tui/packages/hermes-ink/index.d.ts | 5 +++-- .../packages/hermes-ink/src/entry-exports.ts | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/ui-tui/packages/hermes-ink/index.d.ts b/ui-tui/packages/hermes-ink/index.d.ts index 2a8ccef6297..a0db6e7e0c7 100644 --- a/ui-tui/packages/hermes-ink/index.d.ts +++ b/ui-tui/packages/hermes-ink/index.d.ts @@ -36,5 +36,6 @@ export type { Instance, RenderOptions, Root } from './src/ink/root.ts' export { stringWidth } from './src/ink/stringWidth.ts' export type { MouseTrackingMode } from './src/ink/termio/dec.ts' export { wrapAnsi } from './src/ink/wrapAnsi.ts' -export { default as TextInput, UncontrolledTextInput } from 'ink-text-input' -export type { Props as TextInputProps } from 'ink-text-input' +// 'ink-text-input' types deliberately not re-exported here; see +// src/entry-exports.ts for the full rationale (#31227). Use the +// '@hermes/ink/text-input' subpath when the upstream widget is needed. diff --git a/ui-tui/packages/hermes-ink/src/entry-exports.ts b/ui-tui/packages/hermes-ink/src/entry-exports.ts index 2251fa6c82c..aaa849506ae 100644 --- a/ui-tui/packages/hermes-ink/src/entry-exports.ts +++ b/ui-tui/packages/hermes-ink/src/entry-exports.ts @@ -29,4 +29,21 @@ export { stringWidth } from './ink/stringWidth.js' export { isXtermJs } from './ink/terminal.js' export type { MouseTrackingMode } from './ink/termio/dec.js' export { wrapAnsi } from './ink/wrapAnsi.js' -export { default as TextInput, UncontrolledTextInput } from 'ink-text-input' + +// NOTE: Do not re-export from 'ink-text-input' here. +// +// 'ink-text-input' depends on the npm 'ink' package; pulling it in from +// this re-export drags an entire second copy of ink (and its async +// top-level init chain) into any caller that bundles `@hermes/ink` from +// source. esbuild's `__esm` helper then deadlocks on the circular +// async init between the two ink graphs — the dashboard TUI bundle +// stalls at startup with only 141 bytes of ANSI reset output, blank +// screen forever (#31227). +// +// Consumers that actually want the upstream ink-text-input widget must +// import it via the dedicated subpath: +// +// import TextInput from '@hermes/ink/text-input' +// +// which still resolves through this package's `./text-input` export, +// just outside the entry-exports surface that gets inlined by callers.