vista-core-js 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/ErrorOverlay.d.ts +7 -0
  2. package/dist/ErrorOverlay.js +68 -0
  3. package/dist/app.d.ts +21 -0
  4. package/dist/app.js +119 -0
  5. package/dist/client/link.d.ts +23 -0
  6. package/dist/client/link.js +42 -0
  7. package/dist/client.d.ts +2 -0
  8. package/dist/client.js +290 -0
  9. package/dist/components/PixelBlast.d.ts +28 -0
  10. package/dist/components/PixelBlast.js +584 -0
  11. package/dist/entry-client.d.ts +1 -0
  12. package/dist/entry-client.js +56 -0
  13. package/dist/entry-server.d.ts +9 -0
  14. package/dist/entry-server.js +33 -0
  15. package/dist/error-overlay.d.ts +1 -0
  16. package/dist/error-overlay.js +166 -0
  17. package/dist/font/google/index.d.ts +1923 -0
  18. package/dist/font/google/index.js +1948 -0
  19. package/dist/image/get-img-props.d.ts +20 -0
  20. package/dist/image/get-img-props.js +46 -0
  21. package/dist/image/image-config.d.ts +20 -0
  22. package/dist/image/image-config.js +17 -0
  23. package/dist/image/image-loader.d.ts +7 -0
  24. package/dist/image/image-loader.js +10 -0
  25. package/dist/image/index.d.ts +12 -0
  26. package/dist/image/index.js +12 -0
  27. package/dist/index.d.ts +6 -0
  28. package/dist/index.js +4 -0
  29. package/dist/plugin.d.ts +5 -0
  30. package/dist/plugin.js +88 -0
  31. package/dist/router.d.ts +18 -0
  32. package/dist/router.js +55 -0
  33. package/package.json +47 -0
  34. package/src/ErrorOverlay.tsx +194 -0
  35. package/src/app.tsx +138 -0
  36. package/src/assets/vista.gif +0 -0
  37. package/src/client/link.tsx +85 -0
  38. package/src/client.tsx +368 -0
  39. package/src/entry-client.tsx +70 -0
  40. package/src/entry-server.tsx +58 -0
  41. package/src/error-overlay.ts +187 -0
  42. package/src/font/google/index.d.ts +19011 -0
  43. package/src/font/google/index.ts +1968 -0
  44. package/src/font/types.d.ts +13 -0
  45. package/src/image/get-img-props.ts +100 -0
  46. package/src/image/image-config.ts +22 -0
  47. package/src/image/image-loader.ts +23 -0
  48. package/src/image/index.tsx +21 -0
  49. package/src/index.ts +7 -0
  50. package/src/plugin.ts +100 -0
  51. package/src/router-loader.ts +51 -0
  52. package/src/router.tsx +80 -0
  53. package/tsconfig.json +23 -0
