esdev create
esdev create my-app cd my-app npm install # or bun, pnpm, yarn npm run dev
What you get is a project with its esdev.json written, its entry named by the script tag in its index.html, and a deploy line that is already narrow:
esrun --allow-read=./dist --allow-env=PORT --allow-listen=8080 \ --allow-signals=SIGTERM,SIGINT dist/server.js
Templates
esdev create --list esdev create my-app --template=react
react | React + react-router — server-rendered, hydrated, prerenderable to static HTML |
api | A JSON API — routing, validation, error handling. No dependencies |
vanilla | TypeScript and the DOM — no framework |
lib | A publishable package — module tree, .d.ts, exports wired |
The react template is a route table with loaders, nested layouts, dynamic segments and error boundaries, served by a file you own: a Content-Security-Policy with a per-response nonce, a SIGTERM that drains before it exits, immutable caching on hashed assets, and one JSON log line per request.
They are baked into the binary, so create works offline and always writes a project the esdev that wrote it can build. Nothing is downloaded.
It asks, or it writes files and stops
On a terminal it asks which template and whether to install:
Which template? 1) api A JSON API — routing, validation, error handling. No dependencies 2) lib A publishable TypeScript package — module tree, .d.ts 3) react React + react-router — server-rendered, hydrated (default) 4) vanilla TypeScript and the DOM — no framework >
Away from one — a pipe, a CI job, anything with CI set — it takes the defaults and says nothing. A prompt in a script is a script that hangs.
Every question has a flag, so nothing is only reachable by answering one:
esdev create my-app --template=api --install=bun esdev create my-app --yes # defaults, no questions esdev create my-app --no-install
Only package managers this machine actually has are offered. Unattended it installs nothing: there is no lockfile yet to say which one this project uses, and guessing wrong leaves the wrong one behind — which is a reason not to guess, not a reason not to ask.
It still does no git init.
It never overwrites
A directory that already holds something is refused. --force writes among what is there and still leaves every existing file alone — it means "write here too", never "write over".
error: my-app is not empty.
Options
--template=<name> | Which template (default react) |
--install[=<manager>] | Install after writing: npm, bun, pnpm, yarn |
--no-install | Write the files and stop |
-y, --yes | Take every default; never ask |
--list | List the templates and exit |
--force | Write into a directory that already holds something |
esdev create weather-app writes "name": "weather-app" into package.json and <title>weather-app</title> into the document. A directory name that npm would reject — My App — is lowercased and hyphenated on the way in.