UNPKG

@wizecorp/stratusjs

Version:
154 lines 5.91 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { Suspense, lazy, useEffect, useState } from 'react'; import { BrowserRouter, Routes, Route } from 'react-router-dom'; import { hydrateRoot, createRoot } from 'react-dom/client'; import { ServiceProvider } from '../services/ServiceContext'; import { ServiceContainer } from '../services/ServiceContainer'; import { RouterProvider } from './RouterContext'; import { discoverRoutes } from './routeUtils'; import { getConfig, setConfig } from '../config'; const DefaultFallback = () => _jsx("div", { children: "Loading..." }); const DefaultErrorBoundary = ({ error, retry }) => (_jsxs("div", { children: [_jsx("h2", { children: "An error occurred" }), _jsx("p", { children: error.message }), _jsx("button", { onClick: retry, children: "Retry" })] })); // Layout cache const layoutsCache = new Map(); /** * Hybrid router that supports both client-side and server-side rendering */ export const HybridRouter = ({ config, fallback = _jsx(DefaultFallback, {}), errorBoundary: ErrorBoundary = DefaultErrorBoundary, routes: predefinedRoutes, serviceContainer }) => { const [routes, setRoutes] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [container] = useState(() => serviceContainer || new ServiceContainer()); const [hydrationData, setHydrationData] = useState(null); // Configure Stratus on startup useEffect(() => { if (config) { setConfig(config); } }, [config]); // Load hydration data if available (SSR) useEffect(() => { if (typeof window !== 'undefined') { const data = window.__STRATUS_DATA__; if (data) { setHydrationData(data); // Clean up delete window.__STRATUS_DATA__; } } }, []); // Route discovery useEffect(() => { const loadRoutes = async () => { try { setLoading(true); if (predefinedRoutes) { setRoutes(predefinedRoutes); } else { const discoveredRoutes = await discoverRoutes(); setRoutes(discoveredRoutes); } setError(null); } catch (err) { console.error('Error loading routes:', err); setError(err instanceof Error ? err : new Error('Unknown error')); } finally { setLoading(false); } }; loadRoutes(); }, [predefinedRoutes]); const retry = () => { setError(null); setRoutes([]); setLoading(true); }; if (loading) { return _jsx(Suspense, { fallback: fallback, children: fallback }); } if (error) { return _jsx(ErrorBoundary, { error: error, retry: retry }); } const app = (_jsx(ServiceProvider, { container: container, children: _jsx(BrowserRouter, { basename: getConfig().basePath, children: _jsx(RouterProvider, { children: _jsx(Routes, { children: routes.map((route) => (_jsx(Route, { path: route.path, element: _jsx(RouteRenderer, { route: route, fallback: fallback, initialProps: hydrationData?.props }) }, route.path))) }) }) }) })); return app; }; const RouteRenderer = ({ route, fallback, initialProps }) => { const LazyComponent = lazy(route.component); const [Layout, setLayout] = useState(null); // Load layout if necessary useEffect(() => { const loadLayout = async () => { if (!route.layout) { setLayout(null); return; } // Check cache if (layoutsCache.has(route.layout)) { setLayout(layoutsCache.get(route.layout)); return; } try { const config = getConfig(); const layoutPath = `${config.layoutsDir}/${route.layout}`; const layoutModule = await import(/* @vite-ignore */ layoutPath); const LayoutComponent = layoutModule.default; // Cache layout layoutsCache.set(route.layout, LayoutComponent); setLayout(() => LayoutComponent); } catch (err) { console.warn(`Layout not found: ${route.layout}`, err); setLayout(null); } }; loadLayout(); }, [route.layout]); const content = (_jsx(Suspense, { fallback: fallback, children: _jsx(LazyComponent, { ...(initialProps || {}) }) })); if (Layout) { return _jsx(Layout, { children: content }); } return content; }; /** * Client-side hydration function */ export const hydrateApp = (element, container, callback) => { const hydrationData = window.__STRATUS_DATA__; if (hydrationData) { // Hydrate if server-rendered hydrateRoot(container, element, { onRecoverableError: (error) => { console.error('Hydration error:', error); } }); if (callback) callback(); } else { // Regular client-side rendering createRoot(container).render(element); if (callback) callback(); } }; /** * Helper to create a Stratus app with SSR support */ export const createStratusApp = (props) => { return _jsx(HybridRouter, { ...props }); }; /** * Mount the app to DOM (client-side) */ export const mountApp = (props, containerId = 'root', callback) => { const container = document.getElementById(containerId); if (!container) { throw new Error(`Container with id "${containerId}" not found`); } const app = createStratusApp(props); hydrateApp(app, container, callback); }; //# sourceMappingURL=HybridRouter.js.map