Open-Source Wikis

/

Deno

/

Systems

/

CLI

denoland/deno

CLI

Active contributors: Bartek Iwańczuk, David Sherret, Nathan Whitaker

Purpose

The cli/ directory implements the deno binary — flag parsing, subcommand dispatch, the dependency-injection container that wires module loading and type checking and npm together, and the per-subcommand handlers under cli/tools/. Every user-visible Deno command flows through here before reaching the runtime in runtime/ or the extensions in ext/.

Directory layout

cli/
├── main.rs                # 6-line entry point: deno::main()
├── lib.rs                 # 1,265 lines: run_subcommand dispatch
├── factory.rs             # 1,441 lines: CliFactory dependency injection
├── args/                  # CLI flag parser
│   ├── flags.rs           # 15,873 lines: every flag and subcommand
│   ├── flags_net.rs       # network-permission flag parsing
│   └── mod.rs             # config-file integration with flags
├── tools/                 # subcommand handlers
│   ├── run/, test/, lint/, fmt.rs, …
│   └── …                  # one per subcommand
├── lsp/                   # 29 files, the language server
├── module_loader.rs       # 1,668 lines: ModuleLoader implementation
├── file_fetcher.rs        # 1,234 lines: HTTPS/file/data: fetching
├── graph_util.rs          # 1,195 lines: module graph construction
├── type_checker.rs        # 1,138 lines: tsc + tsgo orchestration
├── tsc.rs                 # bundled tsc snapshot driver
├── tsc/                   # the bundled TypeScript JS (snapshotted)
├── jsr.rs                 # JSR registry client
├── npm.rs                 # 736 lines: npm registry integration
├── http_util.rs           # 841 lines: HTTPS client used everywhere
├── worker.rs              # 659 lines: CLI-side worker construction
├── lib/, rt/, snapshot/   # sub-crates: deno_lib, deno_rt, deno_snapshot
└── …

The key thing to internalize: cli/ is a single Cargo crate (deno) containing many sibling top-level files. New CLI concerns add a new file at this level rather than nesting deeper.

Key abstractions

Type / function File What it does
deno::main() cli/lib.rs Top-level entry point invoked by cli/main.rs
Flags cli/args/flags.rs Parsed CLI flags (clap derive macro)
DenoSubcommand cli/args/flags.rs Enum of every subcommand variant
flags_from_vec_with_initial_cwd cli/args/flags.rs Parser entry point
run_subcommand cli/lib.rs Big match on DenoSubcommand that spawns the right handler
spawn_subcommand cli/lib.rs Wraps each subcommand future on a Tokio task with boxed_local so debug builds don't blow the Windows stack
CliFactory cli/factory.rs Lazy DI container; constructs module loader, npm, type checker, etc.
CliFactory::from_flags cli/factory.rs The standard constructor invoked from each subcommand
LibWorkerFactoryRoots cli/lib/ (deno_lib crate) Bundle of root services used to construct workers

How it works

graph TD
    main["cli/main.rs<br/>main()"] --> deno_main["deno::main()<br/>cli/lib.rs"]
    deno_main --> parse["flags_from_vec_with_initial_cwd<br/>cli/args/flags.rs"]
    parse --> flags["Flags struct"]
    flags --> dispatch["run_subcommand<br/>cli/lib.rs"]
    dispatch --> spawn["spawn_subcommand → tokio task"]
    spawn --> tools_run["tools::run::run_script"]
    spawn --> tools_test["tools::test::run_tests"]
    spawn --> tools_fmt["tools::fmt::format"]
    spawn --> tools_lsp["lsp::start"]
    spawn --> tools_compile["tools::compile::compile"]
    spawn --> tools_x["tools::x::run"]
    spawn --> tools_more["…all other subcommands"]
    tools_run --> factory["CliFactory<br/>cli/factory.rs"]
    tools_test --> factory
    tools_compile --> factory
    factory --> ml["ModuleLoader"]
    factory --> npm["NpmResolver"]
    factory --> tc["TypeChecker"]
    factory --> ff["FileFetcher"]
    factory --> lf["Lockfile"]
    factory --> perms["PermissionsContainer"]

Subcommand dispatch

