Skip to content

Make webjs db/test commands runtime-native (Bun-safe, drop npx/node --test) #570

@vivek7405

Description

@vivek7405

Problem

Several webjs CLI commands hard-code a runtime/tool that only exists under Node, so they break under Bun (and in a Node-less oven/bun image):

  • webjs db <generate|migrate|push|studio> shells npx drizzle-kit. A pure oven/bun image has no npx (it ships only a node fallback shim), so webjs db migrate fails at container boot.
  • webjs test --server spawns process.execPath --test. Under Bun that becomes bun --test, which is invalid (Bun's runner is the bun test subcommand; bun --test errors with "Cannot use test outside of the test runner").
  • webjs test --browser shells npx wtr (same npx-absent problem under Bun).

This forced #541 (Bun-first scaffold) to keep a node:24-alpine base and copy the Bun binary into the image, instead of a clean FROM oven/bun:1. Making the commands runtime-native removes that workaround and lets a bun scaffold ship a pure oven/bun Dockerfile.

Design / approach

The codebase already has the pattern: webjs typecheck resolves the app's own tsc via createRequire(join(process.cwd(), 'package.json')) and spawns it with process.execPath (so it runs under whatever runtime is current, Node or Bun). Apply the same to the other commands:

  • webjs db: resolve the drizzle-kit bin via createRequire from the app cwd and spawn it with process.execPath (no npx/bunx). Verified: bun <drizzle-kit-bin> --version runs cleanly (drizzle-kit v1.0.0-rc.3), so drizzle-kit works under Bun.
  • webjs test --browser: resolve the wtr (@web/test-runner) bin the same way and spawn with process.execPath (drop npx wtr). (wtr-under-bun should be verified; if it does not run under Bun, gate this one to keep node for the browser layer and document it.)
  • webjs test --server: this is the hard one. node --test and bun test are different runners with different flag shapes and partially different node:test semantics. Detect the runtime (process.versions.bun) and dispatch: node --test <files> on Node, bun test <files> on Bun. Verify the scaffold's example node:test-style tests pass under bun test (the repo's own bun matrix already runs node:test files under bun test, so this is plausible), and reconcile the e2e gating (WEBJS_E2E) + the wtr browser layer.

Scope can land incrementally: the webjs db fix alone makes a pure oven/bun Dockerfile viable (the container only runs start -> start.before -> webjs db migrate, never tests). The webjs test fixes are what make a fully Node-free CI possible.

Implementation notes (for the implementing agent)

  • Where to edit, all in packages/cli/bin/webjs.js:
    • webjs db case: spawn('npx', ['drizzle-kit', ...kitArgs, ...args], ...) at L221. Replace with a resolved-bin + process.execPath spawn.
    • webjs test --server: spawn(process.execPath, ['--test', ...testFiles], ...) at L301. Branch on process.versions.bun: bun test vs node --test.
    • webjs test --browser: spawn('npx', ['wtr'], ...) at L314 and spawn('npx', ['wtr', '--files', ...], ...) at L324. Resolve the wtr bin.
  • Prior art to copy: the webjs typecheck case (L443-L455) resolves tsc via createRequire(join(process.cwd(), 'package.json')) and spawns process.execPath, [tscPath, ...]. webjs db seed (L211) already spawns process.execPath, [seedFile]. Mirror these.
  • Landmines:
    • bun --test is NOT bun test (the flag form is invalid). Use the subcommand form.
    • oven/bun has a node shim at /usr/local/bun-node-fallback-bin/node but NO npx/bunx-as-npx. Do not rely on npx.
    • Resolving the bin from the app cwd (not import.meta.url) matters: the bin must come from the APP's node_modules (drizzle-kit / wtr are the app's devDependencies), like webjs typecheck resolves the app's tsc.
    • drizzle-kit's entry is drizzle-kit/bin.cjs (a .cjs); confirm the resolve target.
  • Invariants: packages/ stays plain .js + JSDoc (no .ts). Keep the commands' Node behaviour byte-identical (this must not regress Node users).
  • Tests + docs: packages/cli/test/ for the dispatch logic (unit, with an injectable spawn); a Bun cross-runtime assert if feasible. Run the 4 dogfood apps' webjs db generate/migrate on BOTH runtimes. Docs: packages/cli/AGENTS.md (the command table), agent-docs/testing.md if the test-runner behaviour changes, and the deployment docs (the pure-oven/bun Dockerfile becomes possible).

Acceptance criteria

  • webjs db generate / migrate / push run with NO npx dependency, on both Node and Bun, including in a Node-less oven/bun image.
  • webjs test --server runs the suite on both runtimes (node --test on Node, bun test on Bun), with the e2e gating preserved.
  • webjs test --browser resolves wtr without npx (or, if wtr cannot run under Bun, is explicitly gated to Node with a documented reason).
  • Node behaviour is unchanged (a counterfactual / existing tests prove no Node regression).
  • The 4 in-repo apps' db commands work on both runtimes.
  • Once landed, Add a Bun-first scaffold mode (--runtime bun) across all 3 templates #541's bun Dockerfile can switch to a clean FROM oven/bun:1 (follow-up on that PR).
  • Tests cover the new dispatch (unit, injectable spawn) + a cross-runtime check where feasible.
  • Docs updated: packages/cli/AGENTS.md, deployment docs, testing docs.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type
No fields configured for issues without a type.

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions