@furystack/shades
Version:
A lightweight UI framework for FuryStack with JSX support
73 lines • 2.84 kB
JavaScript
import { LocationService } from '../services/location-service.js';
/**
* Walks a route tree and returns the route value declared at the given path,
* matching each URL pattern segment-by-segment. The root `/` pattern is
* transparent: its children are searched with the full path.
*
* Exported for unit testing only — consumers should go through
* {@link createNestedHooks}.
*
* @internal
*/
export const walkRoute = (routes, path) => {
for (const [pattern, route] of Object.entries(routes)) {
if (pattern === path)
return route;
if (pattern === '/' && route.children) {
const nested = walkRoute(route.children, path);
if (nested)
return nested;
}
else if (route.children && path.startsWith(pattern)) {
const rest = path.slice(pattern.length);
if (rest.startsWith('/') || rest === '') {
const nested = walkRoute(route.children, rest || '/');
if (nested)
return nested;
}
}
}
return undefined;
};
/**
* Creates a pair of type-safe synchronous read helpers (`getTypedQuery`,
* `getTypedHash`) bound to a specific route tree. The returned functions
* validate the current URL's query and hash against the route declared at
* the given path, returning typed values or `null`/`undefined` on mismatch.
*
* The concrete route tree is captured at factory time, so call sites only
* pass `(injector, path)`; the type-level narrowing is derived from the
* inferred tree generic.
*
* @typeParam TRoutes - The route tree type (inferred from the `routes` argument)
*
* @example
* ```typescript
* const { getTypedQuery, getTypedHash } = createNestedHooks(appRoutes)
*
* const query = getTypedQuery(injector, '/users') // typed
* const hash = getTypedHash(injector, '/users') // typed
* ```
*/
export const createNestedHooks = (routes) => {
return {
getTypedQuery: (injector, path) => {
const route = walkRoute(routes, path);
if (!route?.query)
return null;
const locationService = injector.get(LocationService);
const deserialized = locationService.onDeserializedLocationSearchChanged.getValue();
return route.query(deserialized);
},
getTypedHash: (injector, path) => {
const route = walkRoute(routes, path);
const declaredHash = route?.hash;
if (!declaredHash)
return undefined;
const locationService = injector.get(LocationService);
const currentHash = locationService.onLocationHashChanged.getValue();
return declaredHash.includes(currentHash) ? currentHash : undefined;
},
};
};
//# sourceMappingURL=nested-hooks.js.map