RepoPilotOpen in app →

denoland/deno

A modern runtime for JavaScript and TypeScript.

WAIT

Mixed signals — read the receipts

  • Last commit today
  • 5 active contributors
  • MIT licensed
  • CI configured
  • Tests present
  • Small team — 5 top contributors
  • Concentrated ownership — top contributor handles 52% of commits

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Embed this verdict

[![RepoPilot: WAIT](https://repopilot.app/api/badge/denoland/deno)](https://repopilot.app/r/denoland/deno)

Paste into your README — the badge live-updates from the latest cached analysis.

Onboarding doc

Onboarding: denoland/deno

Generated by RepoPilot · 2026-05-05 · Source

Verdict

WAIT — Mixed signals — read the receipts

  • Last commit today
  • 5 active contributors
  • MIT licensed
  • CI configured
  • Tests present
  • ⚠ Small team — 5 top contributors
  • ⚠ Concentrated ownership — top contributor handles 52% of commits

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

TL;DR

Deno is a secure JavaScript/TypeScript/WebAssembly runtime built on V8, Rust, and Tokio. It solves Node.js pain points by providing first-class TypeScript support without transpilation steps, a permission-based security model (--allow-net, --allow-read, etc.), and a built-in standard library. It ships as a single binary with built-in tooling (formatter, linter, test runner, bundler, LSP). Cargo workspace monorepo: the main runtime lives in runtime/, the CLI entrypoint and flag parsing are in cli/ (with cli/args/flags.rs defining all subcommands), and individual Web/Node API extensions are isolated crates under ext/ (e.g., ext/fetch, ext/crypto, ext/node, ext/http). Core JS/TS bindings and snapshot infrastructure are in libs/core and cli/snapshot, while test suites are fully isolated under tests/.

Who it's for

JavaScript/TypeScript developers building server-side applications, CLI tools, or web APIs who want a secure-by-default runtime with zero-config TypeScript support. Also targets Rust systems engineers contributing to the runtime internals, and Node.js developers seeking a modern alternative with Web-standard APIs (fetch, Streams, WebCrypto) instead of Node-specific globals.

Maturity & risk

Deno is production-ready and actively maintained — it has a full CI pipeline in .github/workflows/ (ci.generated.yml, pr.generated.yml), extensive test suites across tests/unit, tests/integration, tests/node_compat, and tests/specs, and a public release cadence tracked in Releases.md. The codebase is large (~25M lines across Rust, TypeScript, and JavaScript) and has been in active development since 2018. The workspace includes 60+ Cargo crates, indicating a mature, modular architecture.

Very low risk for consumers — Deno is backed by Deno Land Inc., has a large contributor base, and publishes to crates.io (visible in Cargo.toml). The main risk for contributors is the massive surface area: the Cargo.lock ties to pinned versions of deno_ast (=0.53.2), deno_doc (=0.199.0), and deno_error (=0.7.1) using exact version constraints, meaning upstream breaking changes require coordinated bumps. The Node.js compatibility layer (ext/node, libs/node_resolver, tests/node_compat) is a large ongoing effort that may have incomplete coverage for edge cases.

Active areas of work

Active work is visible in several areas: the TypeScript-Go client (libs/typescript_go_client) suggests integration of a Go-based TypeScript type checker (likely Deno's shift to a faster tsc replacement). The ecosystem_compat_test and node_compat_test workflows (.github/workflows/) indicate ongoing Node.js compatibility improvements. The workflow files are TypeScript-generated (.ts → .generated.yml pattern) suggesting active CI infrastructure evolution.

Get running

git clone https://github.com/denoland/deno.git && cd deno

Prerequisites: Rust (rustup), Python 3, cmake, and protobuf

cargo build # builds the deno binary (~10-30 min first time) ./target/debug/deno run --allow-net https://deno.land/std/http/file_server.ts

Or run tests:

cargo test -p deno

See .github/CONTRIBUTING.md for full build instructions

Daily commands:

Build from source:

cargo build --release

Run a script:

./target/release/deno run --allow-net script.ts

Run the test suite:

cargo test

Run Deno's own JS/TS unit tests:

./target/release/deno test tests/unit/

Lint and format:

./target/release/deno lint && ./target/release/deno fmt

Map of the codebase

  • cli/lib/lib.rs — Core library entry point that wires together all major subsystems (runtime, extensions, ops) and is the foundational abstraction every contributor must understand.
  • cli/factory.rs — Dependency injection factory that constructs and owns all major services (compiler, file fetcher, module graph, npm resolver) — the central composition root of the CLI.
  • cli/args/flags.rs — Defines every CLI flag and subcommand; changing any user-facing behavior or adding a new subcommand starts here.
  • cli/graph_util.rs — Manages the module graph lifecycle including building, validating, and walking the dependency graph — critical for understanding how Deno resolves and loads modules.
  • cli/file_fetcher.rs — Handles fetching of local and remote files with caching; the primary I/O gateway for module loading and central to Deno's network-first module system.
  • cli/lib/standalone/binary.rs — Implements the compiled executable (deno compile) binary format — essential for understanding how self-contained executables are built and loaded.
  • Cargo.toml — Workspace manifest declaring all crates and their relationships; understanding the crate graph is prerequisite for any structural change.

How to make changes

Add a new CLI subcommand

  1. Define the new subcommand's flags and argument struct using clap derives, following existing patterns like RunFlags. (cli/args/flags.rs)
  2. Add the subcommand variant to the DenoSubcommand enum and wire its flag parser into the top-level clap App. (cli/args/flags.rs)
  3. Add a match arm for the new subcommand in the main dispatch and call your new runner function, passing CliOptions. (cli/lib.rs)
  4. Implement the subcommand logic as a standalone async function, using CliFactory to access shared services like the module graph or file fetcher. (cli/factory.rs)

Add a new caching backend

  1. Create a new file in cli/cache/ implementing the relevant cache trait (e.g., a struct with get/set methods backed by SQLite via CacheDb). (cli/cache/cache_db.rs)
  2. Register and expose the new cache type from the cache module root. (cli/cache/mod.rs)
  3. Instantiate the cache in Caches struct so it is lifecycle-managed alongside existing caches. (cli/cache/caches.rs)
  4. Wire the new cache into the factory so services can receive it via dependency injection. (cli/factory.rs)

Add a new Deno JS built-in (extension op)

  1. Create a new JS file in cli/js/ following the numeric prefix naming convention (e.g., 40_myfeature.js) and implement the JS-side API surface. (cli/js/40_test.js)
  2. Register the new JS file in the build script so it is included in the V8 snapshot. (cli/build.rs)
  3. If Rust ops are needed, add the op implementations in the appropriate ext/ crate (e.g., ext/web, ext/fetch) and register them with deno_core::extension!. (cli/lib/lib.rs)

Add a new permission flag

  1. Define the new --allow-X / --deny-X flags following the pattern of existing net/read/write flags. (cli/args/flags_net.rs)
  2. Add the flag variants to the global flags struct and ensure they are parsed and surfaced in CliOptions. (cli/args/flags.rs)
  3. Integrate the new permission into the permission checker used for npm packages and module loading. (cli/lib/npm/permission_checker.rs)

Why these technologies

  • Rust — Provides memory safety without GC, enabling safe systems-level integration with V8 and OS APIs while delivering C-level performance for the runtime host.
  • V8 (via rusty_v8 / deno_core) — Mature, heavily-optimized JavaScript engine with JIT compilation, widespread compatibility, and extensive embedding API needed to host JS/TS/Wasm execution.
  • Tokio — Async runtime providing the event loop, I/O reactor, and task scheduler that maps naturally onto JavaScript's single-threaded async model.
  • TypeScript (for tooling/workflows) — CI workflows and internal tooling are written in TypeScript and compiled to their target formats, dog-fooding Deno's own TypeScript support.
  • SQLite (via rusqlite) — Lightweight, zero-dependency persistent store used for all caches (HTTP, code, type-check) without requiring an external database server.

Trade-offs already made

  • Monorepo with many Cargo workspace crates
    • Why: Allows fine-grained feature/dependency control, incremental compilation, and independent publishing of crates (e.g., deno_core
    • Consequence: undefined

Traps & gotchas

  1. First build is extremely long (10-30 min) because it compiles V8 from source via rusty_v8 unless a prebuilt binary is available for your platform. 2) The .cargo/config.toml and .cargo/local-build.toml may override linker settings — on Linux you may need lld or mold. 3) Exact version pins (e.g., deno_ast = '=0.53.2') mean you cannot freely upgrade transitive deps without coordinated changes. 4) Workflow YAML files are auto-generated from .ts sources — never edit .generated.yml directly, always edit the .ts source and regenerate. 5) The libs/typescript_go_client crate implies a Go binary dependency for type checking that may need separate installation.

