Skip to content

Commit

Permalink
refactor(file-router): get route title from component if there is no …
Browse files Browse the repository at this point in the history
…config title.
  • Loading branch information
Lodin committed Jan 31, 2024
1 parent de38f7e commit 46bd2bf
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 64 deletions.
9 changes: 4 additions & 5 deletions packages/ts/hilla-file-router/src/generateJson.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { RouteMeta } from './collectRoutes.js';
import type { ViewConfig } from './react.js';
import { processPattern } from './utils.js';
import { processPattern, prepareConfig, type ViewConfig } from './utils.js';

function* traverse(
views: RouteMeta,
Expand All @@ -19,7 +18,7 @@ function* traverse(

type RouteModule = Readonly<{
default: unknown;
meta?: ViewConfig;
config?: ViewConfig;
}>;

export default async function generateJson(views: RouteMeta): Promise<string> {
Expand All @@ -30,8 +29,8 @@ export default async function generateJson(views: RouteMeta): Promise<string> {
.filter(({ file, layout }) => !!file || !!layout)
.map(({ file, layout }) => (file ? file : layout!).toString())
.map(async (path) => {
const { meta }: RouteModule = await import(`${path.substring(0, path.lastIndexOf('.'))}.js`);
return meta;
const { config, default: fn }: RouteModule = await import(`${path.substring(0, path.lastIndexOf('.'))}.js`);
return prepareConfig(config, fn);
}),
);

Expand Down
62 changes: 3 additions & 59 deletions packages/ts/hilla-file-router/src/react.ts
Original file line number Diff line number Diff line change
@@ -1,67 +1,11 @@
import type { UIMatch } from '@remix-run/router';
import { type ComponentType, createElement } from 'react';
import { type RouteObject, useMatches } from 'react-router';
import { type AgnosticRoute, transformRoute } from './utils.js';

export type ViewConfig = Readonly<{
/**
* View title used in the main layout header, as <title> and as the default
* for the menu entry. If not defined, then the view function name is converted
* from CamelCase after removing any "View" postfix.
*/
title?: string;

/**
* Same as in the explicit React Router configuration.
*/
rolesAllowed?: string[];

/**
* Allows overriding the route path configuration. Uses the same syntax as
* the path property with React Router.This can be used to define a route
* that conflicts with the file name conventions, e.g. /foo/index
*/
route?: string;

/**
* Controls whether the view implementation will be lazy loaded the first time
* it's used or always included in the bundle. If set to undefined (which is
* the default), views mapped to / and /login will be eager and any other view
* will be lazy (this is in sync with defaults in Flow)
*/
lazy?: boolean;

/**
* If set to false, then the route will not be registered with React Router,
* but it will still be included in the main menu and used to configure
* Spring Security
*/
register?: boolean;

menu?: Readonly<{
/**
* Title to use in the menu. Falls back the title property of the view
* itself if not defined.
*/
title?: string;

/**
* Used to determine the order in the menu. Ties are resolved based on the
* used title. Entries without explicitly defined ordering are put below
* entries with an order.
*/
priority?: number;
/**
* Set to true to explicitly exclude a view from the automatically
* populated menu.
*/
exclude?: boolean;
}>;
}>;
import { type AgnosticRoute, transformRoute, prepareConfig, type ViewConfig } from './utils.js';

export type RouteModule<P = object> = Readonly<{
default: ComponentType<P>;
meta?: ViewConfig;
config?: ViewConfig;
}>;

/**
Expand All @@ -78,7 +22,7 @@ export function toReactRouter(routes: AgnosticRoute<RouteModule>): RouteObject {
path,
element: module?.default ? createElement(module.default) : undefined,
children: children.length > 0 ? (children as RouteObject[]) : undefined,
handle: module?.meta,
handle: prepareConfig(module?.config, module?.default),
}) satisfies RouteObject,
);
}
Expand Down
73 changes: 73 additions & 0 deletions packages/ts/hilla-file-router/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,59 @@
export type ViewConfig = Readonly<{
/**
* View title used in the main layout header, as <title> and as the default
* for the menu entry. If not defined, then the view function name is converted
* from CamelCase after removing any "View" postfix.
*/
title?: string;

/**
* Same as in the explicit React Router configuration.
*/
rolesAllowed?: string[];

/**
* Allows overriding the route path configuration. Uses the same syntax as
* the path property with React Router.This can be used to define a route
* that conflicts with the file name conventions, e.g. /foo/index
*/
route?: string;

/**
* Controls whether the view implementation will be lazy loaded the first time
* it's used or always included in the bundle. If set to undefined (which is
* the default), views mapped to / and /login will be eager and any other view
* will be lazy (this is in sync with defaults in Flow)
*/
lazy?: boolean;

/**
* If set to false, then the route will not be registered with React Router,
* but it will still be included in the main menu and used to configure
* Spring Security
*/
register?: boolean;

menu?: Readonly<{
/**
* Title to use in the menu. Falls back the title property of the view
* itself if not defined.
*/
title?: string;

/**
* Used to determine the order in the menu. Ties are resolved based on the
* used title. Entries without explicitly defined ordering are put below
* entries with an order.
*/
priority?: number;
/**
* Set to true to explicitly exclude a view from the automatically
* populated menu.
*/
exclude?: boolean;
}>;
}>;

export type AgnosticRoute<T> = Readonly<{
path: string;
module?: T;
Expand All @@ -23,3 +79,20 @@ export function transformRoute<T, U>(
children ? Array.from(children, (child) => transformRoute(child, getChildren, transformer)) : [],
);
}

const viewPattern = /view/giu;

export function prepareConfig(config?: ViewConfig, component?: unknown): ViewConfig | undefined {
if (config?.title) {
return config;
}

if (component && typeof component === 'object' && 'name' in component && typeof component.name === 'string') {
return {
...config,
title: component.name.replace(viewPattern, ''),
};
}

return config;
}

0 comments on commit 46bd2bf

Please sign in to comment.