@vistagenic/vista 0.1.0-alpha.1

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 (100) hide show
  1. package/bin/vista.js +98 -0
  2. package/dist/auth/index.d.ts +8 -0
  3. package/dist/auth/index.js +16 -0
  4. package/dist/bin/build-rsc.d.ts +17 -0
  5. package/dist/bin/build-rsc.js +320 -0
  6. package/dist/bin/build.d.ts +4 -0
  7. package/dist/bin/build.js +336 -0
  8. package/dist/bin/file-scanner.d.ts +66 -0
  9. package/dist/bin/file-scanner.js +399 -0
  10. package/dist/bin/server-component-plugin.d.ts +17 -0
  11. package/dist/bin/server-component-plugin.js +133 -0
  12. package/dist/bin/webpack.config.d.ts +6 -0
  13. package/dist/bin/webpack.config.js +138 -0
  14. package/dist/build/manifest.d.ts +95 -0
  15. package/dist/build/manifest.js +168 -0
  16. package/dist/build/rsc/client-manifest.d.ts +48 -0
  17. package/dist/build/rsc/client-manifest.js +191 -0
  18. package/dist/build/rsc/client-reference-plugin.d.ts +37 -0
  19. package/dist/build/rsc/client-reference-plugin.js +185 -0
  20. package/dist/build/rsc/compiler.d.ts +36 -0
  21. package/dist/build/rsc/compiler.js +311 -0
  22. package/dist/build/rsc/index.d.ts +16 -0
  23. package/dist/build/rsc/index.js +32 -0
  24. package/dist/build/rsc/native-scanner.d.ts +123 -0
  25. package/dist/build/rsc/native-scanner.js +165 -0
  26. package/dist/build/rsc/rsc-renderer.d.ts +99 -0
  27. package/dist/build/rsc/rsc-renderer.js +269 -0
  28. package/dist/build/rsc/server-component-loader.d.ts +19 -0
  29. package/dist/build/rsc/server-component-loader.js +147 -0
  30. package/dist/build/rsc/server-manifest.d.ts +63 -0
  31. package/dist/build/rsc/server-manifest.js +268 -0
  32. package/dist/build/webpack/loaders/vista-flight-loader.d.ts +17 -0
  33. package/dist/build/webpack/loaders/vista-flight-loader.js +93 -0
  34. package/dist/build/webpack/plugins/vista-flight-plugin.d.ts +36 -0
  35. package/dist/build/webpack/plugins/vista-flight-plugin.js +133 -0
  36. package/dist/client/dynamic.d.ts +25 -0
  37. package/dist/client/dynamic.js +68 -0
  38. package/dist/client/font.d.ts +98 -0
  39. package/dist/client/font.js +109 -0
  40. package/dist/client/head.d.ts +79 -0
  41. package/dist/client/head.js +261 -0
  42. package/dist/client/hydration.d.ts +45 -0
  43. package/dist/client/hydration.js +291 -0
  44. package/dist/client/link.d.ts +30 -0
  45. package/dist/client/link.js +188 -0
  46. package/dist/client/navigation.d.ts +28 -0
  47. package/dist/client/navigation.js +116 -0
  48. package/dist/client/router.d.ts +41 -0
  49. package/dist/client/router.js +190 -0
  50. package/dist/client/script.d.ts +51 -0
  51. package/dist/client/script.js +118 -0
  52. package/dist/components/client-island.d.ts +34 -0
  53. package/dist/components/client-island.js +75 -0
  54. package/dist/components/client.d.ts +29 -0
  55. package/dist/components/client.js +102 -0
  56. package/dist/components/index.d.ts +1 -0
  57. package/dist/components/index.js +8 -0
  58. package/dist/components/link.d.ts +6 -0
  59. package/dist/components/link.js +13 -0
  60. package/dist/config.d.ts +10 -0
  61. package/dist/config.js +31 -0
  62. package/dist/dev-error.d.ts +35 -0
  63. package/dist/dev-error.js +310 -0
  64. package/dist/image/get-img-props.d.ts +28 -0
  65. package/dist/image/get-img-props.js +49 -0
  66. package/dist/image/image-config.d.ts +20 -0
  67. package/dist/image/image-config.js +20 -0
  68. package/dist/image/image-loader.d.ts +7 -0
  69. package/dist/image/image-loader.js +14 -0
  70. package/dist/image/index.d.ts +6 -0
  71. package/dist/image/index.js +110 -0
  72. package/dist/image.d.ts +10 -0
  73. package/dist/image.js +7 -0
  74. package/dist/index.d.ts +20 -0
  75. package/dist/index.js +53 -0
  76. package/dist/metadata/generate.d.ts +22 -0
  77. package/dist/metadata/generate.js +324 -0
  78. package/dist/metadata/index.d.ts +7 -0
  79. package/dist/metadata/index.js +26 -0
  80. package/dist/metadata/types.d.ts +325 -0
  81. package/dist/metadata/types.js +15 -0
  82. package/dist/router/context.d.ts +8 -0
  83. package/dist/router/context.js +13 -0
  84. package/dist/router/index.d.ts +2 -0
  85. package/dist/router/index.js +18 -0
  86. package/dist/router/provider.d.ts +5 -0
  87. package/dist/router/provider.js +31 -0
  88. package/dist/server/client-boundary.d.ts +48 -0
  89. package/dist/server/client-boundary.js +133 -0
  90. package/dist/server/engine.d.ts +4 -0
  91. package/dist/server/engine.js +651 -0
  92. package/dist/server/index.d.ts +95 -0
  93. package/dist/server/index.js +177 -0
  94. package/dist/server/rsc-engine.d.ts +20 -0
  95. package/dist/server/rsc-engine.js +588 -0
  96. package/dist/server/rsc-module-system.d.ts +33 -0
  97. package/dist/server/rsc-module-system.js +119 -0
  98. package/dist/types/index.d.ts +4 -0
  99. package/dist/types/index.js +2 -0
  100. package/package.json +103 -0