Architecture

Concepts to learn

  • V8 Isolates and Snapshots — Deno uses V8 snapshots (cli/snapshot/) to pre-compile built-in TypeScript/JavaScript at build time, dramatically reducing startup latency — understanding this explains why the snapshot crate exists and why JS changes require a rebuild.
  • Capability-based Security (Permission System) — Deno's --allow-net/--allow-read flags implement capability-based security enforced at the op layer in runtime/permissions — this is the core security model that differentiates Deno from Node.js.
  • Tokio Async Runtime — All I/O in Deno is async and driven by Tokio's event loop — understanding how Tokio tasks integrate with V8's microtask queue is essential for working on ext/fetch, ext/net, or any async op.
  • Op System (FFI between JS and Rust) — The #[op] macro in libs/ops/ is how all Rust functionality is exposed to JavaScript — every Web API and Node.js builtin crosses this boundary, so understanding op registration is required for any extension work.
  • ES Module Graph Resolution — Deno resolves imports using a full module graph (libs/node_resolver, deno_graph) rather than require() — this affects how circular deps, dynamic imports, and npm: specifiers behave, which is central to the libs/resolver and libs/npm crates.
  • WebAssembly System Interface (WASI) — Deno supports running WASM modules including those using WASI, bridging the Rust/WASM boundary visible in the .cargo and ext/ structure — relevant for understanding how untrusted code can be sandboxed within the runtime.
  • Cargo Workspace with Feature Flags — The 60+ crate workspace uses Cargo features to conditionally compile extensions — misconfiguring feature flags when adding a new ext/ crate is a common source of build failures for new contributors.

