Configuration API
Router CLI config shape, six-file discovery, static parsing rules, loading behavior, and effective option resolution.
@cookbook/router-cli supports static router configuration files for route discovery, output placement, path options, and path constraints.
The config API is intentionally narrow. The CLI extracts supported static shapes instead of importing and executing arbitrary config modules.
import {
defineRouterConfig,
getRouterConfigFilenames,
getRouterConfigWatchCandidates,
loadRouterConfig,
parseRouterConfig,
resolveEffectiveRouteOptions,
ROUTER_CONFIG_FILENAMES,
} from '@cookbook/router-cli';
import type {
CliRouteOptions,
GeneratedRouteTreeRuntimeOptions,
LoadedRouterConfig,
RouterCliConfig,
RouterConfigFilename,
} from '@cookbook/router-cli';defineRouterConfig(config)
function defineRouterConfig<
const Config extends RouterCliConfig,
>(config: Config): Config;Identity helper for contextual typing and literal preservation.
It validates the config object at runtime when the config module is actually executed by TypeScript or Node.
import {
defineRouterConfig,
} from '@cookbook/router-cli';
export default defineRouterConfig({
routeFiles: 'src/**/*.route.{ts,tsx}',
outDir: '.cookbook-router',
pathOptions: {
prune: 'all',
},
} as const);pathOptions.prune does not accept true.
Valid values:
'all'
'duplication'
'trailing'
falseInvalid:
pathOptions: {
prune: true,
}Prop
Type
Runtime validation boundary
defineRouterConfig() accepts:
routeFilesomitted;routeFilesas a string;routeFilesas an array with at least two unique strings;outDiras a string;pathOptions.pruneas'all','duplication','trailing', orfalse;pathConstraintsas an object whose values are router path constraints.
It rejects:
- unknown root properties;
- empty
routeFilesarrays; - single-item
routeFilesarrays; - duplicate
routeFilesentries; - non-string route file entries;
- unknown
pathOptionsproperties; pathOptions.prune: true;- path constraint entries that are not constraint functions with
verifyandtoRegExp.
The static CLI parser is more permissive than the runtime helper in one place: it can parse a one-item routeFiles array from source. Prefer a string for one route pattern.
RouterCliConfig
interface RouterCliConfig extends DefineRoutesOptions {
readonly routeFiles?: string | readonly string[];
readonly outDir?: string;
}Because it extends DefineRoutesOptions, config also accepts:
pathOptions?: RouterPathOptions;
pathConstraints?: RouterPathConstraints;Prop
Type
pathConstraints are executable configuration. For generated routes.ts, the CLI can preserve a runtime-safe import only when it can resolve a named relative import from config.
Config filenames
const ROUTER_CONFIG_FILENAMES = [
'cookbook-router.config.ts',
'cookbook-router.config.mts',
'cookbook-router.config.cts',
'cookbook-router.config.js',
'cookbook-router.config.mjs',
'cookbook-router.config.cjs',
] as const;
type RouterConfigFilename =
(typeof ROUTER_CONFIG_FILENAMES)[number];The order is the discovery priority.
getRouterConfigFilenames()
function getRouterConfigFilenames():
readonly RouterConfigFilename[];Returns the six standard filenames in priority order.
getRouterConfigFilenames() === ROUTER_CONFIG_FILENAMES;
// trueThe returned value is the exported constant, not a copied array.
getRouterConfigWatchCandidates(cwd?)
function getRouterConfigWatchCandidates(
cwd?: string,
): readonly string[];Returns every standard config filename scoped to cwd.
getRouterConfigWatchCandidates('apps/dashboard');returns:
[
'apps/dashboard/cookbook-router.config.ts',
'apps/dashboard/cookbook-router.config.mts',
'apps/dashboard/cookbook-router.config.cts',
'apps/dashboard/cookbook-router.config.js',
'apps/dashboard/cookbook-router.config.mjs',
'apps/dashboard/cookbook-router.config.cjs',
]With default cwd, candidates are relative filenames:
[
'cookbook-router.config.ts',
'cookbook-router.config.mts',
'cookbook-router.config.cts',
'cookbook-router.config.js',
'cookbook-router.config.mjs',
'cookbook-router.config.cjs',
]Watch candidates are potential files. They are not filtered by existence.
That is how watch mode can recover when a missing config is created later.
loadRouterConfig(options?)
function loadRouterConfig(
options?: {
readonly configFile?: string;
readonly cwd?: string;
readonly fs?: CliFileSystem;
readonly optional?: boolean;
},
): Promise<LoadedRouterConfig | undefined>;Loads and statically parses a router config file.
The named source interface for this options object is not exported from the package root. Treat the inline shape above as the public API.
Prop
Type
Without configFile, discovery starts at cwd and walks upward. At each directory, it checks the six standard filenames in priority order.
With configFile, the path is scoped under cwd unless it is absolute.
Missing config without optional throws:
No cookbook-router config file found. Expected one of: cookbook-router.config.ts, cookbook-router.config.mts, cookbook-router.config.cts, cookbook-router.config.js, cookbook-router.config.mjs, cookbook-router.config.cjs.Explicit unreadable config throws:
Router config "missing.config.ts" could not be found or read.With optional: true, missing or unreadable config returns undefined.
Malformed config still throws even with optional: true.
LoadedRouterConfig
interface LoadedRouterConfig {
readonly config: RouterCliConfig;
readonly configFile: string;
readonly rootDir: string;
readonly runtimeRouteOptions?: GeneratedRouteTreeRuntimeOptions;
}Prop
Type
rootDir is used to resolve config-owned routeFiles and outDir.
GeneratedRouteTreeRuntimeOptions
interface RuntimeImportReference {
readonly path: string;
readonly exportName: string;
}
interface GeneratedRouteTreeRuntimeOptions {
readonly pathConstraints?: RuntimeImportReference;
}Prop
Type
runtimeRouteOptions is emitted only when loadRouterConfig() can preserve a named relative import for pathConstraints.
Supported import shape:
import { pathConstraints } from './path-constraints';
export default defineRouterConfig({
routeFiles: 'src/**/*.route.tsx',
pathConstraints,
} as const);Supported exported constraint module:
import {
createPathConstraint,
} from '@cookbook/router/path';
export const pathConstraints = {
slug: createPathConstraint({
parse() {},
verify() {},
toRegExp: () => '[a-z0-9-]+',
}),
};The generated route module can then import the same runtime-safe constraint object.
Inline or local config-only path constraints are enough for validation and contract generation, but they do not produce a runtime import reference for generated routes.ts.
parseRouterConfig(path, contents)
function parseRouterConfig(
path: string,
contents: string,
): RouterCliConfig;Parses supported static config forms from source text.
It does not import the config module.
It supports these default export forms:
export default defineRouterConfig({
// ...
});export default {
// ...
};const config = defineRouterConfig({
// ...
});
export default config;const config = {
// ...
};
export default config;The local identifier form supports const, let, or var, optional export, and optional TypeScript annotation.
Static values
routeFiles and outDir must be static strings or static string arrays.
Supported:
const routeFiles =
['src/**/*.route.tsx', 'features/**/*.route.tsx'] as const;
const outDir = '.cookbook-router' as const;
export default defineRouterConfig({
routeFiles,
outDir,
} as const);Unsupported:
export default defineRouterConfig({
routeFiles: getRouteFiles(),
} as const);Error:
Router config "cookbook-router.config.ts" property "routeFiles" must be a static string or string array.pathOptions must be an inline static object literal or a local static object declaration.
Unsupported path options throw:
Route file "cookbook-router.config.ts" uses pathOptions that the CLI cannot statically evaluate. Use an inline static object literal or a static object declaration for pathOptions.pathConstraints support:
- inline static object;
- local static object declaration;
- named import from a relative module, through
loadRouterConfig().
parseRouterConfig() returns only RouterCliConfig. It does not return runtimeRouteOptions.
Use loadRouterConfig() when imported path constraints must be preserved for generated runtime routes.
Static path-constraint extraction
Inline config object:
export default defineRouterConfig({
routeFiles: 'src/**/*.route.tsx',
pathConstraints: {
slug: createPathConstraint({
parse() {},
verify() {},
toRegExp: () => '[a-z0-9-]+',
}),
},
} as const);Local static object:
const pathConstraints = {
slug: createPathConstraint({
parse() {},
verify() {},
toRegExp: () => '[a-z0-9-]+',
}),
};
export default defineRouterConfig({
routeFiles: 'src/**/*.route.tsx',
pathConstraints,
} as const);Named relative import:
import {
pathConstraints,
} from './path-constraints';
export default defineRouterConfig({
routeFiles: 'src/**/*.route.tsx',
pathConstraints,
} as const);Only the named relative import form can produce runtimeRouteOptions.
Unsupported:
import pathConstraints from './path-constraints';Unsupported:
import * as constraints from './path-constraints';Unsupported:
import { pathConstraints } from '@acme/router-constraints';Error for unsupported config path constraints:
Router config "cookbook-router.config.ts" uses pathConstraints that the CLI cannot statically evaluate. Use an inline static object, a local static object declaration, or a named import from a runtime-safe module.When an imported module cannot be resolved:
Router config "cookbook-router.config.ts" imports pathConstraints from "./path-constraints", but the module could not be resolved.When the imported export is not a static object declaration:
Router config "cookbook-router.config.ts" imports pathConstraints from "./path-constraints", but export "pathConstraints" is not a static object declaration.Import resolution checks:
./module
./module.ts
./module.tsx
./module.mts
./module.cts
./module.js
./module.mjs
./module.cjs
./module/index.ts
./module/index.tsx
./module/index.jsresolveEffectiveRouteOptions(options)
function resolveEffectiveRouteOptions(
options: CliRouteOptions,
): Promise<CliRouteOptions>;Resolves command/plugin options into the route options used by command execution.
It can:
- load config;
- resolve output directory;
- expand route-file globs;
- derive
routeFileWatchPaths; - merge config route options and command route options;
- preserve runtime-safe config references;
- carry injected
fs,cwd, and watch-mode flags forward.
Resolution behavior
Direct routes
When options.routes is present:
- config is not loaded;
- route files are ignored;
outDiris still resolved;options.routeOptionsis kept as supplied.
{
routes,
routeOptions,
outDir: resolveOutDir(...),
}Explicit route files without explicit config
When options.routeFiles?.[0] exists and configFile is omitted:
- config is not loaded;
- explicit route files are used;
outDirfalls back tocwd/.cookbook-routeror.cookbook-router;- no config
pathOptionsorpathConstraintsare applied.
Explicit route files with explicit config
When both routeFiles and configFile are supplied:
- the config is loaded;
- explicit route files override config
routeFiles; - explicit
outDiroverrides configoutDir; - config route options can still merge with
options.routeOptions; - config runtime route options can be carried forward.
Config route files
When no direct routes and no explicit route files exist:
- config discovery or explicit config loading provides
routeFiles; - config
routeFilesare resolved relative to the configrootDir; - config
outDiris resolved relative to the configrootDir; - config route options participate in validation/generation.
Defaults
When no outDir is supplied by command options or config:
.cookbook-routeris scoped under config.rootDir, then cwd, then the current process directory shape.
Route option merge rules
Config route options and explicit options.routeOptions are merged.
pathOptions are not last-writer-wins. Conflicting values throw.
Error:
Conflicting pathOptions were provided by router config or route source files. Move pathOptions to cookbook-router.config.ts or use the same pathOptions everywhere.pathConstraints are merged by name.
Duplicate names with different constraint objects throw:
Duplicate path constraint name "slug" was provided by router config or route source files. Define each custom path constraint name once, preferably in a runtime-safe module imported by cookbook-router.config.ts.Glob expansion
Config and command routeFiles can be exact paths or simple globs.
Supported glob syntax includes:
*;**;?;{a,b}alternatives.
Default excluded directories:
node_modules
.git
dist
build
coverage
.next
.nuxt
.svelte-kit
.turbo
.vite
.cacheGlob expansion requires fs.readdir and fs.stat.
Error:
Glob routeFiles require a file system with readdir and stat support.If no files match and allowEmptyRouteFiles is not true:
No route files matched routeFiles pattern ["src/**/*.route.tsx"].Route-file watch paths
resolveEffectiveRouteOptions() derives routeFileWatchPaths from the route-file patterns before expansion.
For exact files, the watch path is the file itself.
For globs, the watch path is the static root before the first glob token.
Example:
src/**/*.route.tsxwatch root:
srcDuplicate watch paths are removed.
Watch paths are useful for plugins and watch mode because a route file can be created after the first glob expansion.
Path safety
The CLI path checks are intentionally limited.
It rejects:
- non-string paths;
- empty or whitespace-only paths;
- null bytes.
It also prevents generated artifact filenames from escaping the resolved outDir.
Error:
Refusing to write generated router artifact outside outDir: "...".It refuses to write generated files over route source files:
Refusing to write generated router artifacts over route source file "...".Relative paths are allowed. The current safety layer does not categorically reject every ../ in config, route, or output inputs.
Export inventory for this page
@cookbook/router-cli exports these config values:
ROUTER_CONFIG_FILENAMES
defineRouterConfig
getRouterConfigFilenames
getRouterConfigWatchCandidates
loadRouterConfig
parseRouterConfig
resolveEffectiveRouteOptionsIt exports these config-related types:
GeneratedRouteTreeRuntimeOptions
LoadedRouterConfig
RouterCliConfig
RouterConfigFilename
RuntimeImportReferenceIt also exports these route-option types used by this page:
CliRouteOptions
CliOutputOptions
CliFileSystemThe package root does not export a named LoadRouterConfigOptions type.
Where this bites
prune: true is invalid
Use:
pathOptions: {
prune: 'all',
}or:
pathOptions: {
prune: false,
}Do not use:
pathOptions: {
prune: true,
}Explicit route files can suppress config loading
This command does not load discovered config:
cbr generate --routes "src/**/*.route.tsx"That means config pathOptions, pathConstraints, and outDir are not applied.
Pass --config when you need config options and explicit route files together:
cbr generate --config cookbook-router.config.ts --routes "src/**/*.route.tsx"Empty routeFiles is treated as no override
Programmatic option resolution checks the first route-file entry.
An empty array behaves like no explicit route-file override and can fall back to config.
Use routeFiles with at least one entry when you mean to override config route files.
Imported path constraints must be named relative imports
This can preserve a runtime import:
import {
pathConstraints,
} from './path-constraints';This cannot:
import {
pathConstraints,
} from '@acme/path-constraints';Generated routes.ts needs a runtime-safe relative module path.
parseRouterConfig() does not return runtime imports
parseRouterConfig() returns only RouterCliConfig.
Use loadRouterConfig() when generated route modules need runtime-safe path constraint imports.
Path safety is not a sandbox
The CLI validates path strings and generated artifact placement.
It is not a general filesystem sandbox. Run it with trusted config and route inputs.