@m5nv/rr-builder
Version:
Fluent API for seamless route & navigation authoring experience in React Router v7 framework mode
297 lines (257 loc) • 8.97 kB
TypeScript
// @ts-check
/* ------------------------------------------------------------------
* @m5nv/rr-builder • Type Declarations • v2.1
* ------------------------------------------------------------------ */
import type { RouteConfigEntry } from "@react-router/dev/routes";
/* ------------------------------------------------------------------
* 1 · Base types for route configuration
* ------------------------------------------------------------------ */
/**
* Route configuration entry extended with navigation metadata
*/
export type ExtendedRouteConfigEntry = RouteConfigEntry & {
/** Navigation metadata stored in handle */
handle?: NavMeta;
/** Internal: section name for routes within a section */
_section?: string;
/** Internal: parent id for re-anchoring nav-only nodes */
_anchor?: string;
};
/**
* Action specification for route-level or global actions
*/
export type ActionSpec = {
id: string;
label: string;
iconName?: string;
};
/**
* Global action specification with optional section scoping
*/
export type GlobalActionSpec = ActionSpec & {
/** Sections where this action should appear (omit for all sections) */
sections?: string[];
/** External URL to open instead of dispatching action */
externalUrl?: string;
};
/* ------------------------------------------------------------------
* 2 · Navigation metadata types
* ------------------------------------------------------------------ */
/**
* Complete navigation metadata for routes, indexes, and external links
*/
export type NavMeta = {
/** Human-readable display name */
label?: string;
/** Icon identifier (typically from an icon library) */
iconName?: string;
/** Grouping identifier for organizing related routes */
group?: string;
/** Sort order within a group (lower numbers first) */
order?: number;
/** Tags for filtering, search, or feature flags */
tags?: string[];
/** Hide from navigation UI while keeping route active */
hidden?: boolean;
/** Require exact path match for active highlighting */
end?: boolean;
/** Access control attributes for authorization */
abac?: string | string[];
/** Route-specific actions available in this context */
actions?: ActionSpec[];
/** Automatically set for external links (cannot be set manually) */
external?: true;
};
/* ------------------------------------------------------------------
* 3 · Navigation tree node types
* ------------------------------------------------------------------ */
/**
* Structural navigation node used internally by code generation
*/
export type NavStructNode = {
id: string;
path: string;
external?: true;
children?: NavStructNode[];
};
/**
* Fully hydrated navigation tree node for runtime use
*/
export type NavTreeNode = NavMeta & {
id?: string;
path: string;
children?: NavTreeNode[];
};
/* ------------------------------------------------------------------
* 4 · Build configuration types
* ------------------------------------------------------------------ */
/**
* Extra configuration passed to build()
*/
export type NavExtras = {
/** Global actions available across sections */
globalActions?: GlobalActionSpec[];
/** Route or action IDs that can display badges */
badgeTargets?: string[];
/** Internal: navigation-only nodes (external links) */
navOnly?: ExtendedRouteConfigEntry[];
};
/* ------------------------------------------------------------------
* 5 · Router adapter type
* ------------------------------------------------------------------ */
/**
* Router adapter for framework integration
*/
export type RouterAdapter = {
Link: any; // Component for navigation links
useLocation: () => { pathname: string };
useMatches: () => { id: string; handle?: any }[];
matchPath: (pattern: string, pathname: string) => any | null;
};
/* ------------------------------------------------------------------
* 6 · Navigation API types
* ------------------------------------------------------------------ */
/**
* Generated navigation API for runtime use
*/
export type NavigationApi = {
/** Get all available section names */
sections(): string[];
/** Get routes for a section (defaults to "main") */
routes(section?: string): NavTreeNode[];
/** Filter routes by tags within a section */
routesByTags(section: string, tags: string[]): NavTreeNode[];
/** Filter routes by group within a section */
routesByGroup(section: string, group: string): NavTreeNode[];
/** Hook to hydrate router matches with navigation metadata */
useHydratedMatches: <T = unknown>() => Array<{ handle: NavMeta }>;
/** Global actions defined in build() */
globalActions: GlobalActionSpec[];
/** Badge target IDs defined in build() */
badgeTargets: string[];
/** Router adapter instance (set by `registerRouter()`) */
router: RouterAdapter;
};
/* ------------------------------------------------------------------
* 7 · One-time adapter registration (singleton)
* ------------------------------------------------------------------ */
/**
* Call exactly once (e.g. in AppShell) to plug your router implementation
* into the navigation API.
*
* Typical use in a React-Router apps could be:
* ```ts
* /// file: root.tsx
* import { Link, matchPath, useLocation, useMatches } from "react-router";
* import navigationApi, { registerRouter } from "@/navigation.generated";
*
* /// one-time registration
* registerRouter({ Link, useLocation, useMatches, matchPath });
* ```
*/
export function registerRouter(adapter: RouterAdapter): void;
/* ------------------------------------------------------------------
* 8 · Builder types
* ------------------------------------------------------------------ */
/**
* Base builder type with common properties
*/
type BaseBuilder = {
readonly entry: ExtendedRouteConfigEntry;
readonly _type: string;
};
/**
* Route builder with navigation and children support
*/
export type RouteBuilder = BaseBuilder & {
readonly _type: 'builder';
nav(meta: Omit<NavMeta, 'external'>): RouteBuilder;
children(...builders: NonSectionBuilder[]): RouteBuilder;
};
/**
* Layout builder with navigation and children support
*/
export type LayoutBuilder = BaseBuilder & {
readonly _type: 'builder';
nav(meta: Omit<NavMeta, 'external'>): LayoutBuilder;
children(...builders: NonSectionBuilder[]): LayoutBuilder;
};
/**
* Index builder with navigation support only
*/
export type IndexBuilder = BaseBuilder & {
readonly _type: 'builder';
nav(meta: Omit<NavMeta, 'external'>): IndexBuilder;
// NO children() method
};
/**
* External link builder with navigation support only
*/
export type ExternalBuilder = BaseBuilder & {
readonly _type: 'builder';
nav(meta: Omit<NavMeta, 'external'>): ExternalBuilder;
// NO children() method
};
/**
* Section builder for top-level organization of application's route
* configuration into a `forest of trees`.
*/
export type SectionBuilder = BaseBuilder & {
readonly _type: 'section';
readonly name: string;
nav(meta: Omit<NavMeta, 'external'>): SectionBuilder;
children(...builders: NonSectionBuilder[]): SectionBuilder;
};
/**
* Union of all non-section builders
*/
export type NonSectionBuilder = RouteBuilder | LayoutBuilder | IndexBuilder | ExternalBuilder;
/**
* Union of builders that can be prefixed
*/
export type PrefixableBuilder = RouteBuilder | LayoutBuilder | IndexBuilder;
/**
* Union of all possible builders
*/
export type Builder = NonSectionBuilder | SectionBuilder;
/* ------------------------------------------------------------------
* 9 · RR utility function wrappers + section and external for DX
* ------------------------------------------------------------------ */
/** Create a route builder */
export function route(
path: string,
file: string,
options?: Parameters<typeof import("@react-router/dev/routes").route>[2],
): RouteBuilder;
/** Create an index route builder */
export function index(
file: string,
options?: Parameters<typeof import("@react-router/dev/routes").index>[1],
): IndexBuilder;
/** Create a layout builder */
export function layout(
file: string,
options?: Parameters<typeof import("@react-router/dev/routes").layout>[1],
): LayoutBuilder;
/** Create prefixed route builders */
export function prefix(
prefixPath: string,
builders: PrefixableBuilder[],
): PrefixableBuilder[];
/** Create an external link builder */
export function external(
url: string,
opts?: { id?: string },
): ExternalBuilder;
/** Create a section builder */
export function section(name: string): SectionBuilder;
/** Create shared layout with multiple sections */
export function sharedLayout(
layoutFile: string,
sections: Record<string, NonSectionBuilder[]>
): SectionBuilder[];
/** Build the final route configuration */
export function build(
builders: Builder[],
extras?: NavExtras,
): RouteConfigEntry[] & { readonly nav?: NavExtras };