esdev create

Shell
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, its runtime: types installed and wired into tsconfig.json, and — for a project that runs a server — a deploy line that is already narrow:

Shell
esrun --allow-read=./dist --allow-env=PORT --allow-listen=8080 \
      --allow-signals=SIGTERM,SIGINT dist/server.js

A project that deploys to a static host has no such line, because it runs nothing. Which of the two you get is the mode.

Templates

Shell
esdev create --list
esdev create my-app --template=react --mode=fullstack
reactReact + react-router — a static site, or an app with a server of its own
apiA JSON API — one route, a URLPattern router. Nothing it ships depends on
vanillaTypeScript and the DOM — no framework
micro-uiMicro apps with Micro-UI — framework-free UI with a tiny reactive core
libA publishable package — module tree, .d.ts, exports wired

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.

A template is a scaffold, not a demo

What each one writes is a project that runs and one page — its name, what it was built with, the file to edit, three links — or for api, one route answering the same in JSON. That page is there to be deleted.

Nothing else is in it: no blog, no task store, no counter. What is kept is the machinery a project needs on its first day and would otherwise have to be assembled — the route table, the error boundary that renders a real 404, one render shared by the server and the static build, Fast Refresh, the security headers, the SIGTERM drain, the access log, and a permission line that is already narrow.

Both react modes are that one page on a route table, with the layout and the error boundary wired. What differs is what runs it — see Modes.

Modes

Some templates are two projects wearing one name. react is: an app with a server of its own is not the same project as a site that deploys to a static host — different files, a different esdev.json, and in one case no capabilities at all. Scaffolding the union of them and leaving you to delete half is how a starter ends up shipping a server nobody runs.

--mode=static (default)--mode=fullstack
Runs in productionNothingsrc/server.tsx, under esrun
npm run buildEvery route prerendered to dist/static/Server and browser bundles into dist/
npm run build:spaOne shell, routed in the browser
GrantsNone — nothing runsread, env, listen, signals
DeployAny static hostdist/, whole

SSG and SPA are one mode on purpose. Which of the two a site wants is a deployment decision that moves with the content, and both come out of the same routes and components with no file edited — the switch is which script you run. Which routes are prerendered is staticPaths() in src/paths.ts, and a route left out of it is rendered in the browser instead.

A fullstack project is a file you own answering requests: a Content-Security-Policy with a per-response nonce, a SIGTERM that drains before it exits, immutable caching on hashed assets, HEAD answered with the headers and nothing after them, and one JSON log line per request.

It asks, or it writes files and stops

On a terminal it asks which template, which mode if that template has more than one, and whether to install — as a menu you arrow through:

TEXT
? Which template?
    api      A JSON API — one route, a URLPattern router, a narrow grant
    lib      A publishable TypeScript package — module tree, .d.ts
  ❯ react    React + react-router — a static site, or an app with a server  (default)
    vanilla  TypeScript and the DOM — no framework
    micro-ui  Micro apps with Micro-UI — framework-free UI
  ↑/↓ move · 1-9 jump · enter select · esc cancel

It is drawn where the cursor already is and is replaced in place by one line naming the answer, so the scrollback is left holding a transcript of what was asked and what was chosen rather than a cleared screen. Esc at the first question cancels: nothing is written, and the exit status is zero.

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:

Shell
esdev create my-app --template=react --mode=fullstack
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".

TEXT
error: my-app is not empty.

Options

--template=<name>Which template (default react)
--mode=<name>Which shape of it, where it has more than one: static or fullstack
--install[=<manager>]Install after writing: npm, bun, pnpm, yarn
--no-installWrite the files and stop
-y, --yesTake every default; never ask
--listList the templates and exit
--forceWrite into a directory that already holds something
The name comes from the directory

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.

Last updated on
Edit this page