- Git workflow
- High Level Overview
- Quick Start
- Commands
- Testing
- Development Workflows
- Debugging
- Codebase Navigation
- Troubleshooting
Deno uses a GH based standard git workflow. The main branch is main. All
development happens in feature branches, which are then merged into main via
pull requests.
When the feature is finished and ready to for review, follow these steps:
- Create a new git branch, if you haven't already, with a descriptive name
(e.g.,
feature/new-cli-commandorfix/bug-in-worker-threads). - Commit your changes with clear and descriptive commit messages.
- Push your branch to the remote repository.
- Open a pull request (PR) against the
mainbranch on GitHub. - Before committing, make sure
tools/format.jsis run to format your code - Before committing, if only non-Rust was changed, make sure to run
tools/lint.js --jsand fix any lint errors before committing - If you changed Rust code, make sure to run
tools/lint.jsand fix any lint errors before committing - In the PR description, provide a clear summary of the changes you made, why they were necessary, and any relevant context or links to related issues.
- When pushing updates to the PR, make sure to never force push. Create as many commits as you need, all of them get squashed when the PR is merged, so there is no need to rewrite history. This also allows reviewers to see the incremental changes you made in response to feedback.
- Keep your changes minimal, don't do drive-by changes in a PR. If you need to make a change that is not directly related to the PR, create a separate PR for it. This keeps the review process focused and efficient.
The user visible interface and high level integration is in the deno crate
(located in ./cli).
This includes flag parsing, subcommands, package management tooling, etc. Flag
parsing is in cli/args/flags.rs. Tools are in cli/tools/<tool>.
The deno_runtime crate (./runtime) assembles the JavaScript runtime,
including all "extensions" (native functionality exposed to JavaScript). The
extensions themselves are in the ext/ directory, and provide system access to
JavaScript – for instance filesystem operations and networking.
cli/- User-facing CLI implementation, subcommands, and toolsruntime/- JavaScript runtime assembly and integrationext/- Extensions providing native functionality to JS (fs, net, etc.)tests/specs/- Integration tests (spec tests)tests/unit/- Unit teststests/testdata/- Test fixtures and data files
Before building, install the required prerequisites (Rust, native compilers,
cmake, protobuf, etc.) and clone with --recurse-submodules as described in
.github/CONTRIBUTING.md.
To compile after making changes:
cargo buildFor faster iteration during development (less optimization):
cargo build --bin denoExecute your development build:
./target/debug/deno eval 'console.log("Hello from dev build")'# Run a local file
./target/debug/deno run path/to/file.ts
# Run with permissions
./target/debug/deno run --allow-net --allow-read script.ts
# Run the REPL
./target/debug/deno# Check for compilation errors (fast, no binary output)
cargo check
# Check specific package
cargo check -p deno_runtime
# Build release version (slow, optimized)
cargo build --release# Lint the code
./tools/lint.js
# Format the code
./tools/format.js
# Both lint and format
./tools/format.js && ./tools/lint.js# Run all tests (this takes a while)
cargo test
# Filter tests by name
cargo test <nameOfTest>
# Run tests in a specific package
cargo test -p deno_core
# Run just the CLI integration tests
cargo test --bin deno
# Run spec tests only
cargo test specs
# Run a specific spec test
cargo test spec::test_name- Spec tests (
tests/specs/) - Main integration tests, CLI command execution and output validation - Unit tests - Inline with source code in each module
- Integration tests (
cli/tests/) - Additional integration tests - WPT (
tests/wpt/) - Web Platform Tests for web standards compliance
The main form of integration test in deno is the "spec" test. These tests can be
found in tests/specs. The idea is that you have a __test__.jsonc file that
lays out one or more tests, where a test is a CLI command to execute and the
output is captured and asserted against.
The name of the test comes from the directory the __test__.jsonc appears in.
- Create a directory in
tests/specs/with a descriptive name - Add a
__test__.jsoncfile describing your test steps - Add any input files needed for the test
- Add
.outfiles for expected output (or inline in__test__.jsonc)
Example:
tests/specs/my_feature/
__test__.jsonc
main.ts
expected.out
The schema for __test__.jsonc can be found in tests/specs/schema.json.
Example test structure:
The expected output can be inline in a __test__.jsonc file or in a file ending
with .out. For a given test step, the output field tells you either the
inline expectation or the name of the file containing the expectation. The
expectation uses a small matching language to support wildcards and things like
that. A literal character means you expect that exact character, so Foo bar
would expect the output to be "Foo bar". Then there are things with special
meanings:
[WILDCARD]: matches 0 or more of any character, like.*in regex. this can cross newlines[WILDLINE]: matches 0 or more of any character, ending at the end of a line[WILDCHAR]- match the next character[WILDCHARS(5)]- match any of the next 5 characters[UNORDERED_START]followed by many lines then[UNORDERED_END]will match the lines in any order (useful for non-deterministic output)[# example]- line comments start with[#and end with]
Example .out file:
Check file://[WILDCARD]/main.ts
[WILDCARD]
Successfully compiled [WILDLINE]
- Define the command structure in
cli/args/flags.rs - Add the command handler in
cli/tools/<command_name>.rsorcli/tools/<command_name>/mod.rs - Wire it up in
cli/main.rs - Add spec tests in
tests/specs/<command_name>/
Example files to reference:
- Simple command:
cli/tools/fmt.rs - Complex command:
cli/tools/test/
- Navigate to
ext/<extension_name>/(e.g.,ext/fs/,ext/net/) - Rust code provides the ops (operations) exposed to JavaScript
- JavaScript code in the extension provides the higher-level APIs
- Update
runtime/worker.rsto register the extension if new - Add tests in the extension's directory
# Update Cargo dependencies
cargo update
# Update to latest compatible versions
cargo upgrade # Requires cargo-edit: cargo install cargo-edit
# Check for outdated dependencies
cargo outdated # Requires cargo-outdatedUse lldb directly:
lldb ./target/debug/deno
(lldb) run eval 'console.log("test")'Use println debugging.
# Set Rust log level
DENO_LOG=debug ./target/debug/deno run script.ts
# Specific module logging
DENO_LOG=deno_core=debug ./target/debug/deno run script.tsIn Rust code:
eprintln!("Debug: {:?}", some_variable);
dbg!(some_variable);In JavaScript/TypeScript code:
console.log("Debug:", value);cli/main.rs- Entry point, command routingcli/args/flags.rs- CLI flag parsing and structureruntime/worker.rs- Worker/runtime initializationruntime/permissions.rs- Permission systemcli/module_loader.rs- Module loading and resolution
- Ops - Rust functions exposed to JavaScript (in
ext/directories) - Extensions - Collections of ops and JS code providing functionality
- Workers - JavaScript execution contexts (main worker, web workers)
- Resources - Managed objects passed between Rust and JS (files, sockets, etc.)
- Need to add a CLI flag? Look at similar commands in
cli/args/flags.rs - Need to add an op? Look at ops in relevant
ext/directory (e.g.,ext/fs/lib.rs) - Need to add a tool? Reference existing tools in
cli/tools/
Error: linking with cc failed
- Make sure you have the required system dependencies
- On macOS:
xcode-select --install - On Linux: Install
build-essentialor equivalent
Error: failed to download dependencies
- Check internet connection
- Try
cargo cleanthen rebuild - Check if behind a proxy, configure cargo accordingly
For other build failures (missing cmake, stdarg.h, etc.), see the full
prerequisites in
.github/CONTRIBUTING.md.
Spec test failures
- Check the test output carefully for differences
- Update
.outfiles if output format changed intentionally - Use
[WILDCARD]for non-deterministic parts of output
Flaky tests
- Add
[UNORDERED_START]/[UNORDERED_END]for order-independent output - Check for race conditions in test code
- May need to increase timeouts or add retries
Tests failing with permission errors
- Ensure test files have correct permissions
- Check that test setup properly grants necessary permissions
Slow compile times
- Use
cargo checkinstead ofcargo buildwhen possible - Use
--bin denoto build only the main binary - Use
sccacheormoldlinker for faster builds - Consider using
cargo-watchfor incremental builds
Crashes or panics
- Run with
RUST_BACKTRACE=1for full backtrace - Use
RUST_BACKTRACE=fullfor even more detail - Check for unwrap() calls that might panic
Unexpected behavior
- Add debug prints liberally
- Check permission grants - many features require explicit permissions
- Check existing issues on GitHub
- Look at recent PRs for similar changes
- Review the Discord community for discussions
- When in doubt, ask! The maintainers are helpful
{ "tests": { "basic_case": { "args": "run main.ts", "output": "expected.out" }, "with_flag": { "steps": [ { "args": "run --allow-net main.ts", "output": "[WILDCARD]success[WILDCARD]" } ] } } }