package/src/app.tsx ADDED
@@ -0,0 +1,138 @@
1
+ import React, { Suspense } from 'react';
2
+ import { useRouter } from './router.js';
3
+ import { pages, rootLayout, getPageKey } from './router-loader.js';
4
+
5
+ /* NOTE: Keeping VistaHead as is (or assuming it is preserved by replace_file_content Context) */
6
+ // I will rely on the tool to keep lines I didn't verify in 'Context' but I need to be careful.
7
+ // Actually, I must ensure I don't delete lines 51-110. The prompt for replace_file_content requires TargetContent.
8
+ // I will target the top section and App component separately or use multi_replace if needed?
9
+ // The tool documentation says "Use this tool ONLY when you are making a SINGLE CONTIGUOUS block of edits".
10
+ // I need to change lines 11-50 (Loaders) AND lines 136-142 (App component logic).
11
+ // These are non-contiguous.
12
+ // I MUST use multi_replace_file_content.
13
+
14
+
15
+ // Define Asset Types
16
+ export type VistaAsset = {
17
+ type: 'style' | 'script' | 'preamble';
18
+ src?: string;
19
+ content?: string;
20
+ key?: string;
21
+ }
22
+
23
+
24
+ function VistaHead({ assets, metadata }: { assets?: VistaAsset[], metadata?: any }) {
25
+ // Serialize assets for client hydration
26
+ const serializedAssets = JSON.stringify(assets || []);
27
+
28
+ // With hydrateRoot(document) and React 19, we can just render the tags.
29
+ // React handles the hoisting for stylesheets if we hint them, or just standard placement.
30
+ // Since we are hydrating the full document, and RootLayout contains <head>,
31
+ // these tags will be rendered where they are (in body/root children).
32
+ // React 19 "Hoistable" Resources: <title>, <meta>, <link rel=stylesheet>.
33
+ // They will be hoisted to <head> automatically.
34
+
35
+ return (
36
+ <>
37
+ {/* Metadata */}
38
+ {metadata?.title && <title>{metadata.title}</title>}
39
+ {metadata?.description && <meta name="description" content={metadata.description} />}
40
+
41
+ {/* Assets */}
42
+ {assets?.map((asset, index) => {
43
+ if (asset.type === 'style' && asset.src) {
44
+ // Precedence="default" opts into React 19 Resource Loading/Hoisting
45
+ /* @ts-ignore: React 19 prop */
46
+ return <link key={asset.key || asset.src} rel="stylesheet" href={asset.src} precedence="default" />;
47
+ }
48
+ if (asset.type === 'script' && asset.src) {
49
+ return <script key={asset.key || asset.src} type="module" src={asset.src} />;
50
+ }
51
+ if (asset.type === 'preamble' && asset.content) {
52
+ return (
53
+ <script
54
+ key="preamble"
55
+ type="module"
56
+ dangerouslySetInnerHTML={{ __html: asset.content }}
57
+ />
58
+ );
59
+ }
60
+ return null;
61
+ })}
62
+
63
+ {/* Asset Serialization Script */}
64
+ <script
65
+ id="vista-assets"
66
+ dangerouslySetInnerHTML={{
67
+ __html: `window.__VISTA_ASSETS__ = ${serializedAssets};`
68
+ }}
69
+ />
70
+ </>
71
+ );
72
+ }
73
+
74
+ export default function App({
75
+ initialComponent,
76
+ initialRootLayout,
77
+ metadata,
78
+ assets
79
+ }: {
80
+ initialComponent?: React.ComponentType<any>,
81
+ initialRootLayout?: React.ComponentType<any>,
82
+ metadata?: any,
83
+ assets?: VistaAsset[]
84
+ }) {
85
+ const { path } = useRouter();
86
+ const pageKey = getPageKey(path);
87
+
88
+ if (!pages[pageKey]) {
89
+ return <div>404 Not Found: {pageKey}</div>;
90
+ }
91
+
92
+ // --- Page Logic ---
93
+ // --- Page Logic ---
94
+ const isInitialRender = React.useRef(!!initialComponent);
95
+ const [useInitial, setUseInitial] = React.useState(!!initialComponent);
96
+ const [initialPath] = React.useState(path); // FIX: Hook must be at top level
97
+
98
+ React.useEffect(() => {
99
+ // Switch to dynamic component (HMR-capable) after hydration
100
+ if (useInitial) {
101
+ setUseInitial(false);
102
+ isInitialRender.current = false;
103
+ }
104
+ }, []);
105
+
106
+ let PageComponent: React.ComponentType<any>;
107
+
108
+ // Use initial component only if we are on the same path and fresh from hydration
109
+ if (useInitial && initialComponent && path === initialPath) {
110
+ PageComponent = initialComponent;
111
+ } else {
112
+ // Eager: Direct access
113
+ PageComponent = (pages[pageKey] as any).default;
114
+ }
115
+
116
+ // --- Layout Logic ---
117
+ let RootLayoutComponent: any = initialRootLayout;
118
+
119
+ if (!RootLayoutComponent) {
120
+ const rootLayoutKey = '/app/layout.tsx';
121
+ if (rootLayout[rootLayoutKey]) {
122
+ // Eager
123
+ RootLayoutComponent = (rootLayout[rootLayoutKey] as any).default;
124
+ } else {
125
+ RootLayoutComponent = React.Fragment;
126
+ }
127
+ }
128
+
129
+ return (
130
+ /* @ts-ignore */
131
+ <RootLayoutComponent>
132
+ <VistaHead metadata={metadata} assets={assets} />
133
+ <Suspense fallback={null}>
134
+ <PageComponent key={path} />
135
+ </Suspense>
136
+ </RootLayoutComponent>
137
+ );
138
+ }
Binary file
@@ -0,0 +1,85 @@
1
+
2
+ import React, { AnchorHTMLAttributes } from 'react';
3
+ import { useRouter } from '../router';
4
+
5
+ /*
6
+ * Replicating Next.js Link Props Interface
7
+ * based on the provided link.d.ts
8
+ */
9
+
10
+ type Url = string | { href: string; query?: any; hash?: string }; // Simplified UrlObject
11
+
12
+ export interface LinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {
13
+ href: Url;
14
+ as?: Url;
15
+ replace?: boolean;
16
+ scroll?: boolean;
17
+ shallow?: boolean;
18
+ passHref?: boolean;
19
+ prefetch?: boolean | 'auto' | null;
20
+ locale?: string | false;
21
+ legacyBehavior?: boolean;
22
+ onNavigate?: () => void;
23
+ }
24
+
25
+ // Simple normalization for href (string or object)
26
+ const formatUrl = (url: Url): string => {
27
+ if (typeof url === 'string') return url;
28
+ if (typeof url === 'object' && url !== null) {
29
+ // Very basic support just for demo
30
+ return (url.href || '') + (url.hash || '');
31
+ }
32
+ return '';
33
+ };
34
+
35
+ export const Link = React.forwardRef<HTMLAnchorElement, LinkProps>(({
36
+ href,
37
+ as,
38
+ replace,
39
+ scroll,
40
+ shallow,
41
+ passHref,
42
+ prefetch,
43
+ legacyBehavior,
44
+ children,
45
+ onClick,
46
+ ...props
47
+ }, ref) => {
48
+ const { push, replace: replaceRoute } = useRouter();
49
+ const targetPath = formatUrl(as || href);
50
+
51
+ const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
52
+ if (onClick) onClick(e);
53
+
54
+ // Standard link behavior checks
55
+ if (e.defaultPrevented) return;
56
+ if (e.button !== 0) return; // ignore right clicks
57
+ if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return; // ignore open in new tab
58
+ if (!href) return;
59
+
60
+ e.preventDefault();
61
+
62
+ if (replace) {
63
+ replaceRoute(targetPath);
64
+ } else {
65
+ push(targetPath);
66
+ }
67
+ };
68
+
69
+ return (
70
+ <a
71
+ href={targetPath}
72
+ onClick={handleClick}
73
+ ref={ref}
74
+ {...props}
75
+ >
76
+ {children}
77
+ </a>
78
+ );
79
+ });
80
+
81
+ export const useLinkStatus = () => {
82
+ return { pending: false }; // Mock implementation
83
+ };
84
+
85
+ export default Link;
package/src/client.tsx ADDED
@@ -0,0 +1,368 @@
1
+
2
+ import React, { useState, useEffect, ErrorInfo } from 'react';
3
+ import ReactDOM from 'react-dom/client';
4
+ import { RouterProvider, useRouter } from './router.js';
5
+ import { ErrorOverlay } from './ErrorOverlay';
6
+
7
+ // --- Error Management ---
8
+
9
+ function VistaErrorManager({ children, initialError }: { children: React.ReactNode, initialError?: any }) {
10
+ const [error, setError] = useState<any>(initialError || null);
11
+
12
+ useEffect(() => {
13
+ // 1. Listen for Vite HMR/Build Errors
14
+ if (import.meta.hot) {
15
+ import.meta.hot.on('vite:error', (payloade: any) => {
16
+ // Vite sends an object like { err: { message, stack, plugin, id, frame, loc } }
17
+ // OR sometimes just the error object directly depending on version/context.
18
+ console.log('[Vista] Received vite:error payload:', payloade);
19
+
20
+ const err = payloade.err || payloade;
21
+
22
+ setError({
23
+ message: err.message,
24
+ stack: err.stack,
25
+ id: err.id, // Vite uses 'id' for filename often
26
+ frame: err.frame,
27
+ plugin: err.plugin,
28
+ loc: err.loc,
29
+ type: 'build',
30
+ // Fallbacks for Overlay keys
31
+ filename: err.id || err.file || 'Build Error',
32
+ });
33
+ });
34
+ import.meta.hot.on('vite:beforeUpdate', () => {
35
+ setError(null); // Clear error on new update
36
+ });
37
+ }
38
+
39
+ // 2. Listen for Unhandled Promise Rejections
40
+ const handleRejection = (event: PromiseRejectionEvent) => {
41
+ // Ignore dynamic import errors if we likely have a build error
42
+ if (event.reason?.message?.includes('Failed to fetch dynamically imported module')) {
43
+ console.log('[Vista] Ignoring dynamic import error (likely causing a build error)');
44
+ return;
45
+ }
46
+
47
+ setError((prev: any) => {
48
+ // specific check: don't overwrite build errors with runtime noise
49
+ if (prev && prev.type === 'build') return prev;
50
+
51
+ return {
52
+ message: event.reason?.message || 'Unhandled Promise Rejection',
53
+ stack: event.reason?.stack || '',
54
+ filename: 'Runtime Error',
55
+ loc: { line: 0, column: 0 },
56
+ type: 'runtime'
57
+ };
58
+ });
59
+ };
60
+
61
+ // 3. Listen for Global Errors
62
+ const handleError = (event: ErrorEvent) => {
63
+ setError((prev: any) => {
64
+ if (prev && prev.type === 'build') return prev;
65
+
66
+ return {
67
+ message: event.error?.message || event.message || 'Unknown Error',
68
+ stack: event.error?.stack || '',
69
+ filename: 'Runtime Error',
70
+ loc: { line: event.lineno || 0, column: event.colno || 0 },
71
+ type: 'runtime'
72
+ };
73
+ });
74
+ };
75
+
76
+ window.addEventListener('unhandledrejection', handleRejection);
77
+ window.addEventListener('error', handleError);
78
+
79
+ return () => {
80
+ window.removeEventListener('unhandledrejection', handleRejection);
81
+ window.removeEventListener('error', handleError);
82
+ };
83
+ }, []);
84
+
85
+ const handleBoundaryError = (err: any) => {
86
+ // Logic to ignore cascade errors
87
+ if (error && error.type === 'build') return;
88
+ if (err.message && err.message.includes('Failed to fetch dynamically imported module')) return;
89
+
90
+ setError({
91
+ message: err.message,
92
+ stack: err.stack,
93
+ loc: { line: 0, column: 0 },
94
+ filename: 'Runtime Error',
95
+ type: 'runtime'
96
+ });
97
+ };
98
+
99
+ if (error) {
100
+ return <ErrorOverlay error={error} onClose={() => setError(null)} />;
101
+ }
102
+
103
+ return (
104
+ <ErrorBoundary onError={handleBoundaryError}>
105
+ {children}
106
+ </ErrorBoundary>
107
+ );
108
+ }
109
+
110
+ // ... existing ErrorBoundary ...
111
+
112
+ // ... existing AppContent ...
113
+
114
+ // ... existing App ...
115
+
116
+ // Export a mount function that accepts the glob results
117
+ export function mount(modules: Record<string, any>) {
118
+ console.log('[Vista] Mount called with modules:', Object.keys(modules));
119
+ const root = document.getElementById('root');
120
+ if (root) {
121
+ console.log('[Vista] Root element found, rendering...');
122
+ ReactDOM.createRoot(root).render(
123
+ <VistaErrorManager>
124
+ <App modules={modules} />
125
+ </VistaErrorManager>
126
+ );
127
+ } else {
128
+ console.error('[Vista] FATAL: Root element #root not found in DOM');
129
+ }
130
+ }
131
+
132
+ // Export a mountError function for when module loading fails
133
+ export function mountError(error: any) {
134
+ const root = document.getElementById('root');
135
+ if (root) {
136
+ // Mount just the ErrorManager to catch and display the Vite error
137
+ ReactDOM.createRoot(root).render(
138
+ <VistaErrorManager initialError={error}>
139
+ <div style={{ padding: '20px', fontFamily: 'sans-serif', color: '#666' }}>
140
+ {/* Placeholder content while waiting for error overlay */}
141
+ Initializing error overlay...
142
+ </div>
143
+ </VistaErrorManager>
144
+ );
145
+ }
146
+ }
147
+
148
+ class ErrorBoundary extends React.Component<{ children: React.ReactNode, onError: (error: any) => void }, { hasError: boolean }> {
149
+ constructor(props: any) {
150
+ super(props);
151
+ this.state = { hasError: false };
152
+ }
153
+
154
+ static getDerivedStateFromError(error: any) {
155
+ return { hasError: true };
156
+ }
157
+
158
+ componentDidCatch(error: any, errorInfo: ErrorInfo) {
159
+ this.props.onError({
160
+ message: error.message,
161
+ stack: error.stack,
162
+ loc: { line: 0, column: 0 },
163
+ filename: 'Runtime Error'
164
+ });
165
+ }
166
+
167
+ render() {
168
+ if (this.state.hasError) {
169
+ return null;
170
+ }
171
+ return this.props.children;
172
+ }
173
+ }
174
+
175
+ // --- App Logic ---
176
+
177
+ function AppContent({ modules }: { modules: Record<string, any> }) {
178
+ const { path: currentPath } = useRouter();
179
+
180
+ // 1. Find the matching page component
181
+ let PageComponent: any = () => <div className="p-10 text-xl">404 Not Found</div>;
182
+
183
+ // 2. Build the Layout Tree
184
+ // ... (Logic to build layoutPaths is same) ...
185
+
186
+ // Refactored Async Logic
187
+ const [componentTree, setComponentTree] = React.useState<React.ReactNode | null>(null);
188
+ const [pageError, setPageError] = React.useState<any>(null);
189
+
190
+ React.useEffect(() => {
191
+ let isMounted = true;
192
+ setPageError(null);
193
+
194
+ const loadRoute = async () => {
195
+ try {
196
+ // --- 1. Route Matching Engine ---
197
+ let pageLoader: (() => Promise<any>) | null = null;
198
+ let matchedRoutePath = '';
199
+ let routeParams: Record<string, string> = {};
200
+
201
+ // Normalize current path: remove trailing slash (unless root)
202
+ let normalizedPath = currentPath;
203
+ if (normalizedPath.length > 1 && normalizedPath.endsWith('/')) {
204
+ normalizedPath = normalizedPath.slice(0, -1);
205
+ }
206
+
207
+ // Helper: Convert file path to route regex
208
+ // e.g. "/app/users/[id]/index.tsx" -> "^/users/([^/]+)$"
209
+ // e.g. "/app/index.tsx" -> "^/$"
210
+ const createRouteMatcher = (filePath: string) => {
211
+ // Remove prefix: handle "/", "./", or no slash before "app"
212
+ let cleanPath = filePath
213
+ .replace(/^(\.?\/)?app\//, '') // Remove app/ prefix (robust)
214
+ .replace(/\/index\.tsx$/, '') // Remove /index.tsx (nested)
215
+ .replace(/^index\.tsx$/, '') // Remove index.tsx (root)
216
+ .replace(/\.tsx$/, ''); // Remove .tsx (if any)
217
+
218
+ if (cleanPath === '') cleanPath = '/';
219
+
220
+ // Split into segments
221
+ const segments = cleanPath.split('/').filter(Boolean);
222
+ const paramNames: string[] = [];
223
+
224
+ // Console log for debug
225
+ // console.log(`[Router Debug] File: ${filePath} -> Clean: ${cleanPath}`);
226
+
227
+ const pathPattern = segments.map(seg => {
228
+ if (seg.startsWith('[') && seg.endsWith(']')) {
229
+ const paramName = seg.slice(1, -1);
230
+ paramNames.push(paramName);
231
+ return '([^/]+)'; // Match any non-slash char
232
+ }
233
+ return seg; // Static segment
234
+ }).join('/');
235
+
236
+ // Generate Regex: Ensure it starts with ^/ and ends with $
237
+ // cleanPath modules like 'about' become '^/about$'
238
+ // root '' becomes '^/$'
239
+ const regexString = pathPattern ? `^/${pathPattern}$` : '^/$';
240
+
241
+ return {
242
+ regex: new RegExp(regexString),
243
+ paramNames,
244
+ originalPath: cleanPath
245
+ };
246
+ };
247
+
248
+ // Iterate all modules to find a match
249
+ // Note: In a real framework, you'd cache the route table.
250
+ // console.log('[Vista Router] Current Path (Normalized):', normalizedPath);
251
+ // console.log('[Vista Router] Modules Found:', Object.keys(modules));
252
+
253
+ for (const path in modules) {
254
+ // Only care about pages (index.tsx)
255
+ if (!path.endsWith('/index.tsx')) continue;
256
+
257
+ const { regex, paramNames } = createRouteMatcher(path);
258
+ const match = normalizedPath.match(regex);
259
+
260
+ // console.log(`[Vista Router] Checking ${path} -> Regex: ${regex} -> Match: ${match ? 'YES' : 'NO'}`);
261
+
262
+ if (match) {
263
+ pageLoader = modules[path];
264
+ matchedRoutePath = path;
265
+
266
+ // Extract params
267
+ match.slice(1).forEach((val, index) => {
268
+ routeParams[paramNames[index]] = decodeURIComponent(val);
269
+ });
270
+ break; // Found first match (TODO: strict sorting Order)
271
+ }
272
+ }
273
+
274
+ if (!pageLoader) {
275
+ if (isMounted) setComponentTree(<div className="p-10 text-xl">404 Not Found</div>);
276
+ return;
277
+ }
278
+
279
+ // --- 2. Load Page & Layouts ---
280
+ const pageModule = await pageLoader();
281
+ let PageComponent = pageModule.default;
282
+
283
+ // Load Layouts: Climb up the directory tree from the page path
284
+ // e.g. /app/users/[id]/index.tsx
285
+ // Check: /app/users/[id]/layout.tsx
286
+ // Check: /app/users/layout.tsx
287
+ // Check: /app/layout.tsx
288
+
289
+ const layoutLoaders: Promise<any>[] = [];
290
+ const dirSegments = matchedRoutePath.split('/').slice(0, -1); // remove index.tsx
291
+
292
+ // Construct potential layout paths from root down to leaf
293
+ // But we want to load them all.
294
+ // Actually, let's just create the list of all parent directories and check for layout.tsx
295
+
296
+ let currentDir = ''; // starts empty
297
+ const possibleLayouts: string[] = [];
298
+
299
+ // We know everything starts with /app
300
+ // dirSegments looks like ['', 'app', 'users', '[id]']
301
+
302
+ for (const segment of dirSegments) {
303
+ if (!segment) continue;
304
+ currentDir += `/${segment}`;
305
+ const layoutPath = `${currentDir}/layout.tsx`;
306
+ if (modules[layoutPath]) {
307
+ possibleLayouts.push(layoutPath);
308
+ }
309
+ }
310
+
311
+ // Execute layout loaders
312
+ const layoutModules = await Promise.all(
313
+ possibleLayouts.map(path => modules[path]())
314
+ );
315
+
316
+ if (!isMounted) return;
317
+
318
+ // Handle Metadata
319
+ let meta = pageModule.metadata || {};
320
+ layoutModules.forEach(mod => {
321
+ if (mod.metadata) meta = { ...meta, ...mod.metadata };
322
+ });
323
+ if (meta.title) document.title = meta.title;
324
+
325
+ // Build Tree: Wrap Page with Layouts (Inner -> Outer? NO. Outer wraps Inner.)
326
+ // layoutModules is [AppLayout, UsersLayout, UserIdLayout]
327
+ // We want <AppLayout><UsersLayout><UserIdLayout><Page /></...></...></...>
328
+
329
+ let Content = <PageComponent params={routeParams} />; // Pass params to page!
330
+
331
+ // Reverse iterate to wrap from inside out
332
+ for (let i = layoutModules.length - 1; i >= 0; i--) {
333
+ const LayoutComponent = layoutModules[i].default;
334
+ Content = <LayoutComponent params={routeParams}>{Content}</LayoutComponent>;
335
+ }
336
+
337
+ setComponentTree(Content);
338
+
339
+ } catch (err) {
340
+ console.error('[Vista] Failed to load route:', err);
341
+ if (isMounted) setPageError(err);
342
+ }
343
+ };
344
+
345
+ loadRoute();
346
+
347
+ return () => { isMounted = false; };
348
+ }, [currentPath, modules]);
349
+
350
+ if (pageError) {
351
+ // Propagate to ErrorManager
352
+ throw pageError;
353
+ }
354
+
355
+ return componentTree;
356
+ }
357
+
358
+ function App({ modules }: { modules: Record<string, any> }) {
359
+ return (
360
+ <React.StrictMode>
361
+ <RouterProvider>
362
+ <AppContent modules={modules} />
363
+ </RouterProvider>
364
+ </React.StrictMode>
365
+ );
366
+ }
367
+
368
+
@@ -0,0 +1,70 @@
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom/client';
3
+ import { RouterProvider } from './router';
4
+ import App from './app.js';
5
+
6
+ import { showErrorOverlay } from './error-overlay';
7
+ import { loadRoute, loadRootLayout } from './router-loader.js';
8
+
9
+ export async function mount() {
10
+ // For Full Document Hydration (RootLayout provides html/body)
11
+ // we hydrate 'document' directly.
12
+ const root = document;
13
+
14
+ let initialComponent: React.ComponentType<any> | undefined;
15
+ let initialRootLayout: React.ComponentType<any> | undefined;
16
+ let initialMetadata = {};
17
+
18
+ try {
19
+ const [component, layoutResult] = await Promise.all([
20
+ loadRoute(window.location.pathname),
21
+ loadRootLayout() as Promise<any>
22
+ ]);
23
+ initialComponent = component || undefined;
24
+ initialRootLayout = layoutResult?.Component || undefined;
25
+ initialMetadata = layoutResult?.metadata || {};
26
+ } catch (e) {
27
+ // We hydrate the full document because we are doing full SSR with HTML shell.
28
+ }
29
+ // @ts-ignore
30
+ ReactDOM.hydrateRoot(window.document,
31
+ // <React.StrictMode>
32
+ <RouterProvider>
33
+ {/* @ts-ignore */}
34
+ <App
35
+ initialComponent={initialComponent}
36
+ initialRootLayout={initialRootLayout}
37
+ metadata={initialMetadata}
38
+ assets={(window as any).__VISTA_ASSETS__ || []}
39
+ />
40
+ </RouterProvider>,
41
+ // </React.StrictMode>
42
+ {
43
+ onRecoverableError: (error) => {
44
+ console.error('Hydration failed:', error);
45
+ // Only show overlay if it's a real error, not just a cancelled transition
46
+ // showErrorOverlay(error as Error, 'Hydration Mismatch');
47
+ }
48
+ }
49
+ );
50
+ }
51
+
52
+ // Auto-mount if in browser environment and not already mounted
53
+ if (typeof window !== 'undefined') {
54
+ window.addEventListener('error', (event) => {
55
+ console.error('Global Error Caught:', event.error);
56
+ showErrorOverlay(event.error, 'Runtime Error');
57
+ });
58
+
59
+ // Wrapped in async IIFE to handle await
60
+ (async () => {
61
+ try {
62
+ console.log('Mounting Vista App...');
63
+ await mount();
64
+ } catch (e) {
65
+ console.error('Mounting failed:', e);
66
+ showErrorOverlay(e as Error, 'Mount Error');
67
+ }
68
+ })();
69
+ }
70
+