@salla.sa/twilight-theme-engine/routes
A lightweight index of shared route types, locale helpers and one redirect loader; route modules themselves live only on the granular subpaths.
import { RouteModule, LoaderContext, IdParams, SlugIdParams, PageQuery, RoutePagination, NotFoundPageProps, HeadDescriptor, slugBrandRedirectLoader, FALLBACK_LOCALE, SUPPORTED_LOCALES, isLocale, getLanguageInfo, Locale } from '@salla.sa/twilight-theme-engine/routes';In plain words
The name suggests every page lives here. None does. So that each page's code downloads only when that page is visited, the modules (Home, Cart, Blog…), their loaders and their components are exported only from their own paths, such as /routes/cart.
What remains is small: the types route modules share, the props type of every page, the locale helpers and slugBrandRedirectLoader. Prefer the granular path for any name that has one.
Signature
interface RouteModule<TData, TParams extends Record<string, string>, TProps> {
loader: (
ctx: { params: TParams; locale?: string },
extend?: (data: TData, ctx: { params: TParams }) => Promise<Record<string, unknown>> | Record<string, unknown>
) => Promise<TData>;
head: (ctx: TwilightContext, data: TData) => HeadDescriptor;
Component: React.ComponentType<TProps>;
}
interface IdParams { id: string }
interface SlugIdParams { slug: string; id: string }
interface PageQuery { page: number }
interface LoaderContext { search?: { page?: string | number }; locale?: string }
interface RoutePagination { current?: number; next?: string | null; previous?: string | null }
interface NotFoundPageProps {
title?: string; message?: string; showHomeLink?: boolean; homeLinkText?: string;
LinkComponent?: React.ComponentType<{ to: string; className?: string; style?: React.CSSProperties; children: React.ReactNode }>;
}
// Re-exported: HeadDescriptor (/utils/head), FALLBACK_LOCALE, SUPPORTED_LOCALES, isLocale,
// getLanguageInfo, Locale (/i18n), slugBrandRedirectLoader and SlugBrandParams
// (/routes/seo-redirects), and the props and data types of every page module.Example
// Names with a granular path: import them from there, not from the barrel.
import { FALLBACK_LOCALE, isLocale } from '@salla.sa/twilight-theme-engine/i18n';
import type { HeadDescriptor } from '@salla.sa/twilight-theme-engine/utils/head';
import type { WishlistPageProps } from '@salla.sa/twilight-theme-engine/routes/account';
export function localeOf(segment: string | undefined) {
return isLocale(segment) ? segment : FALLBACK_LOCALE;
}
export function wishlistHead(data: WishlistPageProps): HeadDescriptor {
return { title: data.page.title, robots: 'noindex' };
}
How it behaves
RouteModule,LoaderContext,IdParams,SlugIdParams,PageQuery,RoutePaginationandNotFoundPagePropshave no other public path. They are types, so importing them adds nothing to a bundle.The route modules are plain
as constobjects that also carryid; they are not declared withRouteModule, which describes their common shape.LoaderContextis the Wishlist and Wallet loaders' argument,IdParamsandSlugIdParamsare theparamsof id and slug pages, andPageQueryis theirquery.NotFoundPagePropstypes the engine's not-found page, which the router shows fornotFound(); that component is not exported.The generated redirect route (
/{-$locale}/redirect/$type/$id) importsFALLBACK_LOCALEfrom this path, one reason it must stay light.
Gotchas
The engine's shared ESLint config (
@salla.sa/twilight-theme-engine/eslint) warns on every import from this path throughno-restricted-imports,import typeincluded, saying it pulls in all route modules. Its contents no longer do, but the rule stands: for the types with no other path, expect the warning or disable it on that line.docs/03-routing-system.md says all route modules are exported here and imports
RouteContextfrom@salla.sa/twilight-theme-engine/routes/types. Neither exists: the modules are on their own subpaths, there is no/routes/typessubpath and noRouteContexttype.
Related
Every built-in page is an object with a loader that fetches its data, a head that sets its tags, and a Component that draws it.
slugBrandRedirectLoader, pendingOrdersRedirectLoaderLoaders that render nothing and redirect: old /<slug>/brand-<id> links to the brand's product list, and /pending-orders to the orders list.
Order, OrderItem, OrderListItemThe order shapes the account and thank-you pages use: a full order, one line of it, and the lighter row the orders list returns.