@wizecorp/stratusjs
Version:
Stratus React Framework
154 lines • 5.91 kB
JavaScript
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