Skip to content

chore(cli): replace Ink + React with smaller-tree CLI libraries#264

Open
X-Guardian wants to merge 10 commits into
open-constructs:mainfrom
X-Guardian:chore/replace-react
Open

chore(cli): replace Ink + React with smaller-tree CLI libraries#264
X-Guardian wants to merge 10 commits into
open-constructs:mainfrom
X-Guardian:chore/replace-react

Conversation

@X-Guardian

@X-Guardian X-Guardian commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Related issue

Fixes #263

Description

Implements the Ink + React replacement proposed in the linked issue: swaps the Ink/React UI layer for @inquirer/prompts + cli-spinners + cli-table3 + ansi-escapes + cli-cursor. Same commands, flags, approval prompts, and --auto-approve semantics.

Improvements

  • Detached stdin exits cleanly instead of crashing with Ink's "Raw mode is not supported" stack trace.
  • Non-interactive output is ANSI-free — bar/spinner suppressed whenever stdout isn't a TTY.

Measured result:

Metric Before After
Shipped bundle (bundle/bin/cmds/handlers.js) 3.7 MB 2.9 MB
Transitive install size of the affected dep trees 22.6 MB ~6 MB

What changes:

  • New helpers in packages/cdktn-cli/src/bin/cmds/helper/:
    • tty-stream.tsStreamRenderer class. Log-above / bar-below pattern with a dots spinner (cli-spinners), wrap-aware row counting (via strip-ansi + out.columns so the erase/repaint doesn't cannibalise scrollback when the bar wraps), paused-state log buffering during interactive prompts, and a leading blank row above the bar that only paints once logs are streaming (so spinner-only commands stay single-spaced). Painting is gated on stdout.isTTY && !isCI (is-ci), so the bar suppresses itself for piped/redirected output and for CI runners whether or not they allocate a TTY.
    • project-runner.tsrunCdktfProject, replaces the useCdktfProject React hook by subscribing to CdktfProject's existing event callbacks. Maps cli-core's ProjectUpdate events to a discriminated Status union the UI consumes.
    • prompts.tspromptApprove, promptOverride, and the requireTty / isNonTtyError helpers that let callers fall back to status.stop() cleanly when no TTY is present.
    • format.ts — pure render functions for outputs, stack list (Stack-name column sized to 40% of terminal width for stable column positions across runs), provider table, execution counter, watch state.
  • All UI commands rewritten as .ts (no JSX): get, provider-list, synth, list, output, deploy, destroy, diff, watch.
  • Deleted: ui/components/ (bottom-bars, stream-view, outputs), ui/hooks/cdktf-project.ts, helper/render-ink.tsx, src/test/ui/deploy.test.tsx (Ink-specific).
  • packages/cdktn-cli/package.json: removed react, @types/react, ink, ink-select-input, ink-spinner, ink-table, ink-testing-library, yoga-layout-prebuilt. Added @inquirer/prompts@6.0.0 (was a devDep at 2.3.1; now a runtime dep at the latest CJS-compatible major), cli-spinners@2.9.2, cli-table3@0.6.5, ansi-escapes@4.3.2, cli-cursor@3.1.0, is-ci@^4.1.0. Versions are pinned to the latest CJS-compatible releases since the cdktn-cli bundle is CJS — bumping to the ESM-only majors is a separate ESM-output migration.
  • packages/cdktn-cli/build.ts: dropped yoga-layout-prebuilt from esbuild externals.
  • packages/cdktn-cli/tsconfig.json: dropped jsx: "react" and the explicit deploy.test.tsx include/exclude entries.
  • knip.jsonc: dropped the dead yoga-layout-prebuilt ignore for cdktn-cli, narrowed its project glob from *.{ts,tsx} to *.ts.
  • Dead React/JSX tooling sweep across the rest of the workspace: stripped jsx: "react" (and *.test.tsx excludes where present) from the 7 @cdktn/* sibling tsconfigs; removed 'tsx' from cli-core's jest moduleFileExtensions; reduced cdktn-cli's eslint.config.mjs from React-plugin-laden to a one-line spread of root config; removed eslint-plugin-react and eslint-plugin-react-hooks from root devDeps (cdktn-cli was the only consumer).

Manual verification

UI commands (TTY behaviour):

  • cdktn synth — spinner ticks → "Generated Terraform code for the stacks: …" line (no streaming logs in the common case)
  • cdktn list — spinner → columned stack list (no streaming logs in the common case)
  • cdktn get — spinner → language/output message
  • cdktn provider list — cli-table3 bordered output
  • cdktn diff — logs scroll above pinned spinner bar; bar repaints without artefacts even when wider than the terminal
  • cdktn deploy — inquirer takes over stdin at approval; Approve / Dismiss / Stop route correctly
  • cdktn deploy --auto-approve — fully unattended; no prompt
  • cdktn destroy — same approve flow as deploy
  • cdktn watch --auto-approve — "Waiting for changes…" (spinner) → execution counter on file change; Ctrl-C tears down (cursor returns)
  • cdktn diff | cat — stdout is a pipe → no ANSI escapes leak; no spinner; one line per log; no hang
  • cdktn deploy | cat — stdout is a pipe but stdin is still the TTY → arrow-key prompt still works (matches old Ink behaviour)
  • echo "" \| cdktn deploy (or any context where stdin is piped / detached) — stderr line "Approval required but stdin is not a TTY. … Stopping." then clean exit

Checklist

  • I have updated the PR title to match CDKTN's style guide
  • I have run the linter on my code locally
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation if applicable
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works if applicable
  • New and existing unit tests pass locally with my changes

@X-Guardian X-Guardian force-pushed the chore/replace-react branch from 1cc4ab6 to 07fc7d6 Compare June 23, 2026 14:33
@X-Guardian X-Guardian force-pushed the chore/replace-react branch from a78cb20 to 0001b46 Compare June 26, 2026 09:47
@X-Guardian X-Guardian force-pushed the chore/replace-react branch from 60f3555 to 7cc694c Compare June 26, 2026 10:10
@X-Guardian X-Guardian marked this pull request as ready for review June 26, 2026 14:10
@X-Guardian X-Guardian requested a review from a team as a code owner June 26, 2026 14:10
@so0k

so0k commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

this looks awesome, but certainly feels like it needs to soak a bit and as we are close to cutting a release - can we merge this after cutting the release?

2 concerns raised by an automated review:

  • In deploy.ts / destroy.ts, currently only handle non-TTY errors while awaiting approval prompts. With inquirer the prompt (depending on runtime behaviour) may reject on user cancellation, and status.stop() /status.reject() may need to be called explicitly or core-cli may hang?

  • add replacement tests for the new non-React helpers:

    • helper/format.ts
    • helper/prompts.ts
    • helper/project-runner.ts
    • helper/tty-stream.ts
    • rewritten deploy / destroy prompt routing

    would it make sense to add small unit tests around:

    • renderOutputs preserving sensitive output redaction and nested/object formatting
    • renderProviderTable / stack-list formatting if snapshots are meant to be stable
    • prompt non-TTY fallback behavior
    • deploy/destroy prompt rejection or cancellation routing

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.

cdktn-cli: remove Ink + React dependency tree

2 participants