Problem
webjs runs on Node 24+ OR Bun, and all 4 in-repo apps now deploy on Bun. But a scaffolded app is still npm/node-flavored everywhere: the package.json scripts call npm, the lockfile is package-lock.json, the Dockerfile uses a node base, and the README + agent-config markdown all show npm run commands. A Bun-first user who runs bun create webjs my-app gets an app whose generated files do not speak Bun, so they have to convert it by hand. We should let the scaffold emit a fully Bun-flavored app.
Design / approach
Add a RUNTIME dimension orthogonal to --template (keep the exactly-3 templates invariant intact, runtime is a separate axis), defaulting to node:
webjs create <name> --runtime bun (and the same flag forwarded by create-webjs).
- Auto-adopt bun when the scaffold is invoked through bun:
scaffoldApp() already has detectPackageManager() which reads npm_config_user_agent and returns bun. So bun create webjs my-app can imply bun mode, with the explicit flag overriding.
ENTRY POINT IS ALREADY BUN-COMPATIBLE (verified): bun create webjs maps to bunx create-webjs per bun's own bun create --help ("NPM: Runs bunx create-"), which resolves the existing packages/wrappers/create-webjs bin. No new package is needed. bunx create-webjs@latest my-app is the explicit pin-latest form. One flag-forwarding difference to document: npm needs the -- separator (npm create webjs@latest my-app -- --template api) while bun forwards flags directly (bun create webjs my-app --template api).
Surfaces bun mode must rewrite in the generated app:
- package.json scripts:
bun run dev / start, the db:* scripts, bun test, predev / prestart hooks.
- Lockfile: generate and commit
bun.lock (text JSONC, Bun 1.2+, git-diffable) instead of package-lock.json. Adjust .gitignore / any CI to expect it.
- Dockerfile: an
oven/bun base instead of node:NN-alpine + copied-in bun binary, IF every build step (prisma generate, the ui registry copy) runs on the pure-bun base. Verify before switching the base.
compose.yaml: the service command runs bun.
- README + agent-config markdown (
AGENTS.md, CONVENTIONS.md, .cursorrules, .agents/rules/workflow.md, .github/copilot-instructions.md): show bun commands, and show both bun create webjs and bunx create-webjs@latest invocation styles.
.claude/hooks/*: anywhere they shell out to npm.
TWO BUN-SPECIFIC GOTCHAS the implementation must handle (both verified):
-
The --bun shebang override. bun run dev where dev is webjs dev execs the webjs bin under NODE, because the bin is #!/usr/bin/env node. To actually run on bun you need bun --bun run dev (the --bun flag overrides shebangs for the script and its subprocesses), which is exactly what the docs already prescribe. So the bun-mode scripts/docs MUST use the --bun form, or the "bun" app silently runs on node.
-
trustedDependencies. Unlike npm, bun does not run a dependency's postinstall by default (security); a package needs listing in "trustedDependencies" in package.json for its postinstall to run on bun install. webjs generates the Prisma client via the app's own predev / prestart (prisma generate), which always runs, so that path is safe, but the bun-mode template should still set a sane trustedDependencies so no dep silently skips its postinstall.
NON-GOALS: do not flip the in-repo example apps' local scripts (separate concern); do not add a 4th template (runtime is orthogonal to the 3 templates).
Acceptance criteria
Implementation notes (where to edit)
- Scaffold logic lives in
packages/cli/lib/create.js. scaffoldApp() writes package.json PROGRAMMATICALLY (around L275, the scripts block around L280) and tsconfig (around L335), and already has detectPackageManager() (around L23, returns bun from the UA). Branch the scripts, the lockfile choice, and trustedDependencies here on the resolved runtime.
- CLI flag plumbing:
packages/cli/bin/webjs.js parses --template via flag(rest, '--template', ...) (around L400) against TEMPLATES (L40). Add --runtime node|bun here and thread it into scaffoldApp() opts. Mirror the same flag in packages/wrappers/create-webjs/bin/create-webjs.js (its own flagValue('--template') parsing) and forward to scaffoldApp.
- saas-only files:
packages/cli/lib/saas-template.js.
- Verbatim template files copied as-is:
packages/cli/templates/Dockerfile, compose.yaml, plus the README and agent-config markdown under templates/. Bun mode needs either variants or a post-copy rewrite driven from create.js.
- Tests:
test/scaffolds/ (scaffold-template-validation, scaffold-integration, scaffold-ui-integration).
- Invariants to respect: keep EXACTLY 3 templates (runtime is orthogonal, do not add a 4th);
packages/ stays plain .js + JSDoc (no .ts).
Problem
webjs runs on Node 24+ OR Bun, and all 4 in-repo apps now deploy on Bun. But a scaffolded app is still npm/node-flavored everywhere: the package.json scripts call
npm, the lockfile ispackage-lock.json, the Dockerfile uses a node base, and the README + agent-config markdown all shownpm runcommands. A Bun-first user who runsbun create webjs my-appgets an app whose generated files do not speak Bun, so they have to convert it by hand. We should let the scaffold emit a fully Bun-flavored app.Design / approach
Add a RUNTIME dimension orthogonal to
--template(keep the exactly-3 templates invariant intact, runtime is a separate axis), defaulting to node:webjs create <name> --runtime bun(and the same flag forwarded bycreate-webjs).scaffoldApp()already hasdetectPackageManager()which readsnpm_config_user_agentand returnsbun. Sobun create webjs my-appcan imply bun mode, with the explicit flag overriding.ENTRY POINT IS ALREADY BUN-COMPATIBLE (verified):
bun create webjsmaps tobunx create-webjsper bun's ownbun create --help("NPM: Runs bunx create-"), which resolves the existingpackages/wrappers/create-webjsbin. No new package is needed.bunx create-webjs@latest my-appis the explicit pin-latest form. One flag-forwarding difference to document: npm needs the--separator (npm create webjs@latest my-app -- --template api) while bun forwards flags directly (bun create webjs my-app --template api).Surfaces bun mode must rewrite in the generated app:
bun run dev/start, thedb:*scripts,bun test,predev/prestarthooks.bun.lock(text JSONC, Bun 1.2+, git-diffable) instead ofpackage-lock.json. Adjust.gitignore/ any CI to expect it.oven/bunbase instead ofnode:NN-alpine+ copied-in bun binary, IF every build step (prisma generate, the ui registry copy) runs on the pure-bun base. Verify before switching the base.compose.yaml: the service command runs bun.AGENTS.md,CONVENTIONS.md,.cursorrules,.agents/rules/workflow.md,.github/copilot-instructions.md): show bun commands, and show bothbun create webjsandbunx create-webjs@latestinvocation styles..claude/hooks/*: anywhere they shell out tonpm.TWO BUN-SPECIFIC GOTCHAS the implementation must handle (both verified):
The
--bunshebang override.bun run devwheredeviswebjs devexecs thewebjsbin under NODE, because the bin is#!/usr/bin/env node. To actually run on bun you needbun --bun run dev(the--bunflag overrides shebangs for the script and its subprocesses), which is exactly what the docs already prescribe. So the bun-mode scripts/docs MUST use the--bunform, or the "bun" app silently runs on node.trustedDependencies. Unlike npm, bun does not run a dependency'spostinstallby default (security); a package needs listing in"trustedDependencies"in package.json for its postinstall to run onbun install. webjs generates the Prisma client via the app's ownpredev/prestart(prisma generate), which always runs, so that path is safe, but the bun-mode template should still set a sanetrustedDependenciesso no dep silently skips its postinstall.NON-GOALS: do not flip the in-repo example apps' local scripts (separate concern); do not add a 4th template (runtime is orthogonal to the 3 templates).
Acceptance criteria
webjs create <name> --runtime bunandbun create webjs <name>(auto-detected) both produce a Bun-flavored app across all 3 templates (full-stack / api / saas).bun --bun run(closing the shebang gotcha), the app commitsbun.lock(notpackage-lock.json), andtrustedDependenciesis set so Prisma and any postinstall dep work.compose.yamlrun on bun (the Dockerfile base decision is made and justified: pureoven/bunvs node-base + bun binary).bun create webjs/bunx create-webjs@latestinvocation styles.--runtime node(the default) is unchanged from today, so existing npm users see no difference.--runtime bunscaffold (and abun create webjsinvocation) passeswebjs checkand BOOTS on bun (test/scaffolds/+ a cross-runtime check). A node-mode scaffold still boots on node.packages/cli/AGENTS.mdreflect the--runtimeflag and the bun invocation forms.Implementation notes (where to edit)
packages/cli/lib/create.js.scaffoldApp()writes package.json PROGRAMMATICALLY (around L275, thescriptsblock around L280) and tsconfig (around L335), and already hasdetectPackageManager()(around L23, returnsbunfrom the UA). Branch the scripts, the lockfile choice, andtrustedDependencieshere on the resolved runtime.packages/cli/bin/webjs.jsparses--templateviaflag(rest, '--template', ...)(around L400) againstTEMPLATES(L40). Add--runtime node|bunhere and thread it intoscaffoldApp()opts. Mirror the same flag inpackages/wrappers/create-webjs/bin/create-webjs.js(its ownflagValue('--template')parsing) and forward toscaffoldApp.packages/cli/lib/saas-template.js.packages/cli/templates/Dockerfile,compose.yaml, plus the README and agent-config markdown undertemplates/. Bun mode needs either variants or a post-copy rewrite driven from create.js.test/scaffolds/(scaffold-template-validation, scaffold-integration, scaffold-ui-integration).packages/stays plain.js+ JSDoc (no.ts).