@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,123 @@
1
+ /**
2
+ * Vista RSC Native Scanner
3
+ *
4
+ * Uses Rust-powered native bindings for blazing fast component scanning.
5
+ * Falls back to TypeScript implementation if native module unavailable.
6
+ */
7
+ export interface NapiScannedComponent {
8
+ absolutePath: string;
9
+ relativePath: string;
10
+ isClient: boolean;
11
+ directiveLine: number;
12
+ componentType: string;
13
+ exports: string[];
14
+ clientHooksUsed: string[];
15
+ hasMetadata: boolean;
16
+ hasGenerateMetadata: boolean;
17
+ }
18
+ export interface NapiServerComponentError {
19
+ file: string;
20
+ message: string;
21
+ hooks: string[];
22
+ }
23
+ export interface NapiScanResult {
24
+ clientComponents: NapiScannedComponent[];
25
+ serverComponents: NapiScannedComponent[];
26
+ pages: NapiScannedComponent[];
27
+ layouts: NapiScannedComponent[];
28
+ apiRoutes: NapiScannedComponent[];
29
+ errors: NapiServerComponentError[];
30
+ totalFiles: number;
31
+ scanTimeMs: number;
32
+ }
33
+ export interface NapiClientModuleEntry {
34
+ id: string;
35
+ path: string;
36
+ absolutePath: string;
37
+ chunkName: string;
38
+ exports: string[];
39
+ asyncLoad: boolean;
40
+ }
41
+ export interface NapiClientManifest {
42
+ buildId: string;
43
+ clientModules: NapiClientModuleEntry[];
44
+ }
45
+ export interface NapiRouteEntry {
46
+ pattern: string;
47
+ pagePath: string;
48
+ layoutPaths: string[];
49
+ loadingPath: string | null;
50
+ errorPath: string | null;
51
+ routeType: string;
52
+ }
53
+ export interface NapiServerModuleEntry {
54
+ id: string;
55
+ path: string;
56
+ absolutePath: string;
57
+ componentType: string;
58
+ hasMetadata: boolean;
59
+ hasGenerateMetadata: boolean;
60
+ }
61
+ export interface NapiServerManifest {
62
+ buildId: string;
63
+ serverModules: NapiServerModuleEntry[];
64
+ routes: NapiRouteEntry[];
65
+ }
66
+ /**
67
+ * Check if native module is available
68
+ */
69
+ export declare function isNativeAvailable(): boolean;
70
+ /**
71
+ * Scan app directory using Rust native code
72
+ * Returns null if native module is not available
73
+ */
74
+ export declare function scanAppNative(appDir: string): NapiScanResult | null;
75
+ /**
76
+ * Generate client manifest using Rust native code
77
+ */
78
+ export declare function generateClientManifestNative(appDir: string, buildId: string): NapiClientManifest | null;
79
+ /**
80
+ * Generate server manifest using Rust native code
81
+ */
82
+ export declare function generateServerManifestNative(appDir: string, buildId: string): NapiServerManifest | null;
83
+ /**
84
+ * Generate unique mount ID for client component
85
+ */
86
+ export declare function generateMountIdNative(): string | null;
87
+ /**
88
+ * Reset mount ID counter (call at start of each request)
89
+ */
90
+ export declare function resetMountCounterNative(): boolean;
91
+ /**
92
+ * Convert NAPI scan result to internal format
93
+ */
94
+ export declare function convertScanResult(result: NapiScanResult): {
95
+ clientComponents: {
96
+ absolutePath: string;
97
+ relativePath: string;
98
+ isClient: boolean;
99
+ directiveLine: number;
100
+ componentType: string;
101
+ exports: string[];
102
+ clientHooksUsed: string[];
103
+ hasMetadata: boolean;
104
+ hasGenerateMetadata: boolean;
105
+ }[];
106
+ serverComponents: {
107
+ absolutePath: string;
108
+ relativePath: string;
109
+ isClient: boolean;
110
+ directiveLine: number;
111
+ componentType: string;
112
+ exports: string[];
113
+ clientHooksUsed: any[];
114
+ hasMetadata: boolean;
115
+ hasGenerateMetadata: boolean;
116
+ }[];
117
+ pages: NapiScannedComponent[];
118
+ layouts: NapiScannedComponent[];
119
+ apiRoutes: NapiScannedComponent[];
120
+ errors: NapiServerComponentError[];
121
+ totalFiles: number;
122
+ scanTimeMs: number;
123
+ };
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ /**
3
+ * Vista RSC Native Scanner
4
+ *
5
+ * Uses Rust-powered native bindings for blazing fast component scanning.
6
+ * Falls back to TypeScript implementation if native module unavailable.
7
+ */
8
+ var __importDefault = (this && this.__importDefault) || function (mod) {
9
+ return (mod && mod.__esModule) ? mod : { "default": mod };
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.isNativeAvailable = isNativeAvailable;
13
+ exports.scanAppNative = scanAppNative;
14
+ exports.generateClientManifestNative = generateClientManifestNative;
15
+ exports.generateServerManifestNative = generateServerManifestNative;
16
+ exports.generateMountIdNative = generateMountIdNative;
17
+ exports.resetMountCounterNative = resetMountCounterNative;
18
+ exports.convertScanResult = convertScanResult;
19
+ const path_1 = __importDefault(require("path"));
20
+ let nativeModule = null;
21
+ let nativeLoadError = null;
22
+ /**
23
+ * Try to load the native Rust module
24
+ */
25
+ function loadNativeModule() {
26
+ if (nativeModule !== null)
27
+ return nativeModule;
28
+ if (nativeLoadError !== null)
29
+ return null;
30
+ // Try multiple paths since we might be running from src or dist
31
+ const possiblePaths = [
32
+ // From compiled dist/build/rsc/native-scanner.js
33
+ path_1.default.resolve(__dirname, '../../../../../crates/vista-napi'),
34
+ // From source src/build/rsc/native-scanner.ts
35
+ path_1.default.resolve(__dirname, '../../../../crates/vista-napi'),
36
+ // From workspace root
37
+ path_1.default.resolve(process.cwd(), '../crates/vista-napi'),
38
+ // Try @aspect-build/vista-napi as npm package
39
+ '@aspect-build/vista-napi',
40
+ ];
41
+ for (const modulePath of possiblePaths) {
42
+ try {
43
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
44
+ nativeModule = require(modulePath);
45
+ console.log('[Vista RSC] Using Rust-powered scanner');
46
+ return nativeModule;
47
+ }
48
+ catch {
49
+ // Continue trying other paths
50
+ }
51
+ }
52
+ nativeLoadError = new Error('Native module not found');
53
+ console.warn('⚠ Native module unavailable, using TypeScript fallback');
54
+ return null;
55
+ }
56
+ /**
57
+ * Check if native module is available
58
+ */
59
+ function isNativeAvailable() {
60
+ return loadNativeModule() !== null;
61
+ }
62
+ /**
63
+ * Scan app directory using Rust native code
64
+ * Returns null if native module is not available
65
+ */
66
+ function scanAppNative(appDir) {
67
+ const native = loadNativeModule();
68
+ if (!native)
69
+ return null;
70
+ try {
71
+ const startTime = performance.now();
72
+ const result = native.rscScanApp(appDir);
73
+ const scanTime = performance.now() - startTime;
74
+ console.log(`🦀 Native scan completed in ${scanTime.toFixed(2)}ms (${result.totalFiles} files)`);
75
+ return result;
76
+ }
77
+ catch (e) {
78
+ console.error('Native scan failed:', e);
79
+ return null;
80
+ }
81
+ }
82
+ /**
83
+ * Generate client manifest using Rust native code
84
+ */
85
+ function generateClientManifestNative(appDir, buildId) {
86
+ const native = loadNativeModule();
87
+ if (!native)
88
+ return null;
89
+ try {
90
+ return native.rscGenerateClientManifest(appDir, buildId);
91
+ }
92
+ catch (e) {
93
+ console.error('Native client manifest generation failed:', e);
94
+ return null;
95
+ }
96
+ }
97
+ /**
98
+ * Generate server manifest using Rust native code
99
+ */
100
+ function generateServerManifestNative(appDir, buildId) {
101
+ const native = loadNativeModule();
102
+ if (!native)
103
+ return null;
104
+ try {
105
+ return native.rscGenerateServerManifest(appDir, buildId);
106
+ }
107
+ catch (e) {
108
+ console.error('Native server manifest generation failed:', e);
109
+ return null;
110
+ }
111
+ }
112
+ /**
113
+ * Generate unique mount ID for client component
114
+ */
115
+ function generateMountIdNative() {
116
+ const native = loadNativeModule();
117
+ if (!native)
118
+ return null;
119
+ return native.rscGenerateMountId();
120
+ }
121
+ /**
122
+ * Reset mount ID counter (call at start of each request)
123
+ */
124
+ function resetMountCounterNative() {
125
+ const native = loadNativeModule();
126
+ if (!native)
127
+ return false;
128
+ native.rscResetMountCounter();
129
+ return true;
130
+ }
131
+ /**
132
+ * Convert NAPI scan result to internal format
133
+ */
134
+ function convertScanResult(result) {
135
+ return {
136
+ clientComponents: result.clientComponents.map((c) => ({
137
+ absolutePath: c.absolutePath,
138
+ relativePath: c.relativePath,
139
+ isClient: c.isClient,
140
+ directiveLine: c.directiveLine,
141
+ componentType: c.componentType,
142
+ exports: c.exports,
143
+ clientHooksUsed: c.clientHooksUsed,
144
+ hasMetadata: c.hasMetadata,
145
+ hasGenerateMetadata: c.hasGenerateMetadata,
146
+ })),
147
+ serverComponents: result.serverComponents.map((c) => ({
148
+ absolutePath: c.absolutePath,
149
+ relativePath: c.relativePath,
150
+ isClient: false,
151
+ directiveLine: 0,
152
+ componentType: c.componentType,
153
+ exports: c.exports,
154
+ clientHooksUsed: [],
155
+ hasMetadata: c.hasMetadata,
156
+ hasGenerateMetadata: c.hasGenerateMetadata,
157
+ })),
158
+ pages: result.pages,
159
+ layouts: result.layouts,
160
+ apiRoutes: result.apiRoutes,
161
+ errors: result.errors,
162
+ totalFiles: result.totalFiles,
163
+ scanTimeMs: result.scanTimeMs,
164
+ };
165
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Vista RSC Renderer
3
+ *
4
+ * Renders React Server Components to an RSC Payload format.
5
+ * This is the core of the "Zero Bundle Size Server Components" feature.
6
+ *
7
+ * RSC Payload Format:
8
+ * - Server components render to a special streaming format
9
+ * - Client component references are serialized as "holes" to be filled client-side
10
+ * - The client receives a stream of instructions + HTML
11
+ */
12
+ import type { ClientManifest } from './client-manifest';
13
+ import type { ServerManifest, RouteEntry } from './server-manifest';
14
+ export interface RSCPayload {
15
+ /** The rendered HTML for server components */
16
+ html: string;
17
+ /** Client component references to hydrate */
18
+ clientReferences: ClientReference[];
19
+ /** Data passed to client for hydration */
20
+ data: Record<string, any>;
21
+ /** Flight data for streaming (if enabled) */
22
+ flight?: string;
23
+ }
24
+ export interface ClientReference {
25
+ /** Unique ID matching client manifest */
26
+ id: string;
27
+ /** DOM element ID where component should mount */
28
+ mountId: string;
29
+ /** Props to pass to the client component */
30
+ props: Record<string, any>;
31
+ /** Chunk URL to load */
32
+ chunkUrl: string;
33
+ /** Export name to use (usually 'default') */
34
+ exportName: string;
35
+ }
36
+ export interface RSCRenderContext {
37
+ /** Client manifest for resolving client components */
38
+ clientManifest: ClientManifest;
39
+ /** Server manifest for component metadata */
40
+ serverManifest: ServerManifest;
41
+ /** Current route being rendered */
42
+ route: RouteEntry;
43
+ /** URL parameters */
44
+ params: Record<string, string>;
45
+ /** Search params */
46
+ searchParams: Record<string, string>;
47
+ /** Request metadata */
48
+ request: {
49
+ url: string;
50
+ method: string;
51
+ headers: Record<string, string>;
52
+ };
53
+ }
54
+ /**
55
+ * Reset mount ID counter (call at start of each request)
56
+ */
57
+ export declare function resetMountIdCounter(): void;
58
+ /**
59
+ * RSC Renderer class
60
+ * Handles rendering a route with proper server/client component separation
61
+ */
62
+ export declare class RSCRenderer {
63
+ private clientManifest;
64
+ private serverManifest;
65
+ private clientReferences;
66
+ private cwd;
67
+ constructor(options: {
68
+ clientManifest: ClientManifest;
69
+ serverManifest: ServerManifest;
70
+ cwd: string;
71
+ });
72
+ /**
73
+ * Render a route to RSC Payload
74
+ */
75
+ render(context: RSCRenderContext): Promise<RSCPayload>;
76
+ /**
77
+ * Render a route with nested layouts
78
+ */
79
+ private renderRoute;
80
+ /**
81
+ * Require a component with cache clearing in dev
82
+ */
83
+ private requireComponent;
84
+ /**
85
+ * Check if a component is a client component
86
+ */
87
+ private isClientComponent;
88
+ }
89
+ /**
90
+ * Create an RSC renderer instance
91
+ */
92
+ export declare function createRSCRenderer(cwd: string): RSCRenderer | null;
93
+ /**
94
+ * Generate the client-side hydration script
95
+ *
96
+ * This just sets the data - the actual hydration is handled by rsc-client.tsx
97
+ * which is bundled and has all client components pre-imported.
98
+ */
99
+ export declare function generateHydrationScript(payload: RSCPayload): string;
@@ -0,0 +1,269 @@
1
+ "use strict";
2
+ /**
3
+ * Vista RSC Renderer
4
+ *
5
+ * Renders React Server Components to an RSC Payload format.
6
+ * This is the core of the "Zero Bundle Size Server Components" feature.
7
+ *
8
+ * RSC Payload Format:
9
+ * - Server components render to a special streaming format
10
+ * - Client component references are serialized as "holes" to be filled client-side
11
+ * - The client receives a stream of instructions + HTML
12
+ */
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.RSCRenderer = void 0;
18
+ exports.resetMountIdCounter = resetMountIdCounter;
19
+ exports.createRSCRenderer = createRSCRenderer;
20
+ exports.generateHydrationScript = generateHydrationScript;
21
+ const react_1 = __importDefault(require("react"));
22
+ const server_1 = require("react-dom/server");
23
+ const path_1 = __importDefault(require("path"));
24
+ const fs_1 = __importDefault(require("fs"));
25
+ // Import RSC module system for client component interception
26
+ let rscModuleSystem = null;
27
+ try {
28
+ rscModuleSystem = require('../../server/rsc-module-system');
29
+ }
30
+ catch {
31
+ // Module system not available (during build)
32
+ }
33
+ // Counter for generating unique mount IDs
34
+ let mountIdCounter = 0;
35
+ /**
36
+ * Reset mount ID counter (call at start of each request)
37
+ */
38
+ function resetMountIdCounter() {
39
+ mountIdCounter = 0;
40
+ }
41
+ /**
42
+ * Generate a unique mount ID for a client component
43
+ */
44
+ function generateMountId() {
45
+ return `__vista_cc_${++mountIdCounter}`;
46
+ }
47
+ /**
48
+ * Check if a component module is a client component
49
+ */
50
+ function isClientComponent(modulePath, manifest) {
51
+ return modulePath in manifest.pathToId;
52
+ }
53
+ /**
54
+ * Create a client component placeholder
55
+ * This renders a marker div that will be hydrated on the client
56
+ */
57
+ function createClientComponentPlaceholder(entry, props, children) {
58
+ const mountId = generateMountId();
59
+ // Create the reference for hydration
60
+ const reference = {
61
+ id: entry.id,
62
+ mountId,
63
+ props: serializeProps(props),
64
+ chunkUrl: `/_vista/static/chunks/${entry.chunkName}.js`,
65
+ exportName: 'default',
66
+ };
67
+ // Create placeholder element
68
+ // Children are rendered server-side if they're server components
69
+ const element = react_1.default.createElement('div', {
70
+ 'data-vista-cc': entry.id,
71
+ 'data-vista-mount': mountId,
72
+ id: mountId,
73
+ suppressHydrationWarning: true,
74
+ }, children);
75
+ return { element, reference };
76
+ }
77
+ /**
78
+ * Serialize props for client-side hydration
79
+ * Handles Date, undefined, functions, etc.
80
+ */
81
+ function serializeProps(props) {
82
+ const serialized = {};
83
+ for (const [key, value] of Object.entries(props)) {
84
+ if (key === 'children') {
85
+ // Children are handled separately
86
+ continue;
87
+ }
88
+ if (typeof value === 'function') {
89
+ // Functions can't be serialized - skip or warn
90
+ console.warn(`[Vista RSC] Cannot serialize function prop "${key}" to client`);
91
+ continue;
92
+ }
93
+ if (value instanceof Date) {
94
+ serialized[key] = { __type: 'Date', value: value.toISOString() };
95
+ }
96
+ else if (value === undefined) {
97
+ serialized[key] = { __type: 'undefined' };
98
+ }
99
+ else if (react_1.default.isValidElement(value)) {
100
+ // React elements need special handling
101
+ serialized[key] = { __type: 'ReactElement', rendered: true };
102
+ }
103
+ else {
104
+ serialized[key] = value;
105
+ }
106
+ }
107
+ return serialized;
108
+ }
109
+ /**
110
+ * RSC Renderer class
111
+ * Handles rendering a route with proper server/client component separation
112
+ */
113
+ class RSCRenderer {
114
+ clientManifest;
115
+ serverManifest;
116
+ clientReferences = [];
117
+ cwd;
118
+ constructor(options) {
119
+ this.clientManifest = options.clientManifest;
120
+ this.serverManifest = options.serverManifest;
121
+ this.cwd = options.cwd;
122
+ }
123
+ /**
124
+ * Render a route to RSC Payload
125
+ */
126
+ async render(context) {
127
+ // Reset counters
128
+ resetMountIdCounter();
129
+ this.clientReferences = [];
130
+ // Reset RSC module system state (for client component interception)
131
+ if (rscModuleSystem) {
132
+ rscModuleSystem.resetRSCState();
133
+ }
134
+ const { route, params, searchParams } = context;
135
+ // Load and render layouts + page
136
+ const element = await this.renderRoute(route, params, searchParams);
137
+ // Render to HTML string
138
+ const html = (0, server_1.renderToString)(element);
139
+ // Collect client references from module system (intercepted requires)
140
+ let collectedReferences = this.clientReferences;
141
+ if (rscModuleSystem) {
142
+ const moduleReferences = rscModuleSystem.getClientReferences();
143
+ collectedReferences = [...collectedReferences, ...moduleReferences];
144
+ }
145
+ return {
146
+ html,
147
+ clientReferences: collectedReferences,
148
+ data: {
149
+ params,
150
+ searchParams,
151
+ route: route.pattern,
152
+ },
153
+ };
154
+ }
155
+ /**
156
+ * Render a route with nested layouts
157
+ */
158
+ async renderRoute(route, params, searchParams) {
159
+ // Load the page component
160
+ const PageModule = this.requireComponent(route.pagePath);
161
+ const PageComponent = PageModule.default;
162
+ // Fetch page metadata if exists
163
+ let metadata = {};
164
+ if (PageModule.metadata) {
165
+ metadata = { ...metadata, ...PageModule.metadata };
166
+ }
167
+ if (typeof PageModule.generateMetadata === 'function') {
168
+ try {
169
+ const dynamicMeta = await PageModule.generateMetadata({ params, searchParams });
170
+ metadata = { ...metadata, ...dynamicMeta };
171
+ }
172
+ catch (e) {
173
+ console.error('[Vista RSC] generateMetadata error:', e);
174
+ }
175
+ }
176
+ // Create page element
177
+ let pageElement = react_1.default.createElement(PageComponent, {
178
+ params,
179
+ searchParams,
180
+ });
181
+ // Check if page is a client component
182
+ if (this.isClientComponent(route.pagePath)) {
183
+ const entry = this.clientManifest.clientModules[this.clientManifest.pathToId[route.pagePath]];
184
+ const { element, reference } = createClientComponentPlaceholder(entry, {
185
+ params,
186
+ searchParams,
187
+ });
188
+ this.clientReferences.push(reference);
189
+ pageElement = element;
190
+ }
191
+ // Wrap in layouts (innermost to outermost)
192
+ let wrappedElement = pageElement;
193
+ for (let i = route.layoutPaths.length - 1; i >= 0; i--) {
194
+ const layoutPath = route.layoutPaths[i];
195
+ const LayoutModule = this.requireComponent(layoutPath);
196
+ const LayoutComponent = LayoutModule.default;
197
+ if (this.isClientComponent(layoutPath)) {
198
+ // Client component layout
199
+ const entry = this.clientManifest.clientModules[this.clientManifest.pathToId[layoutPath]];
200
+ const { element, reference } = createClientComponentPlaceholder(entry, { params, searchParams }, wrappedElement);
201
+ this.clientReferences.push(reference);
202
+ wrappedElement = element;
203
+ }
204
+ else {
205
+ // Server component layout
206
+ wrappedElement = react_1.default.createElement(LayoutComponent, { params, searchParams }, wrappedElement);
207
+ }
208
+ }
209
+ return wrappedElement;
210
+ }
211
+ /**
212
+ * Require a component with cache clearing in dev
213
+ */
214
+ requireComponent(componentPath) {
215
+ // Clear cache in development
216
+ if (process.env.NODE_ENV !== 'production') {
217
+ delete require.cache[require.resolve(componentPath)];
218
+ }
219
+ return require(componentPath);
220
+ }
221
+ /**
222
+ * Check if a component is a client component
223
+ */
224
+ isClientComponent(componentPath) {
225
+ return componentPath in this.clientManifest.pathToId;
226
+ }
227
+ }
228
+ exports.RSCRenderer = RSCRenderer;
229
+ /**
230
+ * Create an RSC renderer instance
231
+ */
232
+ function createRSCRenderer(cwd) {
233
+ const clientManifestPath = path_1.default.join(cwd, '.vista', 'client-manifest.json');
234
+ const serverManifestPath = path_1.default.join(cwd, '.vista', 'server', 'server-manifest.json');
235
+ if (!fs_1.default.existsSync(clientManifestPath) || !fs_1.default.existsSync(serverManifestPath)) {
236
+ console.warn('[Vista RSC] Manifests not found. Run build first.');
237
+ return null;
238
+ }
239
+ try {
240
+ const clientManifest = JSON.parse(fs_1.default.readFileSync(clientManifestPath, 'utf-8'));
241
+ const serverManifest = JSON.parse(fs_1.default.readFileSync(serverManifestPath, 'utf-8'));
242
+ return new RSCRenderer({
243
+ clientManifest,
244
+ serverManifest,
245
+ cwd,
246
+ });
247
+ }
248
+ catch (e) {
249
+ console.error('[Vista RSC] Failed to load manifests:', e);
250
+ return null;
251
+ }
252
+ }
253
+ /**
254
+ * Generate the client-side hydration script
255
+ *
256
+ * This just sets the data - the actual hydration is handled by rsc-client.tsx
257
+ * which is bundled and has all client components pre-imported.
258
+ */
259
+ function generateHydrationScript(payload) {
260
+ const references = JSON.stringify(payload.clientReferences);
261
+ const data = JSON.stringify(payload.data);
262
+ return `
263
+ <script>
264
+ window.__VISTA_RSC_DATA__ = ${data};
265
+ window.__VISTA_CLIENT_REFERENCES__ = ${references};
266
+ console.log('[Vista RSC] Client references:', ${references.length > 2 ? references.length : 0});
267
+ </script>
268
+ `;
269
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Vista Server Component Loader
3
+ *
4
+ * Webpack loader that transforms server component imports in client bundles.
5
+ *
6
+ * When a server component is imported in a client bundle:
7
+ * 1. The loader detects if the file has 'client load' directive
8
+ * 2. If NOT a client component, replace the module with a proxy
9
+ * 3. The proxy provides helpful error messages when misused
10
+ */
11
+ import type { LoaderContext } from 'webpack';
12
+ export interface ServerComponentLoaderOptions {
13
+ appDir: string;
14
+ clientManifestPath: string;
15
+ }
16
+ /**
17
+ * Server Component Loader
18
+ */
19
+ export default function serverComponentLoader(this: LoaderContext<ServerComponentLoaderOptions>, source: string): string;