Module
A React Framework for Deno that makes it easy to create highly interactive apps that have server side rendering with file based routing for both your UI and API.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205import { ComponentType, Context, lazy as reactLazy, LazyExoticComponent, ReactNode, startTransition, StrictMode,} from "npm/react";import { HelmetProvider } from "npm/react-helmet-async";import { hydrateRoot } from "npm/react-dom/client";import { createBrowserRouter, RouteObject, RouterProvider,} from "npm/react-router-dom";
import { AppWindow, createAppContext } from "./env.ts";export { createAppContext, getEnv, isBrowser, isDevelopment, isProduction, isServer, isTest,} from "./env.ts";export type { AppEnvironment } from "./env.ts";import { AppErrorBoundaryProps, AppErrorContext, FallbackProps, HttpError, withAppErrorBoundary,} from "./error.tsx";export { AppErrorBoundary, DefaultErrorFallback, HttpError, isHttpError, NotFound, useAutoReset, withAppErrorBoundary,} from "./error.tsx";export type { AppErrorBoundaryProps, ErrorBoundaryProps, FallbackProps, HttpErrorOptions,} from "./error.tsx";
/** * An interface that defines the configuration for the application's hydration. */export interface HydrateOptions< AppContext extends Record<string, unknown> = Record<string, unknown>,> { /** * A react router route object. * The build script will automatically generate this for your application's routes. * The route object is a default export from `_main.tsx` in your routes directory. */ route: RouteObject; /** A React Component that wraps the entire application. */ Provider?: ComponentType<{ children: ReactNode }>; /** A context object for the App. */ Context?: Context<AppContext>;}
/** An interface that extends `HydrateOptions` and is used to configure the App Component. */interface AppOptions< AppContext extends Record<string, unknown> = Record<string, unknown>,> extends HydrateOptions<AppContext> { Provider: ComponentType<{ children: ReactNode }>; Context: Context<AppContext>;}
/** * A React component used to render the application. */function App< AppContext extends Record<string, unknown> = Record<string, unknown>,>({ route, Provider, Context }: AppOptions<AppContext>) { const router = createBrowserRouter([route]); const rawError = (window as AppWindow).app.error; const { stack, ...errorOptions } = rawError ?? {}; const error = rawError && new HttpError(errorOptions); if (error && typeof stack === "string") { error.stack = stack; } console.error(error);
const context = (window as AppWindow<AppContext>).app.context ?? {}; const appErrorContext = { error }; return ( <StrictMode> <HelmetProvider> <AppErrorContext.Provider value={appErrorContext}> <Context.Provider value={context}> <Provider> <RouterProvider router={router} /> </Provider> </Context.Provider> </AppErrorContext.Provider> </HelmetProvider> </StrictMode> );}
/** * This function is used to hydrate the application in the browser. * It turns the server rendered application into a single-page application (SPA). * Hydration is not required if only server-side rendering is desired. * * If the default configuration is used, this function will load the route generated from the application's routes. * * ```tsx * import { hydrate } from "x/udibo_react_app/app.tsx"; * * import route from "./routes/_main.tsx"; * * hydrate({ route }); * ``` * * An optional Provider argument can be used to include customer providers around the application. */export function hydrate< AppContext extends Record<string, unknown> = Record<string, unknown>,>({ route, Provider, Context }: HydrateOptions<AppContext>) { const hydrate = () => startTransition(() => { hydrateRoot( document.body, <App route={route} Provider={Provider ?? (({ children }) => <>{children}</>)} Context={Context ?? createAppContext<AppContext>()} />, ); });
if (typeof requestIdleCallback !== "undefined") { requestIdleCallback(hydrate); } else { // Safari doesn't support requestIdleCallback // https://caniuse.com/requestidlecallback setTimeout(hydrate, 1); }}
/** * An interface that defines a route file. * A route file exports a react component by default. * It can optionally export an `ErrorFallback` or `boundary` that will be used for an `AppErrorBoundary` around the react component that it exports by default. */export type RouteFile = { /** The react component for the route. */ default: ComponentType; /** An ErrorFallback for an AppErrorBoundary around the react component for the route. */ ErrorFallback?: ComponentType<FallbackProps>; /** The boundary used by the route. If there is an ErrorFallback exported, exporting a boundary will override the default boundary. */ boundary?: string;};
/** * A function used to lazily load a component. This function takes in a factory that returns Promise that resolves to a React component. * The purpose of this function is to automatically add error boundaries to routes with an `ErrorFallback` or `boundary` export. * This function is intended for internal use only, and is typically used in the generated `_main.tsx` file for a routes directory. */export function lazy< T extends RouteFile,>(factory: () => Promise<T>): LazyExoticComponent<ComponentType>;export function lazy< T extends RouteFile,>( boundary: string, factory: () => Promise<T>,): LazyExoticComponent<ComponentType>;export function lazy< T extends RouteFile,>( boundaryOrFactory?: string | (() => Promise<T>), factory?: () => Promise<T>,): LazyExoticComponent<ComponentType> { let boundary = typeof boundaryOrFactory === "string" ? boundaryOrFactory : undefined; if (typeof boundaryOrFactory !== "string") factory = boundaryOrFactory; return reactLazy(async () => { const { default: Component, ErrorFallback, boundary: boundaryOverride } = await factory!(); const errorBoundaryProps = { FallbackComponent: ErrorFallback, } as AppErrorBoundaryProps; if (boundaryOverride) boundary = boundaryOverride; if (boundary) errorBoundaryProps.boundary = boundaryOverride ?? boundary;
return { default: errorBoundaryProps.FallbackComponent ? withAppErrorBoundary(Component, errorBoundaryProps) : Component, }; });}