Related repos

  • nicolo-ribaudo/tc39-proposal-esm-phase-imports — Upstream TC39 proposals that Deno implements — tracking these explains why certain module behaviors exist
  • nodejs/node — The primary alternative runtime Deno aims to be compatible with via ext/node and tests/node_compat
  • oven-sh/bun — Direct competitor: another modern JS/TS runtime with Node.js compatibility goals, JavaScriptCore-based instead of V8
  • denoland/deno_std — Deno's official standard library published on JSR — the companion repo every Deno application developer uses
  • denoland/rusty_v8 — Provides the Rust bindings to V8 that deno_core (libs/core) depends on — changes here directly affect Deno's JS engine interface

PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Split cli/args/flags.rs into focused sub-modules

cli/args/flags.rs is a well-known large file in the Deno codebase that handles all CLI flag parsing. Given the existing flags_net.rs sub-module already extracted networking flags, this pattern should be extended. A large monolithic flags.rs makes it hard for new contributors to find relevant flag definitions and increases merge conflict frequency. Splitting it into domain-specific modules (e.g., flags_compile.rs, flags_run.rs, flags_test.rs, flags_lint.rs) mirrors the flags_net.rs precedent and improves maintainability.

  • [ ] Audit cli/args/flags.rs to identify logical groupings (run flags, test flags, lint flags, compile flags, install flags, etc.) by searching for subcommand-specific structs and enums
  • [ ] Create new files cli/args/flags_run.rs, cli/args/flags_test.rs, cli/args/flags_lint.rs, cli/args/flags_compile.rs following the pattern already established in cli/args/flags_net.rs
  • [ ] Move the relevant structs, enums, and parsing functions into each new sub-module, re-exporting from cli/args/flags.rs to preserve the public API
  • [ ] Update cli/args/mod.rs to declare the new sub-modules
  • [ ] Run cargo test and cargo clippy to verify no regressions, then check that existing integration tests in tests/integration still pass

