-
Notifications
You must be signed in to change notification settings - Fork 2
Doctor Build
Dev-time codegen that scans @Vial-decorated classes and writes a
VialRegistry type augmentation, so inject('Token') is
typed without importing the class.
Imported from the ./build subpath — Deno-only (declared in deno.json
only, not package.json). It uses Deno.readDir / writeTextFile, so run it
under Deno as a dev/CI step; the file it emits is plain type declarations
consumed unchanged on every runtime.
import { build } from '@tundralibs/doctor/build';build(options: BuildOptions): Promise<VialSite[]>;
interface BuildOptions {
roots: string[]; // directories/files scanned recursively for @Vial
out: string; // registry file to (over)write
module?: string; // module to augment — defaults to '@tundralibs/doctor'
}build returns the discovered sites ({ token, className, file }), the same
data it wrote, so callers can log or assert on what was found.
For every @Vial(...) class X it finds, build records X as the token (the
class name) and emits an augmentation keyed by it:
// AUTO-GENERATED by @tundralibs/doctor/build — do not edit by hand.
import type { Config } from './Config.ts';
import type { Logger } from './Logger.ts';
declare module '@tundralibs/doctor' {
interface VialRegistry {
Config: Config;
Logger: Logger;
}
}Entries are sorted and deduped (last registration of a name wins). The output file is never scanned, so re-running is idempotent.
// scripts/build-registry.ts
import { build } from '@tundralibs/doctor/build';
await build({
roots: ['./src'],
out: './src/vial-registry.ts',
});deno run -A scripts/build-registry.tsThen import the generated file once (e.g. at your entry point) so the augmentation is in scope, and resolve by token:
import './vial-registry.ts';
import { inject } from '@tundralibs/doctor';
const config = inject('Config'); // typed as ConfigUse the module option when the registry references the package by a relative
path instead of its name — for example module: '../mod.ts' inside this repo.
scan(roots, out?), render(sites, out, module), and
relativeImport(from, to) are exported individually for testing or custom
pipelines; build is scan + render + write.
- The token is the class name — names must be unique and survive minification. See inject caveats.
- Only
@Vial-decorated classes are scanned. Vials registered viaDoctor.prescribe(...)/ a factory are not found; add those to the registry by hand. -
*.test.ts,*.bench.ts, and*.d.tsfiles are skipped, so test-only vials never enter the registry. - Scanning is textual, not a full parse — but comments and string contents
are blanked before matching, and only decorators /
export/default/abstractmay sit between@Vial(...)and itsclass, so a@Vial(inside a comment, string, or JSDoc example does not register. Pathological sources (e.g. regex literals containing an unpaired quote) can still confuse the blanking pass on that line.