run_subcommand (cli/lib.rs:120+) is a single big match on DenoSubcommand with one arm per command. Recognized subcommands include Run, Test, Bench, Bundle, Compile, Coverage, Fmt, Lint, Init, Info, Install / Uninstall / Add / Remove / Outdated / Audit / ApproveScripts, Cache, Check, Clean, Doc, Eval, Repl, Task, Upgrade, Lsp, Jupyter, Deploy, Serve, BumpVersion, JSONReference, and the developer escape hatch X (which dispatches to tools/x.rs). Each arm:

  1. Wraps the handler in spawn_subcommand so the future runs on a fresh Tokio task with boxed_local().
  2. Calls into tools::<subcommand> to do the actual work.
  3. Returns an i32 exit code via the SubcommandOutput trait (defined at the top of cli/lib.rs).

The Run arm is itself a small dispatcher: it switches between stdin input, eszip-embedded scripts, regular module loading, watch mode, and special "no script given" behavior that falls through to Task (so deno run with no args lists tasks).

CliFactory

Most subcommand handlers immediately construct a CliFactory::from_flags(flags). The factory then exposes accessors that lazily build the heavy services on first use:

  • factory.module_loader_factory() → CLI's ModuleLoader
  • factory.npm_resolver() → npm dependency resolver
  • factory.type_checker() → orchestrator for type checking
  • factory.file_fetcher() → HTTPS + file: + data: fetcher
  • factory.cli_options() → resolved Deno config (deno.json, lockfile, etc.)
  • factory.create_main_worker(...) → constructs a MainWorker

Lazy construction matters because most subcommands only need a subset of services, and configuring (e.g.) the npm resolver involves discovery work that's expensive when unnecessary. See the source for the full surface — it's the single most important file to understand if you're adding subcommand behavior.

Flag parsing

cli/args/flags.rs is one of the largest files in the repo at 15,873 lines. It uses clap's derive macros to define:

  • One struct per subcommand (RunFlags, TestFlags, FmtFlags, …)
  • The DenoSubcommand enum that wraps them
  • The top-level Flags struct holding shared state (config-file path, log level, lockfile mode, permission flags, etc.)

Parsing happens in flags_from_vec_with_initial_cwd, which also handles a few hand-rolled cases (e.g., the -- argument-passthrough boundary, alternate flag forms for back-compat).

When adding a flag:

  1. Add the field to the relevant *Flags struct (or to Flags for global flags).
  2. Add the clap attribute (#[arg(long, ...)]).
  3. Wire it through cli/factory.rs if it should change service configuration.
  4. Use it in the subcommand handler.

Many flags also have a corresponding field in deno.json; those are wired in cli/args/mod.rs where flags merge with config.

Integration points

  • Reads from: cli/args/flags.rs (CLI input), libs/config (config file parsing), libs/lockfile (lockfile state), libs/cache_dir (DENO_DIR).
  • Constructs: runtime::worker::MainWorker via cli/factory.rs and cli/worker.rs.
  • Calls: every tools::<subcommand> handler. Each handler in turn typically asks CliFactory for what it needs and calls into runtime:: for execution.
  • Used by: cli/main.rs (wrapper binary), cli/lib.rs (also exposed as the deno crate's library API).

Entry points for modification

  • New subcommand: add a variant to DenoSubcommand in cli/args/flags.rs; add a *Flags struct; add the clap attributes; add a match arm in run_subcommand (cli/lib.rs) that spawns tools::<name>::...; create cli/tools/<name>.rs or cli/tools/<name>/mod.rs with the handler.
  • New flag on existing subcommand: edit the *Flags struct and corresponding handler. Remember to also expose it in deno.json if it should be persistable.
  • New service that should be lazily constructed: add the accessor to CliFactory in cli/factory.rs, with a memoized field.

For module loading specifically see Module loading. For LSP-specific work see LSP.

Key source files

File Purpose
cli/main.rs Six-line main that calls deno::main()
cli/lib.rs Top-level entry, run_subcommand dispatch, error formatting
cli/args/flags.rs Every CLI flag and subcommand, defined via clap
cli/args/flags_net.rs Network permission flag parsing
cli/args/mod.rs Merges CLI flags with deno.json config
cli/factory.rs CliFactory — DI container for module loader, npm, type checker, etc.
cli/tools/run/ The deno run handler
cli/tools/test/ The deno test handler
cli/tools/lsp/ (via cli/lsp) The deno lsp handler
cli/tools/upgrade.rs deno upgrade (101K — one of the largest files)
cli/worker.rs CLI-side worker construction wrapper

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

CLI – Deno wiki | Factory