@@ -0,0 +1,588 @@
1
+ "use strict";
2
+ /**
3
+ * Vista RSC Engine
4
+ *
5
+ * React Server Components aware rendering engine.
6
+ *
7
+ * This engine implements the "True RSC Architecture":
8
+ * 1. Server components render on the server only, contribute 0kb to client
9
+ * 2. Client components are sent as references, hydrated on demand
10
+ * 3. Strict separation ensures server secrets never leak
11
+ */
12
+ var __importDefault = (this && this.__importDefault) || function (mod) {
13
+ return (mod && mod.__esModule) ? mod : { "default": mod };
14
+ };
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.startRSCServer = startRSCServer;
17
+ exports.default = startRSCServer;
18
+ const express_1 = __importDefault(require("express"));
19
+ const fs_1 = __importDefault(require("fs"));
20
+ const path_1 = __importDefault(require("path"));
21
+ const server_1 = require("react-dom/server");
22
+ const react_1 = __importDefault(require("react"));
23
+ const webpack_dev_middleware_1 = __importDefault(require("webpack-dev-middleware"));
24
+ const webpack_hot_middleware_1 = __importDefault(require("webpack-hot-middleware"));
25
+ const config_1 = require("../config");
26
+ const dev_error_1 = require("../dev-error");
27
+ const rsc_renderer_1 = require("../build/rsc/rsc-renderer");
28
+ const rsc_module_system_1 = require("./rsc-module-system");
29
+ // Support CSS imports (ignore them on server)
30
+ require.extensions['.css'] = () => { };
31
+ // IMPORTANT: Initialize RSC module system BEFORE TypeScript compiler
32
+ // This must happen first so we can intercept client component requires
33
+ function earlyInitializeRSC(cwd) {
34
+ try {
35
+ const clientManifestPath = path_1.default.join(cwd, '.vista', 'client-manifest.json');
36
+ if (fs_1.default.existsSync(clientManifestPath)) {
37
+ const manifest = JSON.parse(fs_1.default.readFileSync(clientManifestPath, 'utf-8'));
38
+ (0, rsc_module_system_1.initializeRSCModuleSystem)(manifest);
39
+ return manifest;
40
+ }
41
+ }
42
+ catch (e) {
43
+ // Will initialize later
44
+ }
45
+ return null;
46
+ }
47
+ // Early init before TypeScript compilation
48
+ const earlyManifest = earlyInitializeRSC(process.cwd());
49
+ // Use SWC for faster server-side TypeScript compilation
50
+ try {
51
+ require('@swc-node/register');
52
+ console.log('[Vista RSC] Using SWC for server-side TypeScript compilation');
53
+ }
54
+ catch (e) {
55
+ try {
56
+ require('ts-node').register({
57
+ transpileOnly: true,
58
+ compilerOptions: {
59
+ module: 'commonjs',
60
+ jsx: 'react-jsx',
61
+ moduleResolution: 'node',
62
+ esModuleInterop: true,
63
+ },
64
+ });
65
+ console.log('[Vista RSC] Using ts-node for server-side TypeScript compilation');
66
+ }
67
+ catch (e2) {
68
+ console.warn('[Vista RSC] No TypeScript compiler found for SSR.');
69
+ }
70
+ }
71
+ /**
72
+ * Start the RSC-aware Vista server
73
+ */
74
+ function startRSCServer(options = {}) {
75
+ const app = (0, express_1.default)();
76
+ const cwd = process.cwd();
77
+ const vistaConfig = (0, config_1.loadConfig)(cwd);
78
+ const isDev = process.env.NODE_ENV !== 'production';
79
+ const port = options.port || vistaConfig.server?.port || 3003;
80
+ console.log('[Vista RSC] Starting React Server Components Engine...');
81
+ console.log('[Vista RSC] App Directory:', cwd);
82
+ // Initialize RSC renderer
83
+ let rscRenderer = null;
84
+ let clientManifest = null;
85
+ let serverManifest = null;
86
+ function loadManifests() {
87
+ try {
88
+ const clientManifestPath = path_1.default.join(cwd, '.vista', 'client-manifest.json');
89
+ const serverManifestPath = path_1.default.join(cwd, '.vista', 'server', 'server-manifest.json');
90
+ if (fs_1.default.existsSync(clientManifestPath)) {
91
+ clientManifest = JSON.parse(fs_1.default.readFileSync(clientManifestPath, 'utf-8'));
92
+ }
93
+ if (fs_1.default.existsSync(serverManifestPath)) {
94
+ serverManifest = JSON.parse(fs_1.default.readFileSync(serverManifestPath, 'utf-8'));
95
+ }
96
+ if (clientManifest && serverManifest) {
97
+ // Initialize RSC module system to intercept client component requires
98
+ (0, rsc_module_system_1.initializeRSCModuleSystem)(clientManifest);
99
+ rscRenderer = new rsc_renderer_1.RSCRenderer({
100
+ clientManifest,
101
+ serverManifest,
102
+ cwd,
103
+ });
104
+ console.log('[Vista RSC] Manifests loaded successfully');
105
+ console.log('[Vista RSC] Module interception enabled for client components');
106
+ }
107
+ }
108
+ catch (e) {
109
+ console.warn('[Vista RSC] Could not load manifests:', e);
110
+ }
111
+ }
112
+ loadManifests();
113
+ // SSE clients for live reload and error overlay
114
+ const sseClients = new Set();
115
+ // Webpack Dev + Hot Middleware (only in dev mode)
116
+ if (isDev && options.compiler) {
117
+ console.log('[Vista RSC] Enabling Webpack HMR...');
118
+ app.use((0, webpack_dev_middleware_1.default)(options.compiler, {
119
+ publicPath: '/',
120
+ stats: 'minimal',
121
+ writeToDisk: (filePath) => filePath.endsWith('.css'),
122
+ }));
123
+ app.use((0, webpack_hot_middleware_1.default)(options.compiler, {
124
+ log: console.log,
125
+ path: '/__webpack_hmr',
126
+ heartbeat: 2000,
127
+ }));
128
+ // SSE endpoint for live reload and compile errors
129
+ app.get('/__vista_reload', (req, res) => {
130
+ res.setHeader('Content-Type', 'text/event-stream');
131
+ res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
132
+ res.setHeader('Connection', 'keep-alive');
133
+ res.flushHeaders();
134
+ sseClients.add(res);
135
+ res.write('data: connected\n\n');
136
+ req.on('close', () => {
137
+ sseClients.delete(res);
138
+ });
139
+ });
140
+ // Push compile errors to browser via SSE
141
+ const pushCompileError = (errorMessage) => {
142
+ const errorData = JSON.stringify({
143
+ type: 'error',
144
+ message: errorMessage,
145
+ });
146
+ sseClients.forEach((client) => {
147
+ client.write(`data: ${errorData}\n\n`);
148
+ });
149
+ };
150
+ // Push build success to browser (clears error overlay)
151
+ const pushBuildSuccess = () => {
152
+ const data = JSON.stringify({ type: 'ok' });
153
+ sseClients.forEach((client) => {
154
+ client.write(`data: ${data}\n\n`);
155
+ });
156
+ };
157
+ // Listen to webpack compilation - push errors or success to browser
158
+ options.compiler.hooks.done.tap('VistaRSCErrorOverlay', (stats) => {
159
+ if (stats.hasErrors()) {
160
+ const errors = stats.toJson().errors || [];
161
+ const errorMessage = errors.map((e) => (typeof e === 'string' ? e : e.message)).join('\n');
162
+ console.log('[Vista RSC] Build error detected, pushing to browser...');
163
+ pushCompileError(errorMessage);
164
+ }
165
+ else {
166
+ pushBuildSuccess();
167
+ }
168
+ });
169
+ // Reload manifests on rebuild
170
+ options.compiler.hooks.afterEmit.tap('VistaRSCEngine', () => {
171
+ loadManifests();
172
+ });
173
+ // Watch server files and trigger reload for server component changes
174
+ let debounceTimer = null;
175
+ const triggerReload = () => {
176
+ if (debounceTimer)
177
+ clearTimeout(debounceTimer);
178
+ debounceTimer = setTimeout(() => {
179
+ console.log('[Vista RSC] Server file changed, triggering reload...');
180
+ sseClients.forEach((client) => {
181
+ client.write('data: reload\n\n');
182
+ });
183
+ }, 100);
184
+ };
185
+ // Watch app directory for server component changes
186
+ const appDir = path_1.default.join(cwd, 'app');
187
+ fs_1.default.watch(appDir, { recursive: true }, (event, filename) => {
188
+ if (filename && (filename.endsWith('.tsx') || filename.endsWith('.ts'))) {
189
+ const filePath = path_1.default.join(appDir, filename);
190
+ try {
191
+ const content = fs_1.default.readFileSync(filePath, 'utf-8');
192
+ // Server files don't have 'client load' directive - trigger reload
193
+ if (!content.includes("'client load'") && !content.includes('"client load"')) {
194
+ triggerReload();
195
+ }
196
+ }
197
+ catch (e) {
198
+ // File might be deleted or being written
199
+ }
200
+ }
201
+ });
202
+ }
203
+ // Explicit CSS route - serve .vista/client.css as /styles.css
204
+ app.get('/styles.css', (req, res) => {
205
+ const cssPath = path_1.default.join(cwd, '.vista', 'client.css');
206
+ if (fs_1.default.existsSync(cssPath)) {
207
+ res.setHeader('Content-Type', 'text/css');
208
+ res.sendFile(cssPath);
209
+ }
210
+ else {
211
+ // Fallback to globals.css if client.css doesn't exist
212
+ const globalsPath = path_1.default.join(cwd, 'app', 'globals.css');
213
+ if (fs_1.default.existsSync(globalsPath)) {
214
+ res.setHeader('Content-Type', 'text/css');
215
+ res.sendFile(globalsPath);
216
+ }
217
+ else {
218
+ res.status(404).send('/* CSS not found */');
219
+ }
220
+ }
221
+ });
222
+ // Static file serving
223
+ app.use(express_1.default.static(path_1.default.join(cwd, 'public')));
224
+ app.use('/_vista/static', express_1.default.static(path_1.default.join(cwd, '.vista', 'static')));
225
+ app.use('/_vista', express_1.default.static(path_1.default.join(cwd, '.vista')));
226
+ app.use(express_1.default.static(path_1.default.join(cwd, '.vista')));
227
+ // RSC Payload endpoint (for client-side navigation)
228
+ app.get('/_rsc/*', async (req, res) => {
229
+ const pathname = req.path.replace(/^\/_rsc/, '') || '/';
230
+ if (!rscRenderer || !serverManifest) {
231
+ return res.status(500).json({ error: 'RSC not initialized' });
232
+ }
233
+ try {
234
+ const route = matchRoute(pathname, serverManifest.routes);
235
+ if (!route) {
236
+ return res.status(404).json({ error: 'Route not found' });
237
+ }
238
+ const params = extractParams(pathname, route);
239
+ const searchParams = Object.fromEntries(new URLSearchParams(req.query).entries());
240
+ const payload = await rscRenderer.render({
241
+ clientManifest: clientManifest,
242
+ serverManifest: serverManifest,
243
+ route,
244
+ params,
245
+ searchParams,
246
+ request: {
247
+ url: req.url,
248
+ method: req.method,
249
+ headers: req.headers,
250
+ },
251
+ });
252
+ res.json(payload);
253
+ }
254
+ catch (e) {
255
+ console.error('[Vista RSC] Error rendering RSC payload:', e);
256
+ res.status(500).json({ error: e.message });
257
+ }
258
+ });
259
+ // Main request handler
260
+ app.use(async (req, res, next) => {
261
+ // Skip static files and HMR
262
+ if (req.path.startsWith('/styles.css') ||
263
+ req.path.startsWith('/__webpack_hmr') ||
264
+ req.path.startsWith('/_vista')) {
265
+ return next();
266
+ }
267
+ // Handle API routes (unchanged)
268
+ if (req.path.startsWith('/api/')) {
269
+ return handleApiRoute(req, res, cwd, isDev);
270
+ }
271
+ // Render page with RSC
272
+ try {
273
+ await renderRSCPage(req, res, {
274
+ cwd,
275
+ isDev,
276
+ vistaConfig,
277
+ rscRenderer,
278
+ clientManifest,
279
+ serverManifest,
280
+ });
281
+ }
282
+ catch (err) {
283
+ console.error('[Vista RSC] Render error:', err);
284
+ if (isDev) {
285
+ const errorInfo = {
286
+ type: 'runtime',
287
+ message: err.message || 'Unknown Server Error',
288
+ stack: err.stack,
289
+ };
290
+ const errorHtml = (0, server_1.renderToString)(react_1.default.createElement(dev_error_1.ErrorOverlay, { errors: [errorInfo] }));
291
+ res
292
+ .status(500)
293
+ .send(`<!DOCTYPE html><html><body style="margin:0">${errorHtml}</body></html>`);
294
+ }
295
+ else {
296
+ res.status(500).send('<h1>Internal Server Error</h1>');
297
+ }
298
+ }
299
+ });
300
+ app.listen(port, () => {
301
+ console.log(`[Vista RSC] Server running at http://localhost:${port}`);
302
+ });
303
+ }
304
+ /**
305
+ * Match a pathname to a route
306
+ */
307
+ function matchRoute(pathname, routes) {
308
+ for (const route of routes) {
309
+ if (matchPattern(pathname, route.pattern)) {
310
+ return route;
311
+ }
312
+ }
313
+ return null;
314
+ }
315
+ /**
316
+ * Simple pattern matching for routes
317
+ */
318
+ function matchPattern(pathname, pattern) {
319
+ const patternParts = pattern.split('/').filter(Boolean);
320
+ const pathParts = pathname.split('/').filter(Boolean);
321
+ // Root route
322
+ if (patternParts.length === 0 && pathParts.length === 0) {
323
+ return true;
324
+ }
325
+ // Check each segment
326
+ for (let i = 0; i < patternParts.length; i++) {
327
+ const patternPart = patternParts[i];
328
+ const pathPart = pathParts[i];
329
+ // Catch-all
330
+ if (patternPart.endsWith('*')) {
331
+ return true;
332
+ }
333
+ // Dynamic segment
334
+ if (patternPart.startsWith(':')) {
335
+ if (!pathPart)
336
+ return false;
337
+ continue;
338
+ }
339
+ // Static segment
340
+ if (patternPart !== pathPart) {
341
+ return false;
342
+ }
343
+ }
344
+ return patternParts.length === pathParts.length;
345
+ }
346
+ /**
347
+ * Extract params from pathname based on route pattern
348
+ */
349
+ function extractParams(pathname, route) {
350
+ const params = {};
351
+ const patternParts = route.pattern.split('/').filter(Boolean);
352
+ const pathParts = pathname.split('/').filter(Boolean);
353
+ for (let i = 0; i < patternParts.length; i++) {
354
+ const patternPart = patternParts[i];
355
+ if (patternPart.startsWith(':')) {
356
+ const paramName = patternPart.slice(1).replace('*', '');
357
+ if (patternPart.endsWith('*')) {
358
+ // Catch-all: grab rest of path
359
+ params[paramName] = pathParts.slice(i).join('/');
360
+ }
361
+ else {
362
+ params[paramName] = pathParts[i] || '';
363
+ }
364
+ }
365
+ }
366
+ return params;
367
+ }
368
+ /**
369
+ * Handle API routes
370
+ */
371
+ async function handleApiRoute(req, res, cwd, isDev) {
372
+ const apiRoute = req.path.substring(5); // Remove '/api/'
373
+ const routeTsPath = path_1.default.resolve(cwd, 'app', 'api', apiRoute, 'route.ts');
374
+ const routeTsxPath = path_1.default.resolve(cwd, 'app', 'api', apiRoute, 'route.tsx');
375
+ const legacyPath = path_1.default.resolve(cwd, 'app', 'api', apiRoute + '.ts');
376
+ let apiPath = null;
377
+ if (fs_1.default.existsSync(routeTsPath))
378
+ apiPath = routeTsPath;
379
+ else if (fs_1.default.existsSync(routeTsxPath))
380
+ apiPath = routeTsxPath;
381
+ else if (fs_1.default.existsSync(legacyPath))
382
+ apiPath = legacyPath;
383
+ if (!apiPath) {
384
+ res.status(404).json({ error: 'API Route Not Found' });
385
+ return;
386
+ }
387
+ try {
388
+ if (isDev)
389
+ delete require.cache[require.resolve(apiPath)];
390
+ const apiModule = require(apiPath);
391
+ const method = req.method?.toUpperCase() || 'GET';
392
+ const methodHandler = apiModule[method];
393
+ if (typeof methodHandler === 'function') {
394
+ const request = {
395
+ url: req.protocol + '://' + req.get('host') + req.originalUrl,
396
+ method: req.method,
397
+ headers: new Map(Object.entries(req.headers)),
398
+ json: async () => req.body,
399
+ text: async () => JSON.stringify(req.body),
400
+ nextUrl: {
401
+ pathname: req.path,
402
+ searchParams: new URLSearchParams(req.query),
403
+ },
404
+ };
405
+ const result = await methodHandler(request, { params: {} });
406
+ if (result && typeof result.json === 'function') {
407
+ const json = await result.json();
408
+ res.status(result.status || 200).json(json);
409
+ }
410
+ else if (result) {
411
+ res.status(200).json(result);
412
+ }
413
+ else {
414
+ res.status(204).end();
415
+ }
416
+ }
417
+ else if (apiModule.default) {
418
+ apiModule.default(req, res);
419
+ }
420
+ else {
421
+ res.status(405).json({ error: `Method ${method} not allowed` });
422
+ }
423
+ }
424
+ catch (err) {
425
+ console.error('[Vista RSC] API Route Error:', err);
426
+ res.status(500).json({ error: 'Internal Server Error' });
427
+ }
428
+ }
429
+ /**
430
+ * Render a page using RSC
431
+ */
432
+ async function renderRSCPage(req, res, context) {
433
+ const { cwd, isDev, vistaConfig, rscRenderer, clientManifest, serverManifest } = context;
434
+ // If RSC is not initialized, fall back to legacy rendering
435
+ if (!rscRenderer || !serverManifest) {
436
+ return legacyRender(req, res, cwd, isDev, vistaConfig);
437
+ }
438
+ // Match the route
439
+ const route = matchRoute(req.path, serverManifest.routes);
440
+ if (!route) {
441
+ // Try not-found page
442
+ const notFoundPath = path_1.default.resolve(cwd, 'app', 'not-found.tsx');
443
+ if (fs_1.default.existsSync(notFoundPath)) {
444
+ // Render not-found page
445
+ res.status(404);
446
+ return legacyRender(req, res, cwd, isDev, vistaConfig, notFoundPath);
447
+ }
448
+ res.status(404).send('<h1>404 - Page Not Found</h1>');
449
+ return;
450
+ }
451
+ // Extract params and search params
452
+ const params = extractParams(req.path, route);
453
+ const searchParams = Object.fromEntries(new URLSearchParams(req.query).entries());
454
+ // Render using RSC renderer
455
+ const payload = await rscRenderer.render({
456
+ clientManifest: clientManifest,
457
+ serverManifest: serverManifest,
458
+ route,
459
+ params,
460
+ searchParams,
461
+ request: {
462
+ url: req.url,
463
+ method: req.method,
464
+ headers: req.headers,
465
+ },
466
+ });
467
+ // Generate the full HTML document
468
+ const html = generateFullHtml(payload, vistaConfig, isDev, cwd);
469
+ res.setHeader('Content-Type', 'text/html');
470
+ res.send(html);
471
+ }
472
+ /**
473
+ * Find all JS chunk files in the static directory
474
+ * Returns them sorted by size (smallest first = runtime chunks first)
475
+ */
476
+ function findAllChunkFiles(cwd) {
477
+ const chunksDir = path_1.default.join(cwd, '.vista', 'static', 'chunks');
478
+ const jsFiles = [];
479
+ if (!fs_1.default.existsSync(chunksDir)) {
480
+ return [];
481
+ }
482
+ try {
483
+ const entries = fs_1.default.readdirSync(chunksDir);
484
+ for (const entry of entries) {
485
+ if (entry.endsWith('.js') && !entry.endsWith('.map')) {
486
+ const stat = fs_1.default.statSync(path_1.default.join(chunksDir, entry));
487
+ jsFiles.push({ name: entry, size: stat.size });
488
+ }
489
+ }
490
+ }
491
+ catch (e) {
492
+ // Ignore
493
+ }
494
+ // Sort by size (smallest first - runtime/webpack chunks are usually smallest)
495
+ jsFiles.sort((a, b) => a.size - b.size);
496
+ return jsFiles.map((f) => f.name);
497
+ }
498
+ /**
499
+ * Generate full HTML document from RSC payload
500
+ */
501
+ function generateFullHtml(payload, config, isDev, cwd = process.cwd()) {
502
+ const hydrationScript = (0, rsc_renderer_1.generateHydrationScript)(payload);
503
+ const chunkFiles = findAllChunkFiles(cwd);
504
+ // Build script tags for all chunks
505
+ const scriptTags = chunkFiles.map((chunk) => `<script src="/_vista/static/chunks/${chunk}"></script>`);
506
+ return `<!DOCTYPE html>
507
+ <html lang="en">
508
+ <head>
509
+ <meta charset="UTF-8">
510
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
511
+ <link rel="preload" href="/styles.css" as="style">
512
+ <link rel="stylesheet" href="/styles.css">
513
+ <style>
514
+ /* Critical styles - prevents FOUC */
515
+ :root {
516
+ --background: #ffffff;
517
+ --foreground: #171717;
518
+ }
519
+ @media (prefers-color-scheme: dark) {
520
+ :root {
521
+ --background: #0a0a0a;
522
+ --foreground: #ededed;
523
+ }
524
+ }
525
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
526
+ html, body { height: 100%; }
527
+ body {
528
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
529
+ background: var(--background);
530
+ color: var(--foreground);
531
+ -webkit-font-smoothing: antialiased;
532
+ -moz-osx-font-smoothing: grayscale;
533
+ }
534
+ #root { min-height: 100%; }
535
+ /* Tailwind critical classes */
536
+ .flex { display: flex; }
537
+ .min-h-screen { min-height: 100vh; }
538
+ .flex-col { flex-direction: column; }
539
+ .items-center { align-items: center; }
540
+ .justify-center { justify-content: center; }
541
+ .text-center { text-align: center; }
542
+ .text-3xl { font-size: 1.875rem; line-height: 2.25rem; }
543
+ .font-semibold { font-weight: 600; }
544
+ .relative { position: relative; }
545
+ .absolute { position: absolute; }
546
+ .border { border-width: 1px; }
547
+ .border-dashed { border-style: dashed; }
548
+ .rounded-full { border-radius: 9999px; }
549
+ .p-10 { padding: 2.5rem; }
550
+ .mb-10 { margin-bottom: 2.5rem; }
551
+ .-mt-20 { margin-top: -5rem; }
552
+ .max-w-xs { max-width: 20rem; }
553
+ .tracking-tight { letter-spacing: -0.025em; }
554
+ .leading-10 { line-height: 2.5rem; }
555
+ .bg-white { background-color: #fff; }
556
+ .bg-black { background-color: #000; }
557
+ .text-black { color: #000; }
558
+ .border-gray-300 { border-color: #d1d5db; }
559
+ .transition-colors { transition-property: color, background-color, border-color; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-duration: 150ms; }
560
+ .duration-200 { transition-duration: 200ms; }
561
+ @media (prefers-color-scheme: dark) {
562
+ .dark\\:bg-black { background-color: #000; }
563
+ .dark\\:text-zinc-50 { color: #fafafa; }
564
+ .dark\\:border-neutral-700 { border-color: #404040; }
565
+ .dark\\:invert { filter: invert(1); }
566
+ }
567
+ @media (min-width: 640px) {
568
+ .sm\\:max-w-none { max-width: none; }
569
+ }
570
+ [data-vista-cc] { display: contents; }
571
+ </style>
572
+ </head>
573
+ <body>
574
+ <div id="root">${payload.html}</div>
575
+ ${hydrationScript}
576
+ ${scriptTags.join('\n ')}
577
+ </body>
578
+ </html>`;
579
+ }
580
+ /**
581
+ * Legacy render for fallback or when RSC is not available
582
+ */
583
+ function legacyRender(req, res, cwd, isDev, config, customPage) {
584
+ // Import the original engine for legacy rendering
585
+ const { startServer } = require('./engine');
586
+ // This is a simplified fallback - in practice, you'd handle this better
587
+ res.status(500).send('RSC not initialized. Please run build first.');
588
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Vista RSC Module System
3
+ *
4
+ * Intercepts require() calls for client components during SSR.
5
+ * Client components are replaced with placeholder divs that will be
6
+ * hydrated on the client side.
7
+ */
8
+ export interface ClientReference {
9
+ id: string;
10
+ mountId: string;
11
+ props: Record<string, any>;
12
+ chunkUrl: string;
13
+ exportName: string;
14
+ }
15
+ /**
16
+ * Initialize the RSC module system
17
+ * Must be called BEFORE any app modules are loaded
18
+ */
19
+ export declare function initializeRSCModuleSystem(manifest: any, cwd?: string): void;
20
+ export declare function _getMountId(): number;
21
+ export declare function _addReference(ref: ClientReference): void;
22
+ /**
23
+ * Reset state for new render
24
+ */
25
+ export declare function resetRSCState(): void;
26
+ /**
27
+ * Get collected client references
28
+ */
29
+ export declare function getClientReferences(): ClientReference[];
30
+ /**
31
+ * Shutdown the RSC module system
32
+ */
33
+ export declare function shutdownRSCModuleSystem(): void;