UNPKG

@vaadin/hilla-file-router

Version:

Hilla file-based router

179 lines 7.04 kB
import { transformTree } from "../../shared/transformTree.js"; import { createRouteKey } from "./utils.js"; function defaultRouteTransformer({ original, override, children }) { return { ...original, ...override, children }; } /** * Updates a route tree by merging, transforming, or replacing route objects. * * This function takes an existing route tree and a new tree of routes, then * applies a transformer recursively to produce an updated route tree. The * update strategy depends on the presence of the original and added trees: * - If both `existingRoutes` and `tree` are provided, they are recursively * merged them using the provided transformer. * - If only `existingRoutes` is provided, only the original routes are * transformed. * - If only `tree` is provided, it is transformed and used as a new route tree. * * @typeParam T - The type of the route-like objects in the tree. * * @param existingRoutes - The current route tree. * @param tree - The new tree of route-like objects to merge recursively. * @param transformer - A function to transform route objects. * * @returns The updated route tree as an array of `RouteObject`, or `undefined` * if no routes are present. */ export function mergeRouteTrees(existingRoutes, tree, transformer = defaultRouteTransformer) { return transformTree([existingRoutes, tree], null, ([original, added], next) => { if (original && added) { return mergeBothTrees(original, added, { transformer, next }); } if (original) { return transformOriginalRoutesOnly(original, { transformer, next }); } if (added) { return added.map((route) => transformOverrideOnly(route, { transformer, next })).filter((r) => r != null); } return undefined; }); } /** * Merges two route trees into a single one, applying transformations and * overrides as specified by the provided context. * * For each unique path, this function: * - Applies the override to all matching original routes if both exist. * - Transforms original routes if no override exists. * - Transforms override and adds it as a new route if no original route exists. * * @typeParam T - The type of the override route, extending `RouteLike`. * * @param originals - The original tree of route objects. * @param overrides - The tree of override route objects. * @param ctx - The context used for transforming and applying overrides. * * @returns A new tree of merged and transformed route objects. * @throws If multiple overrides with the same route key are found. */ function mergeBothTrees(originals, overrides, ctx) { const pathKeys = new Set([...originals.map((r) => createRouteKey(r)), ...overrides.map((r) => createRouteKey(r))]); return Array.from(pathKeys).reduce((acc, pathKey) => { const originalRoutes = originals.filter((r) => createRouteKey(r) === pathKey); const _overrides = overrides.filter((r) => createRouteKey(r) === pathKey); if (_overrides.length > 1) { throw new Error("Adding multiple routes with the same path is not allowed"); } const [override] = _overrides; if (originalRoutes.length > 0 && override) { applyOverrideForMultipleRoutesWithSamePath(originalRoutes, override, ctx).forEach((route) => acc.push(route)); } else if (originalRoutes.length > 0) { transformOriginalRoutesWithSamePath(originalRoutes, ctx).forEach((route) => acc.push(route)); } else if (override) { const route = transformOverrideOnly(override, ctx); if (route) { acc.push(route); } } return acc; }, []); } /** * In this case, we have multiple original routes and one override per path. To * merge everything together, we apply the transformer to each original route in * conjunction with the override. * * @remarks To disambiguate the routes, we pass the `dupe` flag to the * transformer for each original route except the last one, which is considered * the "main" route. It follows the React Router logic, where the last route * serves as the fallback for all previous routes with the same path. * * @typeParam T - The type of the route-like to apply. * * @param routes - Original routes to update (they all have the same path). * @param override - The override route-like to apply. * @param transformer - The transformer function to apply to each route/override * pair. * @param next - The next callback to call with the children of the routes. * * @returns The updated routes with the override applied. */ function applyOverrideForMultipleRoutesWithSamePath(routes, override, { transformer, next }) { return routes.map((route, index) => transformer({ original: route, override, children: next([route.children, override.children]), dupe: index < routes.length - 1 }) ?? route); } function createOriginalRoutesOnlyTransformer(shouldHandleDuplicates) { return (routes, { transformer, next }) => routes.map((route, index) => transformer({ original: route, children: next([route.children, undefined]), dupe: shouldHandleDuplicates && index < routes.length - 1 }) ?? route); } /** * In this case, we have multiple original routes and one override per path. To * merge everything together, we apply the transformer to each original route in * conjunction with the override. * * @remarks As in {@link applyOverrideForMultipleRoutesWithSamePath}, here the * `dupe` flag is also used to indicate that the transforming route is not the * "main" route (the last one in the list). * * @typeParam T - The type of the route-like to apply. * * @param routes - Original routes to update (they all have the same path). * @param transformer - The transformer function to apply to each route. * @param next - The next callback to call with the children of the routes. * * @returns The updated routes with the transformer applied. */ const transformOriginalRoutesWithSamePath = createOriginalRoutesOnlyTransformer(true); /** * In this case, we don't have override tree at all, so we simply transform the * original routes only. * * @typeParam T - The type of the route-like to apply. * * @param routes - Original routes to update (they all have the same path). * @param transformer - The transformer function to apply to each route. * @param next - The next callback to call with the children of the routes. * * @returns The updated routes with the transformer applied. */ const transformOriginalRoutesOnly = createOriginalRoutesOnlyTransformer(false); /** * In this case, we have no original routes, so we simply apply the * transformer to the override tree, creating a new route object. * * This function adds a completely new route to the tree. * * @param override - The override route-like to apply. * @param transformer - The transformer function to apply to the override. * @param next - The next callback to call with the children of the override. * * @returns The new route created from the override. */ function transformOverrideOnly(override, { transformer, next }) { return transformer({ original: undefined, override, children: next([undefined, override.children]) }); } //# sourceMappingURL=./mergeRouteTrees.js.map