esdev
Use esrun to run JavaScript ES modules, usually the bundle you deploy. Use esdev during development: it runs TypeScript and JSX, bundles projects, watches files, runs tests and provides debugging tools.
esdev create my-app # scaffold a project that already runs esdev start # the dev loop: build, run, rebuild, reload esdev app.ts # TypeScript, JSX, run esdev --watch server.ts # rerun on change, drain on restart esdev test # run the test files esdev build server.ts # one deployable ES module esdev build --lib src # a publishable library, with its .d.ts esdev --inspect app.ts # attach a debugger esdev --trace-permissions app.ts # print the esrun line to deploy with esdev upgrade # update esdev to the latest release
Ship the artifact and run it under esrun. esdev has a debugger port and a file watcher in it; esrun cannot be built with either.
It never changes what a program does
Same prelude, same snapshot, same providers, same capability enforcement — all shared with esrun, in one crate both binaries sit on. A program cannot behave one way here and another in production. What esdev changes is everything around a run: how source becomes modules, watching, restarting, attaching, discovering, reporting, building.
The permission flags are the same ones and mean the same thing, with one deliberate difference: esdev grants everything by default, esrun grants nothing. An inner loop should not need flags to run the program you are in the middle of writing; a deployment should say what it may reach. Everything else — the names, the scope lists, the rules, the error messages — is one implementation shared by both binaries.
esdev server.ts # everything granted esdev --deny-all --allow-listen=8080 server.ts # ...or exactly esrun's grant
esdev --trace-permissions server.ts closes the gap: it prints the esrun line that grants exactly what a run reached for. See Tracing permissions.
Get it
The installer places both binaries, and esdev updates itself from there:
curl -fsSL https://esrun.opentechf.org/install.sh | bash esdev upgrade # ...and later
esdev upgrade replaces the running binary with the newest esdev release for your platform — the same thing esrun upgrade does for the other one, and the same outcome as re-running the installer. Each resolves its own release: the two are versioned separately and tagged separately.
From source:
cargo build-dev # → target/release/esdev ES_RUNTIME_INSPECTOR=1 cargo build-dev # ...with the debugger
Released binaries include the debugger, which --inspect and esdev test --coverage need. A source build has it only with the second line, and a build without it says so instead of listening on nothing.
Source, read the way the build reads it
esdev runs source the way esdev build would bundle it, so a module means one thing whether it is run, tested or built:
| TypeScript and JSX | .ts, .tsx, .mts, .cts and .jsx run directly; types are erased, not checked. JSX compiles as the project's jsx says |
| Imports | ./util finds util.ts, and @/db resolves through the project's aliases and tsconfig.json paths (Imports) |
| Plugins | The project's plugins compile what they claim — a framework's .jsx, an .mdx |
| CommonJS packages | Converted to ES modules the first time they are imported (CommonJS packages) |
esrun does none of this: it runs the bundle, where all of it is already settled. See TypeScript setup for editor types.
The project
Commands that use project settings read esdev.json from the project directory, then combine it with the command's flags. A flag takes precedence. Use --config=<path> to name a different file. See Project config.
Watch
For the standalone --watch command, see Watch mode. For a configured project that rebuilds and serves targets, see The dev loop.
Three modules only esdev serves
Development machinery is available to the program as well as to the command line — as runtime: modules that exist under esdev and nowhere else:
| Module | |
|---|---|
runtime:build | the bundler, with a plugin system of this project's own — hooks running in your isolate |
runtime:test | test() and the assertions esdev test runs |
runtime:watch | file-change events, with a watch set you can grow while it runs |
The first and last are here for the thing a subcommand cannot be: a framework's dev server, which bundles a route on demand, keeps the chunk in memory, serves it, and on a save drops exactly the chunks that used the changed file. The cache, the browser websocket and the route table belong to that program, so what it needs from this toolchain is an API rather than a command.
import { build } from "runtime:build"; import { watch } from "runtime:watch"; const bundle = await build({ input: route, plugins: [mdx()] }); const { output, watchFiles } = await bundle.generate({ codeSplitting: false });
esrun serves none of them: importing one there fails at load rather than half-working. See the bundler bridge for how a parallel bundler and a single-threaded isolate share the work.
Flags
Everything esrun takes, plus these. Every flag is --flag or --flag=value — a value is never a separate argument — and they come before the script.
| Flag | |
|---|---|
--watch | Rerun the program when its source changes |
--inspect[=<addr>] | Serve the Chrome DevTools Protocol (default 127.0.0.1:9229) |
--inspect-brk[=<addr>] | ...and stop before the first statement |
--trace-permissions | Print the permissions the run used |
--config=<path> | Read this project file instead of ./esdev.json |
--install-types | Add @opentf/esrun-types and wire up tsconfig.json |
| Subcommand | |
|---|---|
esdev create <dir> | Scaffold a project that already runs |
esdev init [dir] | Start a bare project, or adopt existing code |
esdev start | The dev loop: build, run, rebuild, reload |
esdev test [filter...] | Run the test files |
esdev build <entry> | Bundle into one ES module |
esdev build --lib <srcdir> | Build a publishable library |
esdev bench [filter...] | Run the benchmark files |
esdev typecheck [args...] | Typecheck with the project's own tsc |
esdev preview | Serve the release build before deploying it |
esdev upgrade [--dry-run] | Replace this binary with the latest esdev release, or only say whether there is one |
Each subcommand takes --help, and every --help is short on purpose: the grammar and the flags, then a link to the page that has the rest.
What it does not include
esdev does not provide a formatter, linter, task runner, or framework-specific dev server. esdev create can install dependencies while scaffolding; use your package manager to add or update them later. Use tools such as oxfmt and oxlint for formatting and linting. For custom framework servers, esdev exposes the bundler and watcher as runtime: modules. See the bundler API and the watcher API.