wire is a one-way compiler:
Go packages -> DSL -> module graph -> injector plan -> Go source
| Path | Responsibility |
|---|---|
wire.go, doc.go |
The declaration API. No runtime behavior. |
example_test.go |
Executable examples of every declaration form. |
cmd/wire |
The executable; it only calls internal/command. |
internal/command |
Command line parsing, diagnostic and plan output, exit codes. |
internal/engine |
The pipeline: load, parse, plan, group variants, render, verify. |
internal/load |
Two-phase package loading and per-file build constraints. |
internal/dsl |
Recognizes API calls and produces the model. |
internal/model |
The intermediate model shared by later stages. |
internal/graph |
Module instantiation, binding nodes, resolution. |
internal/plan |
Injector and scope contracts, topological order. |
internal/generate |
Go source rendering and the per-file runtime helpers. |
internal/artifact |
Working-tree operations: stale files, ownership, atomic writes, checks, diffs. |
internal/diff |
Unified diff rendering. |
internal/buildconstraint |
Build constraint specialization, combination, variant naming. |
internal/diagnostic |
Diagnostic values and their human and JSON encodings. |
internal/typeindex |
A map keyed by go/types identity. |
internal/integration |
End-to-end fixtures driven through the built binary. |
docs/ |
This document and the diagnostic reference. |
internal/load loads in two phases under the wireinject tag: an
import-graph pass finds every package that imports the wire API, then a
second pass type-checks those and the requested roots from source while every
other dependency comes from export data. Build constraints of files that were
not parsed are derived on demand; standard library declarations count as
unconditional, so tags such as goexperiment.* never reach generated files.
internal/dsl recognizes API methods by their go/types objects, not by
spelling, and rejects declarations whose build constraint does not require
wireinject. It produces the model without resolving anything.
internal/graph instantiates each included module as a lexical boundary.
Resolution checks local bindings, child exports, then the parent; results are
cached per visibility context. Overrides shadow a type in their module and
decorators apply afterward.
Providers, values, bindings, decorators, collections, inputs, scope factories,
struct bindings, and field bindings are explicit node kinds. Every directive
is validated and given its node when the module is instantiated; the resolver
only wires nodes. Exports are checked for ownership; their dependencies are
resolved when an injector or scope uses them. Missing-binding and cycle
diagnostics name the consumer and, for cycles, the full path. Every code is
listed in docs/diagnostics.md.
internal/plan validates injector and scope contracts, adds seeds, resolves
outputs, and produces a stable topological order. Order is the only execution
plan.
internal/generate emits constructor calls in dependency order. Each file
declares its helpers once: a cleanup runner, a panic-rollback guard, a
lifecycle type, and, with scopes, a scope tracker; helper names carry the
variant suffix so co-active files never collide. Generated code has no
dependency on this module.
Cleanups are armed as soon as their provider returns and run in reverse on
failure or panic; a failing provider owns its partial result. Cleanup is
idempotent, joins errors with errors.Join, has no context, and delays a
cleanup panic until the remaining resources closed.
Scope factories build sequentially but may be called concurrently. The generated tracker coordinates caller release with parent shutdown: it cancels active builds, waits for build and release calls, and closes committed scopes in reverse completion order. Scope contexts stay live until scope cleanup.
internal/engine groups injectors by package and derived build constraint,
renders each variant, and type-checks the result through an overlay before
anything is written. Unconditional graphs use wire_gen.go, conditional
graphs wire_gen_<hash>.go; inactive variants are left untouched.
internal/artifact owns every interaction with the working tree; internal/diff
renders unified diffs; internal/command parses the command line, prints
diagnostics and plans as text, JSON, or a Graphviz digraph, and maps results
to exit codes.