Skip to content

build: move to node and npm, and remove code nothing reaches - #14

Merged
RohanReddy44 merged 3 commits into
mainfrom
build/node-and-npm
Sep 23, 2026
Merged

RohanReddy44 merged 3 commits into
mainfrom
build/node-and-npm

Conversation

@RohanReddy44

Copy link
Copy Markdown
Collaborator

The tail of #13. Those three commits were pushed after it merged, so they missed it.

Bun → Node and npm

Bun was chosen because npm could not install this tree at all, crashing with Cannot read properties of null (reading 'edgesOut').

That was one dependency, not the tree. npm installs cleanly with vitest removed and crashes in #loadPeerSet with it present — vitest 4 declares a large optional-peer set npm's arborist cannot walk. Vitest 5 does not have the problem, so the fix is an upgrade rather than a workaround.

What forced the question was the container build failing with Cannot find package 'tsx:'. vite.config.ts passes virtualRouteConfig as a path, so the router generator loads that TypeScript file through tsx — a Node loader. The fix on Bun was to install Node beside it: two runtimes to work around one plugin. It also failed only intermittently, depending on whether the generator's cache re-ran, so the build worked on one machine and not the next.

Nothing runs a JavaScript runtime in production — the artifact is static files behind nginx — so this is a build-time choice only.

Two things npm surfaced that Bun had absorbed silently:

  • @vitest/coverage-v8 was pinned to 4.0.17 against vitest@^4.1.11, a real conflict
  • vitest 3 would also have installed, but carries a moderate path-traversal advisory covering 2.1.0-beta.1–4.1.10. Downgrading would have added a vulnerability

No --legacy-peer-deps anywhere: it hides exactly this kind of conflict. packageManager, the Dockerfile, CI, the hooks and the docs all follow.

Dead code

A whole-graph sweep. Unused exports 35 → 20, unused types 7 → 0; the 20 remaining are all vendored shadcn kept as a complete API, which tree-shaking drops from the bundle anyway.

  • The recording half of audio-utils.ts was dead — no callers, and removing it orphaned four private helpers and an interface in turn. 374 lines → 93. Playback survives
  • The per-screen query layer was empty scaffolding — export const ChatScreenQueries = {} as const with a TODO, behind two barrels and a misspelled componets/ directory. The queryClient and its MutationCache stay; that part is real, and it is where the first thing needing caching or invalidation plugs in
  • OnRequestError was a React component returning an empty <div>, used as a MutationCache onError callback. Only the logging ever had an effect
  • Telemetry left two modules and a 648 KB dependency behind — getBrowserInfo, getFingerprintId and @fingerprintjs/fingerprintjs
  • ConfigProvider's React context had no consumer — nothing called useConfig, since everything reads getConfig() directly. The effect that applies theme variables remains, which was always its real job
  • The screen-scaffold generator, 250 lines, whose default parent was app/(_authenticated) — a route group deleted with sign-in, so its output would not have compiled

Net −439 / +38 across 20 files.

Verified

Clean worktree with npm ci: lint, typecheck, knip, 23 tests, coverage, build, route-tree guard. The image builds --no-cache on node:22-alpine and serves — routes fall back, missing files 404, config.json is no-store, and the app renders under a strict CSP.

🤖 Generated with Claude Code

RohanReddy44 and others added 3 commits September 23, 2026 16:27
Bun was chosen because npm could not install this tree at all, crashing
with "Cannot read properties of null (reading 'edgesOut')". That turned
out to be one dependency, not the tree: npm installs cleanly with vitest
removed and crashes in #loadPeerSet with it present, because vitest 4
declares a large optional-peer set that npm's arborist cannot walk.
Vitest 5 does not have the problem, so the fix is an upgrade rather than
a workaround.

What forced the question was the container build failing with "Cannot
find package 'tsx:'". vite.config.ts passes virtualRouteConfig as a path,
so the router generator loads that TypeScript file through tsx — a Node
loader. The fix on Bun was to install Node beside it, which is two
runtimes to work around one plugin. It also failed only intermittently,
depending on whether the generator's cache re-ran, so the build worked on
one machine and not the next.

Node is what an adopter already has, and nothing runs a JavaScript
runtime in production anyway — the deployed artifact is static files
behind nginx. This is a build-time choice only.

Two things npm surfaced that bun had absorbed silently:

- @vitest/coverage-v8 was pinned to 4.0.17 while vitest was ^4.1.11, a
  real conflict
- vitest 3 would also have installed, but carries a moderate path
  traversal advisory covering 2.1.0-beta.1 to 4.1.10. Downgrading would
  have introduced a vulnerability the repo did not have

packageManager, the Dockerfile, CI, the hooks and the docs all follow.
new:screen runs under tsx, which the tree already carries. No
--legacy-peer-deps anywhere: it hides exactly the kind of conflict found
here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A 250-line code generator from the starter template, creating a screen
directory tree plus optional page and api folders.

This is a single-screen reference client and the scope is deliberately
chat and nothing else, so a generator for screens nobody should be adding
is a trap rather than a convenience. Its default parent was also
app/(_authenticated), a route group deleted with sign-in, so its output
would not have compiled.

Its runner had just been changed from bun to tsx, which meant depending
on a package the tree only carries transitively, through the router
generator. Deleting it removes that too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A whole-graph sweep with knip. Unused exports go from 35 to 20, unused
types from 7 to 0, and the 20 that remain are all src/components/ui —
vendored shadcn kept as a complete API, which the knip gate excludes on
purpose. Tree-shaking already drops them from the bundle.

**The recording half of audio-utils was dead.** setupAudioRecording,
setupAudioVisualization and stopRecording had no callers — chat-input has
its own recording code — and removing them orphaned four private helpers
and an interface in turn. 374 lines to 93. What survives is playback:
playTTS, stopAudio, pauseAudio, resumeAudio.

**The per-screen query layer was empty scaffolding.** queries.ts and
mutations.ts were `export const X = {} as const` with a TODO, re-exported
through two barrels and a misspelled `componets/` directory. The
queryClient and its MutationCache stay — that part is real and is where
the first thing needing caching or invalidation plugs in — but the empty
files are gone, and a comment now says why nothing uses it yet: chat
streams into the Zustand store rather than being cached, and it is the
only live endpoint.

`OnRequestError` was a React component returning an empty `<div>`, used as
a MutationCache onError callback. The element went nowhere and only the
logging ever had an effect. Now a plain function.

**Telemetry left two modules and a dependency behind.** getBrowserInfo and
getFingerprintId were re-exported by a barrel and called by nothing; they
fed device identity into analytics that no longer exist.
@fingerprintjs/fingerprintjs, 648 KB, existed only for the second.

**ConfigProvider's React context had no consumer.** Nothing called
useConfig — everything reads getConfig() directly — so the context, the
hook and the Provider wrapper are gone. What remains is the effect that
applies theme variables, which was always its real job.

Also dropped: the dead half of qa-utils (QUESTION_ANIMAL_SCOPE and the
only function that read it), STUB_LOCATION, and `export` on seven symbols
used only inside their own file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@RohanReddy44
RohanReddy44 merged commit 5843b11 into main Sep 23, 2026
4 checks passed
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