Status: pre-alpha (M0 scaffolding). Not yet usable.
A code-generation dependency injection framework for TypeScript, modeled after Dagger 2. The codegen toolchain is written in Rust and emits plain .ts files — there is no runtime reflection, no reflect-metadata, and the dependency graph is fully validated at build time.
The TypeScript DI ecosystem today splits into two camps:
- Heavy runtime-reflection frameworks (NestJS, InversifyJS, tsyringe) that depend on
reflect-metadataand resolve graphs at startup, with errors surfacing at runtime. - Compile-time-typed containers (
typed-inject,@wessberg/DI) that catch mistakes earlier but lack Dagger's higher-level features: scopes, subcomponents, multibindings, and a generated zero-cost component implementation.
anvil aims to fill that gap — Dagger's developer experience, on top of TypeScript, with a Rust toolchain in the spirit of swc and esbuild.
user .ts files (with @Module/@Inject/@Component decorators)
|
v
┌────────────────────────────────────────┐
│ anvil (Rust CLI, built on Oxc) │
│ parse → IR → graph → validate → emit │
└────────────────────────────────────────┘
|
v
co-located *.anvil.ts files containing the wired graph
|
v
user's tsc compiles everything
User decorators from the anvil npm package are no-op identity functions; all real wiring happens at codegen time.
// src/coffee/heater.ts
import { Inject, Singleton } from "@msulak/anvil";
@Inject
@Singleton
export class Heater {
constructor() {}
on() { console.log("heating"); }
}
// src/coffee/pump.ts
import { Inject } from "@msulak/anvil";
import { Heater } from "./heater";
@Inject
export class Pump {
constructor(private heater: Heater) {}
pump() { this.heater.on(); }
}
// src/coffee/coffee-component.ts
import { Component, Singleton } from "@msulak/anvil";
import { Pump } from "./pump";
@Singleton
@Component({ modules: [] })
export abstract class CoffeeShop {
abstract pump(): Pump;
}$ anvil build
# generates src/coffee/coffee-component.anvil.ts containing DaggerCoffeeShop// later, in app code
import { createCoffeeShop } from "./coffee/coffee-component.anvil";
createCoffeeShop().pump().pump();crates/
anvil-core/ # IR, dependency graph, validation rules
anvil-parser/ # TypeScript parser + decorator extractor (Oxc, M1+)
anvil-codegen/ # TS emitter (M4+)
anvil-cli/ # `anvil` binary
packages/
anvil/ # runtime: no-op decorator stubs + Token<T>
docs/
architecture.md, ir.md, codegen.md, validation.md, cli.md
adr/ # Architecture Decision Records
tests/fixtures/ # golden-file tests for the codegen pipeline (M3+)
examples/ # working sample apps, exercised in CI (M4+)
See docs/architecture.md for the full pipeline and docs/adr/ for the design decisions.
| Milestone | Scope |
|---|---|
| M0 | Workspace scaffolding + CI (current) |
| M1 | Stage-3 decorator parsing into IR |
| M2 | Cross-file symbol resolver (tsconfig-aware) |
| M3 | Graph construction + validation (missing/cycle/duplicate) |
| M4 | First codegen: @Component + @Module + @Provides |
| M5 | CLI: build / watch / check / explain |
| M6 | @Inject ctor + @Singleton — v0.1 release |
| M7+ | @Binds, Token<T>, subcomponents, multibindings, unplugin |
cargo test --workspace # Rust unit + snapshot + integration tests
pnpm install
pnpm -r test # TypeScript runtime tests
pnpm -r build # Build the runtime packageSee CONTRIBUTING.md for the full developer workflow and the testing pyramid.
Apache-2.0 — chosen to match Dagger and remain GPL-compatible.