Generation API
Contracts, register augmentation, manifest serialization, generated route modules, aggregate generation, and idempotent writes.
@cookbook/router-cli exposes generation APIs for producing route contracts, manifest JSON, register augmentation, generated runtime route modules, and aggregate artifacts.
import {
generateContracts,
generateManifest,
generateRegister,
generateRouterArtifacts,
generateRoutesModule,
serializeManifest,
writeGeneratedFile,
} from '@cookbook/router-cli';
import type {
CommandResult,
GeneratedRouteTreeRuntimeOptions,
ManifestRoute,
RouteFileExport,
RouteManifest,
} from '@cookbook/router-cli';These APIs are lower-level than generateCommand(). Use them when embedding generation in tests, build tools, custom CLIs, or one-off integration pipelines.
generateContracts(routes, options?)
function generateContracts(
routes: readonly RouteDefinition[],
options?: DefineRoutesOptions | RouterPathOptions,
): string;Generates the TypeScript contract file normally written to:
.cookbook-router/contracts.tsProp
Type
Generation validates the resolved tree with validateResolvedRouteTree().
When options is a DefineRoutesOptions object, supplied pathConstraints are registered before validation.
When options is a bare RouterPathOptions object, no custom constraints are supplied.
The output includes:
RouteParams
RouteParamsInput
RouteSearch
RouteSearchInput
RouteHash
RouteMeta
RoutePaths
RouteOutletContext
RouterContractsThe generated file begins with:
/* eslint-disable */
/* This file is generated by @cookbook/router-cli. Do not edit directly. */and ends with:
/* eslint-enable */Generated contract behavior
Path params:
| Route path feature | Parsed contract | Input contract | |
|---|---|---|---|
| unconstrained param | string | string | |
| numeric built-in constraint | number | number | |
| custom constraint | string | string | |
| wildcard | readonly string[] | `string | readonly string[]` |
| optional param | optional property | optional property |
Search descriptors:
| Descriptor behavior | Parsed contract | Input contract |
|---|---|---|
| required field | required property | required property |
optional: true | optional property | optional property |
default | required property | optional property |
many: true | readonly array | readonly array |
Hash descriptors:
| Descriptor | Generated hash contract | |
|---|---|---|
| no hash descriptor | never | |
| string hash | string | |
| optional string hash | `string | undefined` |
| enum hash | union of values | |
| optional enum hash | union of values plus undefined | |
| defaulted hash | non-optional parsed value |
Paths:
| Route shape | Generated RoutePaths value |
|---|---|
route with fullPath | string literal full path |
| pathless route | never |
Metadata:
- no metadata becomes
{}; - metadata keys become optional properties;
- metadata value types are rendered with JavaScript
typeofvalues.
Outlet context:
- every generated route currently maps to
{}.
Slot routes are included because generation flattens primary children and normalized slot routes.
generateRegister()
function generateRegister(): string;Generates the declaration file normally written to:
.cookbook-router/register.d.tsThe output imports generated contracts:
import type { RouterContracts } from './contracts';It augments:
@cookbook/router
@cookbook/router-reactIt does not augment @cookbook/router-cli.
Generated shape:
declare module '@cookbook/router' {
interface Register {
contracts: RouterContracts;
}
}
declare module '@cookbook/router-react' {
interface Register {
contracts: RouterContracts;
}
}
export {};Use this file by ensuring TypeScript includes the generated output directory.
generateManifest(routes, options?)
interface ManifestRoute {
readonly id: string;
readonly path?: string;
readonly parentId?: string;
readonly index: boolean;
readonly url?: RouterUrlOptions;
}
interface RouteManifest {
readonly routes: readonly ManifestRoute[];
}
function generateManifest(
routes: readonly RouteDefinition[],
options?: DefineRoutesOptions | RouterPathOptions,
): RouteManifest;Generates a JSON-serializable manifest model.
Prop
Type
Prop
Type
The manifest includes normalized primary routes and normalized slot routes.
It does not serialize:
- views;
- layouts as functions/components;
- middleware;
- lifecycle hooks;
- preload functions;
- module preload functions;
- path constraint implementations.
Route-level url options are preserved because manifest consumers may need URL policy.
serializeManifest(manifest)
function serializeManifest(
manifest: RouteManifest,
): string;Serializes a manifest with stable JSON formatting.
Output rules:
JSON.stringify(manifest, null, 2);- trailing newline;
- no custom sorting beyond the route order produced by normalization and flattening.
{
"routes": [
{
"id": "home",
"path": "/",
"index": true
}
]
}The serialized string ends with \n.
generateRoutesModule(sources, routesPath?, options?, runtimeOptions?)
function generateRoutesModule(
sources: readonly CliRouteSource[],
routesPath?: string,
options?: DefineRoutesOptions,
runtimeOptions?: GeneratedRouteTreeRuntimeOptions,
): string;Renders the generated route module normally written to:
.cookbook-router/routes.tsCliRouteSource is the structural source shape used by the implementation. It is not exported from the package root as a named public type.
Equivalent public shape:
interface CliRouteSource {
readonly path: string;
readonly routes: readonly RouteDefinition[];
readonly routeOptions?: DefineRoutesOptions;
readonly routeExports?: readonly RouteFileExport[];
}RouteFileExport is exported:
interface RouteFileExport {
readonly exportName: string;
readonly kind: 'route' | 'routes' | 'routeTree';
}Prop
Type
Empty source behavior
Calling generateRoutesModule() with no importable route exports returns an empty generated route module:
export const routes = [] as const;Aggregate generation does not call this function unless at least one loaded route source exposes route exports.
Static re-export behavior
A single compatible static routes export can be re-exported directly.
This preserves the original route module and its route-options metadata.
Direct re-export is used only when all of these are true:
- there is exactly one import binding;
- the binding kind is
routes; - there is exactly one source;
- the source has exactly one route export;
- no runtime imports are required;
- source route options match the requested generation options;
- the source route tree does not contain nested absolute child paths.
The generated module imports the original export as routes and re-exports it.
Wrapped module behavior
When direct re-export is not safe, the generator wraps imports.
If any import binding is a single defineRoute() declaration, it emits defineRouteTree().
import { defineRouteTree } from '@cookbook/router/route-config';If all import bindings are route arrays or route trees, it emits defineRoutes().
import { defineRoutes } from '@cookbook/router/route-config';It always imports these types for wrapped output:
import type {
RouteDefinition,
RouteModulePreload,
} from '@cookbook/router/route-config';Module preload preservation
Wrapped route modules attach a generated modulePreload helper to imported routes.
The generated helper points back to the physical source module with a dynamic import:
() => import('../src/routes').then(() => undefined)Known TypeScript and JavaScript extensions are stripped from import specifiers:
.ts
.tsx
.mts
.cts
.js
.jsx
.mjs
.cjsRuntime path-constraint import preservation
runtimeOptions.pathConstraints can be rendered into generated routes.ts.
interface RuntimeImportReference {
readonly path: string;
readonly exportName: string;
}
interface GeneratedRouteTreeRuntimeOptions {
readonly pathConstraints?: RuntimeImportReference;
}The generated route module imports the named export and passes it to defineRoutes() or defineRouteTree():
pathConstraints: __cookbookPathConstraints,Source-level path constraints inside multiple or rewrapped route source files cannot be safely preserved.
Error:
Generated routes.ts cannot safely preserve pathConstraints declared inside multiple or rewrapped route source files. Move custom pathConstraints to cookbook-router.config.ts as a named import from a runtime-safe module, or generate from a single static route-tree source so the original defineRoutes()/defineRouteTree() export can be re-exported directly.generateRouterArtifacts(options)
interface GenerateRouterArtifactsOptions
extends CliRouteOptions {}
function generateRouterArtifacts(
options: GenerateRouterArtifactsOptions,
): Promise<CommandResult>;Runs aggregate generation.
GenerateRouterArtifactsOptions is the source-level interface. It is not exported from the package root as a named public type. Use CliRouteOptions when annotating call sites.
const options: CliRouteOptions = {
routes,
outDir: '.cookbook-router',
};Generation resolves route input from the same pipeline as generateCommand().
Work order:
- Resolve effective route options from direct options, route files, config, globs, output directory, and runtime route options.
- Resolve generated output paths.
- Validate generated output does not overwrite a route source file.
- Resolve and validate route input.
- Register route path constraints from resolved route options.
- Create the output directory.
- Determine whether
routes.tsis applicable. - Render and write applicable outputs.
- Return
CommandResult.
Route input resolution and validation happen before artifact writes begin.
The output directory can still be created before a later rendering or filesystem failure.
Applicable artifacts
When no generated route module is applicable:
contracts.ts
manifest.json
register.d.tsWhen route sources expose importable route exports:
routes.ts
contracts.ts
manifest.json
register.d.tsroutes.ts is conditional.
contracts.ts, manifest.json, and register.d.ts are always applicable for successful aggregate generation.
File order
Without routes.ts, files is:
[
'.cookbook-router/contracts.ts',
'.cookbook-router/manifest.json',
'.cookbook-router/register.d.ts',
]With routes.ts, files is:
[
'.cookbook-router/routes.ts',
'.cookbook-router/contracts.ts',
'.cookbook-router/manifest.json',
'.cookbook-router/register.d.ts',
]changedFiles preserves the same write order, but includes only files whose contents changed.
If all applicable files are already byte-for-byte identical:
{
ok: true,
files: [
'.cookbook-router/contracts.ts',
'.cookbook-router/manifest.json',
'.cookbook-router/register.d.ts',
],
errors: [],
changedFiles: [],
}Write safety
Generated artifact paths are fixed inside outDir.
The generator refuses to write generated files over route source files.
Error:
Refusing to write generated router artifacts over route source file "...".It also refuses generated artifact paths that escape the resolved output directory.
Error:
Refusing to write generated router artifact outside outDir: "...".writeGeneratedFile(fs, path, contents)
function writeGeneratedFile(
fs: CliFileSystem,
path: string,
contents: string,
): Promise<boolean>;Writes one generated file only when contents changed.
Prop
Type
Return value:
| Result | Meaning |
|---|---|
false | Existing file contents are byte-for-byte identical. No write occurred. |
true | File was missing, unreadable, or different, and writeFile() completed. |
A read failure is treated as “file absent.” The write is still attempted.
A write failure rejects the returned promise.
Export inventory for this page
@cookbook/router-cli exports these generation values:
generateContracts
generateManifest
generateRegister
generateRouterArtifacts
generateRoutesModule
serializeManifest
writeGeneratedFileIt exports these generation-related public types:
ManifestRoute
RouteManifest
GeneratedRouteTreeRuntimeOptions
RuntimeImportReference
RouteFileExport
CommandResult
CliRouteOptions
CliFileSystemThe package root does not export these source-level implementation types as named public types:
GenerateRouterArtifactsOptions
CliRouteSourcegenerateRoutesModule() and generateRouterArtifacts() are still public functions. Their option/source shapes are structural.
Where this bites
register.d.ts does not augment @cookbook/router-cli
Generated registration covers:
@cookbook/router
@cookbook/router-reactIt does not cover:
@cookbook/router-cliThe CLI consumes route definitions and emits contracts. Runtime packages consume the generated augmentation.
routes.ts is conditional
A valid route tree can produce:
contracts.ts
manifest.json
register.d.tswithout producing:
routes.tsDirect routes input and JSON route files can generate contracts without providing importable runtime route exports.
Empty low-level route module output is not aggregate behavior
This low-level call can return:
export const routes = [] as const;Aggregate generation does not intentionally write an empty routes.ts just because routes are empty. It writes routes.ts only when route sources expose route exports.
Source-local path constraints can block generated routes.ts
Contracts and manifest can validate with source-local constraints.
A generated runtime route module also needs a runtime-safe import for those constraints unless it can re-export the original static route tree.
Move shared path constraints to a named relative import in config when generated routes.ts must wrap route sources.
Idempotent writes are byte-for-byte
writeGeneratedFile() skips only exact string matches.
Changing formatting, comments, ordering, or trailing newlines counts as a changed file.
Validation happens before artifact writes, not before every side effect
Invalid route graphs do not overwrite previous valid artifacts.
The output directory may still be created before later generation or filesystem failures.