Add missing unit tests for cli/cache/code_cache.rs

The cli/cache directory contains several cache implementations (cache_db.rs, check.rs, code_cache.rs, fast_check.rs, incremental.rs, module_info.rs, node.rs) but code_cache.rs in particular handles V8 code caching which is a performance-critical path. Missing unit tests for cache invalidation logic, cache key generation, and serialization/deserialization edge cases mean regressions can go undetected. This is a high-value, well-scoped contribution for a new contributor.

  • [ ] Read cli/cache/code_cache.rs thoroughly to understand the public API surface, cache key derivation, and storage format
  • [ ] Add a #[cfg(test)] mod tests block at the bottom of cli/cache/code_cache.rs (or a companion cli/cache/code_cache_test.rs)
  • [ ] Write a test verifying that cache entries are stored and retrieved correctly for the same source hash
  • [ ] Write a test verifying that a cache miss is returned when the source hash changes (cache invalidation)
  • [ ] Write a test verifying behaviour when the underlying cache_db returns an error or is unavailable
  • [ ] Run cargo test -p deno and confirm all new tests pass alongside existing cache tests in the module

Add a GitHub Actions workflow to detect generated workflow file drift (ci.generated.yml vs ci.ts)

The .github/workflows directory contains both TypeScript source files (ci.ts, pr.ts, version_bump.ts, etc.) and their corresponding generated YAML files (ci.generated.yml, pr.generated.yml, etc.). There is currently no automated PR check that enforces the generated YAML files are up-to-date with their TypeScript sources. A developer could modify ci.ts without regenerating ci.generated.yml, silently breaking CI configuration. A lightweight workflow that regenerates all *.generated.yml files and fails if there is a diff would catch this class of error early.

  • [ ] Examine how the existing *.generated.yml files are produced from their *.ts counterparts by reading .github/workflows/ci.ts and the existing .github/workflows/pr.generated.yml to understand the generation command/tool used
  • [ ] Create .github/workflows/check_generated_workflows.yml that triggers on pull_request for any change under .github/workflows/**
  • [ ] In the workflow job, install Deno, run the generation script/command for all *.ts workflow sources, then run `git

Good first issues

  1. Add missing test coverage in tests/unit/ for specific Deno.* APIs that lack tests — compare ext/ implementations against tests/unit/ to find gaps. 2) Improve Node.js compatibility: run tests/node_compat/ against the full Node.js test suite and fix failing cases in ext/node/ops/. 3) Add documentation examples to CLI subcommand help strings in cli/args/flags.rs — many flags have minimal descriptions that could include concrete usage examples.

Top contributors

Recent commits

  • 04e5db2 — fix(ext/node): implement node:cluster on unix (#33752) (nathanwhit)
  • 1ab39bf — Revert "fix(ext/node): support AES-128-ECB cipher for PKCS#8 private key encryption and decryption" (#33837) (littledivy)
  • 16fff7d — fix(ext/node): pass URL to kOnHeadersComplete when request has no headers (#33831) (divybot)
  • bfc3c88 — feat: include node lib by default and use NodeJS.Timeout for timers (#33823) (bartlomieju)
  • 05f52c3 — fix: include node lib by default and use NodeJS.Timeout for timers (#33823) (bartlomieju)
  • 19bd3d8 — fix(ext/node): accept CryptoKey input in createPublicKey/createPrivat… (#33750) (Hunnyboy1217)
  • e84fc15 — perf(ext/node): convert errors.ts, util.mjs, and foundation layer to lazy-loaded scripts (#33830) (bartlomieju)
  • 49bc36d — ci: disable flaky Node compat test (#33833) (bartlomieju)
  • fa01b38 — fix(ext/node): support AES-128-ECB cipher for PKCS#8 private key encryption and decryption (#33809) (divybot)
  • 01972d3 — fix(ext/node): implement noDelay property on net.Server and apply TCP_NODELAY to accepted (#33828) (divybot)

Security observations

  • Medium · Pinned dependency versions with potential for supply chain risk — Cargo.toml (workspace dependencies). Several dependencies use exact version pinning (e.g., deno_ast = '=0.53.2', deno_error = '=0.7.1', deno_doc = '=0.199.0'). While pinning prevents unintended upgrades, it also means known vulnerabilities in these exact versions will not be automatically remediated. If any of these pinned crates contain security flaws, the project will remain vulnerable until manually updated. Fix: Regularly audit pinned dependencies using 'cargo audit' (RustSec advisory database) and integrate automated dependency vulnerability scanning (e.g., Dependabot, cargo-deny) into CI/CD pipelines. Establish a process to promptly update pinned versions when security advisories are published.
  • Medium · SQLite extension excluded from workspace with special feature flags — Cargo.toml, tests/sqlite_extension. The comment in Cargo.toml notes that 'tests/sqlite_extension is excluded because it requires rusqlite's loadable_extension feature, which is incompatible with the session feature used by the rest of the workspace.' Loadable SQLite extensions can introduce dynamic code loading risks, and the separation suggests a non-standard build path that may receive less security scrutiny. Fix: Ensure the SQLite extension test path is subject to the same security review processes as the rest of the codebase. Document why loadable extensions are needed and restrict their use to sandboxed test environments only. Ensure loadable extensions are never enabled in production builds.
  • Medium · NAPI (Native API) extension introduces native code execution risk — ext/napi, libs/napi_sys. The presence of ext/napi and libs/napi_sys in the workspace indicates support for Node.js-compatible native addons (N-API/NAPI). Native addons bypass Deno's permission system and can execute arbitrary native code, potentially escaping the sandbox. This is a known architectural tradeoff but represents a significant attack surface if untrusted native modules are loaded. Fix: Clearly document that NAPI modules bypass Deno's security sandbox. Ensure NAPI usage requires explicit user opt-in (e.g., a specific permission flag). Audit all pathways through which native modules can be loaded and ensure they are gated behind appropriate permission checks. Consider adding runtime warnings when NAPI modules are loaded.
  • Medium · FFI extension allows arbitrary native library loading — ext/ffi, tests/ffi. The ext/ffi and tests/ffi components provide Foreign Function Interface capabilities, allowing JavaScript/TypeScript code to call native shared libraries. This can be used to bypass Deno's permission model entirely if not properly restricted. Malicious or compromised code with FFI access can perform any system operation. Fix: Verify that FFI usage is gated behind the '--allow-ffi' permission flag and that the permission check cannot be bypassed. Conduct a thorough audit of the FFI permission enforcement code. Consider implementing path-based FFI restrictions similar to file system permissions.
  • Low · DevContainer Dockerfile may expose development environment risks — .devcontainer/Dockerfile, .devcontainer/devcontainer.json. The presence of a .devcontainer/Dockerfile and devcontainer.json suggests a shared development container configuration. If this Dockerfile installs tools or sets configurations insecurely (e.g., running as root, mounting sensitive host paths, disabling security features), developers using it could be exposed to security risks. Fix: Review the DevContainer Dockerfile to ensure it: runs as a non-root user, does not mount sensitive host directories unnecessarily, uses specific base image tags rather than 'latest', and follows container security best practices. Regularly update the base image to receive security patches.
  • Low · Potential for hardcoded credentials in cache or configuration modules — cli/cache/, cli/args/mod.rs, libs/npmrc. The presence of multiple cache and configuration files (cli/cache/cache_db.rs, cli/cache/caches.rs, cli/args/mod.rs, libs/config, libs/npmrc) suggests areas where credentials, tokens, or sensitive configuration values could potentially be hardcoded or improperly handled. The npmrc handling is particularly sensitive as it may process registry authentication tokens. Fix: Audit all cache and configuration handling code to ensure no credentials are hardcoded. Verify that npmrc token handling follows secure practices (e.g., tokens

LLM-derived; treat as a starting point, not a security audit.

Where to read next


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

WAIT · denoland/deno — RepoPilot Verdict