denoland/deno
Architecture
Deno runs as a single Rust binary that embeds V8 via deno_core and assembles a JavaScript runtime out of pluggable Rust extensions. This page maps the major components and how they fit together.
The 10,000-foot view
graph TD
subgraph User
CLI["deno CLI<br/>cli/main.rs"]
end
subgraph CliCrate["cli/ — deno crate"]
Flags["args/flags.rs<br/>(clap parser)"]
Tools["tools/<subcommand><br/>(run, test, fmt, lsp, …)"]
Factory["factory.rs<br/>(dependency wiring)"]
ModLoader["module_loader.rs"]
TypeChk["type_checker.rs"]
Lsp["lsp/"]
end
subgraph Runtime["runtime/ — deno_runtime crate"]
Worker["worker.rs"]
WebWorker["web_worker.rs"]
Permissions["runtime/permissions/"]
end
subgraph Core["libs/core — deno_core"]
V8[("V8 + Tokio")]
Ops["op2 dispatch"]
ModGraph["JsRuntime / ModuleMap"]
end
subgraph Exts["ext/ — 30+ extension crates"]
FS["deno_fs"]
Net["deno_net"]
Web["deno_web"]
Node["deno_node"]
WebGPU["deno_webgpu"]
OtherExts["…"]
end
subgraph Libs["libs/ — reusable crates"]
NpmLib["deno_npm"]
Resolver["deno_resolver"]
Lockfile["deno_lockfile"]
Eszip["eszip"]
Cfg["deno_config"]
end
CLI --> Flags
Flags --> Tools
Tools --> Factory
Factory --> Worker
Factory --> ModLoader
Factory --> TypeChk
Tools --> Lsp
Worker --> V8
Worker --> Exts
WebWorker --> V8
WebWorker --> Exts
Worker --> Permissions
Exts --> Ops
Ops --> V8
Tools --> Libs
ModLoader --> Resolver
ModLoader --> NpmLib
ModLoader --> EszipThree layers, top to bottom
1. The CLI layer (cli/)
The deno binary lives in the deno crate. cli/main.rs is a six-line stub that calls deno::main(); the real entry point is cli/lib.rs:1, which declares roughly 25 sibling modules and orchestrates subcommand dispatch through run_subcommand (cli/lib.rs).
Everything user-visible flows through this layer:
- Flag parsing.
cli/args/flags.rs(~15.9K lines) is a single huge clap parser that defines every subcommand and flag. New CLI flags go here first. - Subcommand handlers.
cli/tools/<name>/orcli/tools/<name>.rsholds the implementation of each subcommand (run,test,fmt,lint,compile,bundle,lsp,task,repl,install,outdated,bump-version, etc.). - Dependency wiring.
cli/factory.rs(1,441 lines) is the central object factory that lazily constructs the module loader, npm resolver, type checker, file fetcher, lockfile, etc., based on the parsed flags and the discovered config files. - Module loading.
cli/module_loader.rs(1,668 lines) implementsModuleLoaderfordeno_core, handling HTTPS imports,npm:andjsr:specifiers, transpilation, and source map registration. - Type checking.
cli/type_checker.rs(~35K bytes) andcli/tsc.rsdrive TypeScript type checking, either via the bundledtscsnapshot or via the newtsgoIPC client (libs/typescript_go_client). - LSP.
cli/lsp/(29 files, ~1.1M total bytes) is a full-featured Language Server Protocol implementation used by the VS Code Deno extension and other editors.language_server.rsalone is 4,580 lines.
2. The runtime layer (runtime/)
deno_runtime is the crate that turns a deno_core::JsRuntime into something that can actually run user code. It owns:
worker.rs(1,348 lines) —MainWorker, the runtime used for the main thread of anydeno runinvocation.web_worker.rs(1,300 lines) —WebWorker, the runtime used forWorkerinstances spawned from JS.runtime/permissions/— thedeno_permissionscrate, which gates every privileged op behind aPermissionsContainer.runtime/ops/andruntime/js/— runtime-level ops (e.g.,Deno.exit, runtime bootstrap) and the JS bootstrap code that wires the global object.worker_bootstrap.rs—BootstrapOptionscarrying the configured runtime state into JS at startup.
The runtime imports every extension crate in ext/ and registers them as deno_core::Extensions when constructing a worker.
3. The extension layer (ext/)
Each subdirectory of ext/ is a self-contained crate that exposes a slice of native functionality to JS:
| Extension | What it provides |
|---|---|
ext/web |
DOM-style globals: URL, URLPattern, Event, AbortController, MessageChannel, TextEncoder, Blob, FileReader, streams, base64, performance API |
ext/fetch |
fetch, Request, Response, Headers, FormData |
ext/net |
TCP/UDP/Unix sockets, Deno.listen, Deno.connect |
ext/http |
HTTP server primitives backing Deno.serve |
ext/fs |
Deno.open, Deno.readFile, Deno.writeFile, watch APIs |
ext/node |
The Node.js compatibility layer; exposes node:* modules via JS polyfills (ext/node/polyfills/) |
ext/node_crypto, ext/node_sqlite |
Larger Node modules split into their own crates |
ext/crypto |
WebCrypto |
ext/webgpu, ext/websocket, ext/webstorage, ext/webidl |
Other web platform bindings |
ext/kv, ext/cron, ext/cache |
Deno-specific APIs (Deno.openKv, Deno.cron, Web Cache API) |
ext/ffi, ext/napi |
FFI / native addon support |
ext/io, ext/os, ext/process, ext/signals, ext/tls, ext/url, ext/console, ext/image, ext/telemetry, ext/broadcast_channel, ext/bundle, ext/rt_helper |
Smaller capability bundles |
Each extension typically ships:
- A
lib.rsthat callsdeno_core::extension!(...)to declare its ops and JS files. - One or more
*.rsfiles implementing the ops with#[op2]. *.jsor*.tsfiles providing the higher-level API surface that user code interacts with.
See Extensions for the full list and details on a few of the larger ones.
Library crates (libs/)
The libs/ directory contains roughly 24 crates that are also published to crates.io independently and are reused outside Deno:
libs/coreisdeno_coreitself — the V8 + Tokio runtime layer that everything else builds on. DefinesJsRuntime,ModuleLoader,Extension, theop2macro proc-macro support, source maps, and snapshots.libs/resolverisdeno_resolver— the shared module resolution algorithm used by both the CLI and the LSP.libs/node_resolverimplements Node.js-stylerequire/ESM resolution includingpackage.jsonexports, conditional exports, and BYONM mode.libs/npm,libs/npm_cache,libs/npm_installer,libs/npmrctogether implement the npm client.libs/lockfileparses and writesdeno.lock.libs/eszippackages module graphs into a single.eszipfile used bydeno compile.libs/serde_v8is theserde<-> V8 bridge used everywhere ops cross the JS/Rust boundary.libs/configparsesdeno.json/deno.jsonc, includingcompilerOptions,imports,tasks.libs/cache_dir,libs/dotenv,libs/inspector_server,libs/package_json,libs/typescript_go_client,libs/ops,libs/maybe_syncfill out the rest.
See Packages for the per-crate breakdown.
Request lifecycle: deno run script.ts
Most subcommands follow the same shape. Tracing deno run script.ts end-to-end:
sequenceDiagram
participant User
participant Bin as cli/main.rs
participant Lib as cli/lib.rs (run_subcommand)
participant Factory as cli/factory.rs
participant Worker as runtime/worker.rs (MainWorker)
participant ML as cli/module_loader.rs
participant V8 as deno_core::JsRuntime
User->>Bin: deno run script.ts
Bin->>Lib: deno::main()
Lib->>Lib: flags_from_vec_with_initial_cwd
Lib->>Factory: CliFactory::from_flags(...)
Factory->>Factory: build module loader, npm, type checker, permissions
Lib->>Worker: factory.create_main_worker(...)
Worker->>V8: JsRuntime::new(extensions=[...])
Worker->>ML: load_main_module("file:///.../script.ts")
ML->>ML: fetch + transpile + npm/jsr resolve
ML-->>V8: ModuleSource
V8-->>Worker: evaluate
Worker-->>Lib: exit codeEvery other subcommand replays a variant of this flow: deno test instantiates a worker per test file, deno fmt/deno lint skip the runtime entirely, deno compile wraps the module graph in an eszip and emits a self-extracting binary, deno lsp enters an event loop on stdio instead of running JS.
Cross-cutting concerns
| Concern | Where it lives |
|---|---|
| Permissions | runtime/permissions/lib.rs, gates every privileged op |
| Module graph & cache | cli/graph_util.rs, cli/cache/, libs/cache_dir |
| TypeScript type checking | cli/type_checker.rs, cli/tsc.rs, cli/tsc/ (bundled tsc), libs/typescript_go_client |
| Snapshots / startup speed | cli/snapshot/, runtime/snapshot.rs, cli/js/ (bundled JS) |
| Telemetry | ext/telemetry (OpenTelemetry exporter) |
| Source maps | deno_core::source_map, applied in runtime/fmt_errors.rs |
| Logging | deno_core::log, DENO_LOG env var |
The rest of this wiki documents each of these in more depth.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.