Open-Source Wikis

/

Deno

/

Deno

/

Architecture

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/&lt;subcommand&gt;<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 --> Eszip

Three 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>/ or cli/tools/<name>.rs holds 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) implements ModuleLoader for deno_core, handling HTTPS imports, npm: and jsr: specifiers, transpilation, and source map registration.
  • Type checking. cli/type_checker.rs (~35K bytes) and cli/tsc.rs drive TypeScript type checking, either via the bundled tsc snapshot or via the new tsgo IPC 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.rs alone 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 any deno run invocation.
  • web_worker.rs (1,300 lines) — WebWorker, the runtime used for Worker instances spawned from JS.
  • runtime/permissions/ — the deno_permissions crate, which gates every privileged op behind a PermissionsContainer.
  • runtime/ops/ and runtime/js/ — runtime-level ops (e.g., Deno.exit, runtime bootstrap) and the JS bootstrap code that wires the global object.
  • worker_bootstrap.rsBootstrapOptions carrying 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.rs that calls deno_core::extension!(...) to declare its ops and JS files.
  • One or more *.rs files implementing the ops with #[op2].
  • *.js or *.ts files 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/core is deno_core itself — the V8 + Tokio runtime layer that everything else builds on. Defines JsRuntime, ModuleLoader, Extension, the op2 macro proc-macro support, source maps, and snapshots.
  • libs/resolver is deno_resolver — the shared module resolution algorithm used by both the CLI and the LSP.
  • libs/node_resolver implements Node.js-style require/ESM resolution including package.json exports, conditional exports, and BYONM mode.
  • libs/npm, libs/npm_cache, libs/npm_installer, libs/npmrc together implement the npm client.
  • libs/lockfile parses and writes deno.lock.
  • libs/eszip packages module graphs into a single .eszip file used by deno compile.
  • libs/serde_v8 is the serde <-> V8 bridge used everywhere ops cross the JS/Rust boundary.
  • libs/config parses deno.json/deno.jsonc, including compilerOptions, imports, tasks.
  • libs/cache_dir, libs/dotenv, libs/inspector_server, libs/package_json, libs/typescript_go_client, libs/ops, libs/maybe_sync fill 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 code

Every 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.

Architecture – Deno wiki | Factory