Skip to content

fix(tui): map .js imports to .ts sources in esbuild bundle - #56543

Draft
DeviStormrage wants to merge 1 commit into
NousResearch:mainfrom
DeviStormrage:fix/tui-esbuild-js-to-ts
Draft

fix(tui): map .js imports to .ts sources in esbuild bundle#56543
DeviStormrage wants to merge 1 commit into
NousResearch:mainfrom
DeviStormrage:fix/tui-esbuild-js-to-ts

Conversation

@DeviStormrage

Copy link
Copy Markdown

What does this PR do?

Adds an esbuild resolver plugin to ui-tui/scripts/build.mjs that maps .js import paths to .ts/.tsx/.jsx source files on disk. Fixes the hermes --tui rebuild failure on first launch.

Related Issue

The TUI build script (scripts/build.mjs) uses esbuild without a resolver plugin. Combined with ui-tui/tsconfig.json adopting moduleResolution: "nodenext" (which requires every relative import to spell the .js extension even when the file on disk is .ts/.tsx), this means the first launch on any user install fails with errors like:

src/app/useMainApp.ts:27:38: ERROR: Could not resolve "../lib/resizeCoalescer.js"
src/components/appLayout.tsx:21:35: ERROR: Could not resolve "../lib/prompt.js"
src/components/journey.tsx:7:76: ERROR: Could not resolve "../lib/starmapPalette.js"

The user has to manually patch the build script before the TUI can start. This PR adds the missing esbuild plugin so the build script works with the existing nodenext module resolution.

Related closed issue: #31227 (different root cause — async __esm deadlock — but adjacent area)

Type of Change

  • Bug fix (non-breaking change that fixes an issue)

Changes Made

  • ui-tui/scripts/build.mjs: add jsToTsPlugin esbuild resolver plugin (32 lines including doc comment) and register it in the plugins array.

The plugin:

  1. Filters for imports ending in .js
  2. Tries .tsx, .ts, .jsx extensions on disk in order
  3. Returns the absolute path of the first match (esbuild rejects relative paths from resolve plugins)
  4. Returns null to fall through to esbuild's default resolution for real .js files (e.g. in node_modules/)

How to Test

  1. Wipe ui-tui/dist/entry.js (or test on a clean install)
  2. Run hermes --tui
  3. The TUI build should succeed; dist/entry.js is produced
  4. TUI launches and is interactive

Verified on: Windows 11 ARM64 (Snapdragon X Plus), Node.js 22, esbuild 0.28.x. The jsToTsPlugin is platform-agnostic — runs in esbuild on Linux, macOS, Windows alike.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(tui): map .js imports to .ts sources in esbuild bundle)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix
  • I've run pytest tests/ -q — N/A (no Python changes)
  • I've added tests — manual e2e test (build + launch) on Windows ARM64
  • I've tested on my platform: Windows 11 ARM64

Documentation & Housekeeping

  • I've updated relevant documentation — N/A (no user-facing change)
  • I've considered cross-platform impact — yes, the fix is platform-agnostic
  • I've updated tool descriptions/schemas if I changed tool behavior — N/A

Screenshots / Logs

Before (clean install, first hermes --tui):

Error: Build failed with 3 errors:
src/app/useMainApp.ts:27:38: ERROR: Could not resolve "../lib/resizeCoalescer.js"
src/components/appLayout.tsx:21:35: ERROR: Could not resolve "../lib/prompt.js"
src/components/journey.tsx:7:76: ERROR: Could not resolve "../lib/starmapPalette.js"

After (same clean install):

$ node ui-tui/scripts/build.mjs
  dist\entry.js  3.3mb
Done in 1440ms
built C:\Users\...\ui-tui\dist\entry.js

TUI launches and runs as expected.

tsconfig uses moduleResolution: nodenext, which requires every relative
import to spell the .js extension (even when the file on disk is .ts or
.tsx). esbuild does not auto-rewrite .js -> .ts for imports that already
carry an explicit extension — it only walks resolveExtensions for
extensionless imports. Without a resolver plugin, every
`import x from "../lib/foo.js"` fails to resolve even though foo.ts is
right there, and `hermes --tui` rebuilds fail on the first launch with
"Could not resolve ../lib/foo.js". Add a small esbuild plugin that
maps .js -> .tsx/.ts/.jsx/.js on disk and lets esbuild pick up the match.
@alt-glitch alt-glitch added type/bug Something isn't working comp/tui Terminal UI (ui-tui/ + tui_gateway/) P2 Medium — degraded but workaround exists labels Jul 1, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the focused TUI build fix. I found no verified blocker.

Current main still has NodeNext .js imports targeting TypeScript sources, including ui-tui/src/app/useMainApp.ts:28, ui-tui/src/components/appLayout.tsx:21, and ui-tui/src/components/journey.tsx:7. ui-tui/scripts/build.mjs:47 currently registers only the devtools stub, while the normal source-install launch path runs esbuild (hermes_cli/main.py:1786). The proposed resolver directly covers those relative import paths.

The existing bundle regression test already invokes scripts/build.mjs when dist/entry.js is absent (ui-tui/src/__tests__/bundleNoAsyncEsmDeadlock.test.ts:56-70); ui-tui/dist/ is ignored, so a clean checkout exercises that build path.

Automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/tui Terminal UI (ui-tui/ + tui_gateway/) P2 Medium — degraded but workaround exists sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants