@vaadin/hilla-file-router
Version:
Hilla file-based router
153 lines • 5.23 kB
JavaScript
import { createBrowserRouter } from "react-router";
import createFallbackTransformer, { createFallbackRoutes } from "./createFallbackTransformer.js";
import createProtectTransformer from "./createProtectTransformer.js";
import fileRouteTransformer from "./fileRouteTransformer.js";
import mergeLayout from "./mergeLayout.js";
import { mergeRouteTrees } from "./mergeRouteTrees.js";
import mergeSkipLayouts from "./mergeSkipLayout.js";
/**
* A configuration builder for creating a Vaadin-specific router for React with
* authentication and server routes support.
*
* The configuration builder allows you to compose and modify route trees by
* chaining methods that add custom React routes, generated file-based routes,
* layout components, etc. Modifiers are accumulated and applied in order when
* building the final router configuration.
*
* @example
* ```typescript
* const { routes, router } = new RouteConfigurationBuilder()
* .withFileRoutes(fileRoutes)
* .withReactRoutes({ path: '/foo/baz', element: <FooBazPage /> })
* .withFallback(Flow)
* .protect('/login')
* .build();
* ```
*/
export class RouterConfigurationBuilder {
#modifiers = [];
#isFallbackSet = false;
#isLayoutSet = false;
/**
* Adds the given React routes to the current list of routes. All the routes
* are deeply merged to preserve the path uniqueness.
*
* @param routes - An array of React Router route objects to be merged into
* the current route list.
*
* @returns The current instance of the builder for method chaining.
*/
withReactRoutes(routes) {
this.update(routes);
return this;
}
/**
* Adds the given file routes to the current list of routes. All the routes
* are transformed to React RouterObjects and deeply merged to preserve the
* path uniqueness.
*
* @param routes - An array of file-based route objects to be processed and
* merged into the current route list.
*
* @returns The current instance of the builder for method chaining.
*/
withFileRoutes(routes) {
this.update(routes, fileRouteTransformer);
return this;
}
/**
* Adds a fallback component for each branch of the current route tree.
*
* The fallback component is used when no other route matches the requested
* URL. In terms of Vaadin application, after no match on the client side, the
* turn goes to the server-side router.
*
* @remarks This method can be called only once. All the subsequent calls will
* be ignored.
*
* @remarks This method also runs the `withLayout` method with the given
* component to make sure server-side layout is applied to routes.
*
* @param component - The component to use as the fallback and layout
* component.
* @param config - Optional view configuration for the fallback component.
*
* @returns The current instance of the builder for method chaining.
*/
withFallback(component, config) {
if (!this.#isFallbackSet) {
this.#isFallbackSet = true;
this.withLayout(component);
const fallbackRoutes = createFallbackRoutes(component, config);
this.update(
// Add the fallback routes to the end of the route tree.
fallbackRoutes,
// Add the fallback routes to each route tree branch via transformer.
createFallbackTransformer(fallbackRoutes)
);
}
return this;
}
/**
* Adds the parent layout to all views with the `flowLayouts` flag set in the
* ViewConfiguration.
*
* @remarks This method can be called only once. All the subsequent calls will
* be ignored.
*
* @param component - The component to use as the layout for the routes.
* Usually, it is `Flow` component.
*
* @returns The current instance of the builder for method chaining.
*/
withLayout(component) {
if (!this.#isLayoutSet) {
this.#isLayoutSet = true;
this.#modifiers.push((originalRoutes) => mergeLayout(originalRoutes, component));
}
return this;
}
/**
* Adds protection to the route, requiring authentication or authorization to
* access it.
*
* @param redirectPath - Optional path to redirect to when protection fails.
* If not provided, the default redirect page (`/login`) will be used.
*
* @returns The current instance of the builder for method chaining.
*/
protect(redirectPath) {
this.update(undefined, createProtectTransformer(redirectPath));
return this;
}
update(routes, callback) {
this.#modifiers.push((originalRoutes) => mergeRouteTrees(originalRoutes, routes, callback));
return this;
}
/**
* Builds the router configuration by applying all registered modifiers to the
* routes.
*
* @remarks
* This method applies the the logic for layout skipping along with any other
* registered modifiers to transform the routes.
*
* @param options - Optional React `createBrowserRouter` options to configure
* the router.
*
* @returns A RouterConfiguration object containing the processed routes and
* configured browser router
*/
build(options) {
this.#modifiers.push((originalRoutes) => mergeSkipLayouts(originalRoutes));
const routes = this.#modifiers.reduce((acc, mod) => mod(acc) ?? acc, undefined) ?? [];
return {
routes,
router: createBrowserRouter([...routes], {
basename: new URL(document.baseURI).pathname,
...options
})
};
}
}
//# sourceMappingURL=./RouterConfigurationBuilder.js.map