Contributing to Steno
Thank you for helping build Steno. This guide covers how the project is laid out, how to work on it locally, and what we expect from a pull request, so you can get moving quickly instead of guessing.
Project architecture
Steno is split into clean, focused modules. Knowing where logic lives makes it much easier to write code that's easy to find later:
steno/
├── mod.ts # Public SDK and CLI entrypoint
├── src/
│ ├── core/ # Config parsing, collection engines, and build orchestration
│ ├── plugins/ # Trusted and isolated plugin execution
│ ├── theme/ # Theme rendering runtime and Tau integrations
│ ├── utils/ # Parser utilities, CLI arguments, file systems, and dev servers
│ └── types.ts # Shared public TypeScript type definitions and contracts
├── packages/ # Official themes and the init scaffolder
├── benchmarks/ # Benchmark suite, budgets, and report generation
├── integration/ # Real-site and ecosystem compatibility tests
└── test/ # Unit test fixtures and the sandbox dev project
Local workflow
Make sure you've got the latest Deno installed. Once you've cloned the repo, these are the tasks you'll reach for most:
Sandbox development
Want to try your changes against a live local project? Spin up the test sandbox:
deno task dev
Running the test suite
Run the full suite before you open a pull request:
deno task test # Unit tests: ./test.ts and ./src
deno task test:sites # Builds real-world sample sites (integration/real_sites_test.ts)
deno task test:ecosystem # Official theme/plugin compatibility (integration/ecosystem_compat_test.ts)
deno task test:installed # Runs the CLI from a simulated `deno publish` (integration/installed_product_test.ts)
deno task test:all # test + test:sites + test:installed
test:sites, test:ecosystem, and test:installed build real projects end to end, so give them
more time than the unit suite.
Static analysis
Run the linters and type checker so obvious problems get caught before review:
deno lint
deno check mod.ts
deno task doc:check # Lints public API doc comments with `deno doc --lint`
Or run everything CI runs, in one go:
deno task check
Benchmarks
If you're touching the parser, renderer, or build pipeline, make sure you haven't regressed performance. See docs/benchmarks.md for the full methodology.
deno task bench # Run the benchmark suite
deno task bench:check # Assert performance budget thresholds
deno task bench:report # Regenerate the published benchmark report
deno task bench:trends # Compare recent benchmark runs
Change checklist
Before you open a pull request, run through this list:
- Domain ownership. Put new logic in the smallest, most specific module that actually owns it - don't scatter it across layers that only touch it in passing.
- Test coverage. Add a focused test alongside whichever module or code path you changed.
Touching the isolated plugin protocol (
src/plugins/isolated_protocol.ts,isolated_worker.ts) needs coverage on both sides of the stdin/stdout boundary. Touching the Tau parser or compiler should add or update a fixture undersrc/utils/fixtures/tau/. - Strict boundaries. Export contracts as explicit
typedefinitions where you can, and keep runtime imports one-way so you don't end up with circular dependencies down the line. - Code formatting. Run the formatter before you commit:
deno fmt