@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,399 @@
1
+ "use strict";
2
+ /**
3
+ * Vista File Scanner
4
+ *
5
+ * Scans the app directory and categorizes files as client or server components
6
+ * using Rust NAPI bindings for fast detection.
7
+ *
8
+ * Server Component Rules:
9
+ * - By default, all components are Server Components
10
+ * - Using 'client load' directive makes it a Client Component
11
+ * - Using client hooks (useState, useEffect, etc.) without 'client load' is an ERROR
12
+ *
13
+ * Performance: Uses Rust-powered RSC scanner when available (~10-100x faster)
14
+ */
15
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ var desc = Object.getOwnPropertyDescriptor(m, k);
18
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
19
+ desc = { enumerable: true, get: function() { return m[k]; } };
20
+ }
21
+ Object.defineProperty(o, k2, desc);
22
+ }) : (function(o, m, k, k2) {
23
+ if (k2 === undefined) k2 = k;
24
+ o[k2] = m[k];
25
+ }));
26
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
27
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
28
+ }) : function(o, v) {
29
+ o["default"] = v;
30
+ });
31
+ var __importStar = (this && this.__importStar) || (function () {
32
+ var ownKeys = function(o) {
33
+ ownKeys = Object.getOwnPropertyNames || function (o) {
34
+ var ar = [];
35
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
36
+ return ar;
37
+ };
38
+ return ownKeys(o);
39
+ };
40
+ return function (mod) {
41
+ if (mod && mod.__esModule) return mod;
42
+ var result = {};
43
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
44
+ __setModuleDefault(result, mod);
45
+ return result;
46
+ };
47
+ })();
48
+ Object.defineProperty(exports, "__esModule", { value: true });
49
+ exports.getRouteTree = getRouteTree;
50
+ exports.scanAppDirectory = scanAppDirectory;
51
+ exports.isNativeAvailable = isNativeAvailable;
52
+ exports.getVersion = getVersion;
53
+ const fs = __importStar(require("fs"));
54
+ const path = __importStar(require("path"));
55
+ const native_scanner_1 = require("../build/rsc/native-scanner");
56
+ // Try to load Rust NAPI bindings, fallback to JS if not available
57
+ let rustNative = null;
58
+ try {
59
+ // Try multiple paths since we might be running from src or dist
60
+ const possiblePaths = [
61
+ // From compiled dist/bin/file-scanner.js
62
+ require('path').resolve(__dirname, '../../../../crates/vista-napi'),
63
+ // From source src/bin/file-scanner.ts
64
+ require('path').resolve(__dirname, '../../../crates/vista-napi'),
65
+ // From workspace root
66
+ require('path').resolve(process.cwd(), '../crates/vista-napi'),
67
+ ];
68
+ for (const p of possiblePaths) {
69
+ try {
70
+ rustNative = require(p);
71
+ console.log(`[Vista] Loaded Rust native bindings from ${p}`);
72
+ break;
73
+ }
74
+ catch (e) {
75
+ // Try next path
76
+ }
77
+ }
78
+ if (!rustNative) {
79
+ console.log('[Vista] Rust native bindings not found, using JS fallback');
80
+ }
81
+ }
82
+ catch (e) {
83
+ console.log('[Vista] Rust native bindings not found, using JS fallback');
84
+ }
85
+ function getRouteTree(appDir) {
86
+ if (rustNative && rustNative.getRouteTree) {
87
+ return rustNative.getRouteTree(appDir);
88
+ }
89
+ throw new Error('Vista Rust bindings not loaded. Cannot generate route tree.');
90
+ }
91
+ // Client-only hooks and APIs that require 'client load' directive
92
+ const CLIENT_HOOKS = [
93
+ 'useState',
94
+ 'useEffect',
95
+ 'useLayoutEffect',
96
+ 'useReducer',
97
+ 'useRef',
98
+ 'useImperativeHandle',
99
+ 'useCallback',
100
+ 'useMemo',
101
+ 'useContext',
102
+ 'useDebugValue',
103
+ 'useDeferredValue',
104
+ 'useTransition',
105
+ 'useId',
106
+ 'useSyncExternalStore',
107
+ 'useInsertionEffect',
108
+ ];
109
+ const CLIENT_APIS = [
110
+ 'createContext',
111
+ 'forwardRef',
112
+ 'memo',
113
+ 'lazy',
114
+ 'startTransition',
115
+ 'useFormStatus',
116
+ 'useFormState',
117
+ 'useOptimistic',
118
+ ];
119
+ const BROWSER_APIS = [
120
+ 'window',
121
+ 'document',
122
+ 'localStorage',
123
+ 'sessionStorage',
124
+ 'navigator',
125
+ 'location',
126
+ 'history',
127
+ 'addEventListener',
128
+ 'removeEventListener',
129
+ 'setTimeout',
130
+ 'setInterval',
131
+ 'requestAnimationFrame',
132
+ 'fetch', // Can be server but often client
133
+ ];
134
+ /**
135
+ * Fast check using Rust if available, else JS fallback
136
+ */
137
+ function isClientComponent(source) {
138
+ if (rustNative) {
139
+ return rustNative.isClientComponent(source);
140
+ }
141
+ // JS Fallback
142
+ const trimmed = source.trim();
143
+ return trimmed.startsWith("'client load'") || trimmed.startsWith('"client load"');
144
+ }
145
+ /**
146
+ * Analyze directive with line number
147
+ */
148
+ function analyzeClientDirective(source) {
149
+ if (rustNative) {
150
+ return rustNative.analyzeClientDirective(source);
151
+ }
152
+ // JS Fallback
153
+ const isClient = isClientComponent(source);
154
+ let directiveLine = 0;
155
+ if (isClient) {
156
+ const lines = source.split('\n');
157
+ for (let i = 0; i < lines.length; i++) {
158
+ const line = lines[i].trim();
159
+ if (line.startsWith("'client load'") || line.startsWith('"client load"')) {
160
+ directiveLine = i + 1;
161
+ break;
162
+ }
163
+ }
164
+ }
165
+ return { isClient, directiveLine };
166
+ }
167
+ /**
168
+ * Detect client-only hooks/APIs used in source
169
+ */
170
+ function detectClientHooks(source) {
171
+ const usedHooks = [];
172
+ // Check for React hooks
173
+ for (const hook of CLIENT_HOOKS) {
174
+ // Match hook usage: useState, useState(, useState<
175
+ const regex = new RegExp(`\\b${hook}\\s*[(<]`, 'g');
176
+ if (regex.test(source)) {
177
+ usedHooks.push(hook);
178
+ }
179
+ }
180
+ // Check for client APIs
181
+ for (const api of CLIENT_APIS) {
182
+ const regex = new RegExp(`\\b${api}\\s*[(<]`, 'g');
183
+ if (regex.test(source)) {
184
+ usedHooks.push(api);
185
+ }
186
+ }
187
+ // Check for browser APIs (with more context to avoid false positives)
188
+ for (const api of BROWSER_APIS) {
189
+ // Look for direct usage like window.something or just window
190
+ const regex = new RegExp(`\\b${api}\\s*[.\\[]`, 'g');
191
+ if (regex.test(source)) {
192
+ usedHooks.push(api);
193
+ }
194
+ }
195
+ // Check for event handlers (onClick, onChange, etc.)
196
+ const eventHandlerRegex = /\bon[A-Z][a-zA-Z]*\s*=/g;
197
+ if (eventHandlerRegex.test(source)) {
198
+ usedHooks.push('event handlers (onClick, onChange, etc.)');
199
+ }
200
+ return [...new Set(usedHooks)]; // Remove duplicates
201
+ }
202
+ /**
203
+ * Extract exports from source (basic detection)
204
+ */
205
+ function extractExports(source) {
206
+ const exports = [];
207
+ const lines = source.split('\n');
208
+ for (const line of lines) {
209
+ const trimmed = line.trim();
210
+ // export default function Name
211
+ if (trimmed.startsWith('export default function ')) {
212
+ const match = trimmed.match(/export default function (\w+)/);
213
+ if (match)
214
+ exports.push(match[1]);
215
+ }
216
+ // export function Name
217
+ else if (trimmed.startsWith('export function ')) {
218
+ const match = trimmed.match(/export function (\w+)/);
219
+ if (match)
220
+ exports.push(match[1]);
221
+ }
222
+ // export const Name
223
+ else if (trimmed.startsWith('export const ')) {
224
+ const match = trimmed.match(/export const (\w+)/);
225
+ if (match)
226
+ exports.push(match[1]);
227
+ }
228
+ // export default
229
+ else if (trimmed.startsWith('export default')) {
230
+ exports.push('default');
231
+ }
232
+ }
233
+ return exports;
234
+ }
235
+ /**
236
+ * Recursively scan directory for TypeScript/TSX files
237
+ */
238
+ function scanDirectory(dir, baseDir, errors) {
239
+ const results = [];
240
+ if (!fs.existsSync(dir)) {
241
+ return results;
242
+ }
243
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
244
+ for (const entry of entries) {
245
+ const fullPath = path.join(dir, entry.name);
246
+ if (entry.isDirectory()) {
247
+ // Skip node_modules and hidden directories
248
+ if (entry.name.startsWith('.') || entry.name === 'node_modules') {
249
+ continue;
250
+ }
251
+ results.push(...scanDirectory(fullPath, baseDir, errors));
252
+ }
253
+ else if (entry.isFile() && /\.(tsx?|jsx?)$/.test(entry.name)) {
254
+ const source = fs.readFileSync(fullPath, 'utf-8');
255
+ const analysis = analyzeClientDirective(source);
256
+ const exports = extractExports(source);
257
+ const clientHooksUsed = detectClientHooks(source);
258
+ let hasError = false;
259
+ let errorMessage;
260
+ // ERROR: Using client hooks without 'client load' directive
261
+ if (!analysis.isClient && clientHooksUsed.length > 0) {
262
+ hasError = true;
263
+ const hookList = clientHooksUsed.slice(0, 3).join(', ');
264
+ const more = clientHooksUsed.length > 3 ? ` and ${clientHooksUsed.length - 3} more` : '';
265
+ errorMessage = `Server Component Error: You're using ${hookList}${more} in a Server Component.\n\nTo fix this, add 'client load' at the top of your file to make it a Client Component:\n\n'client load';\n\nimport ...`;
266
+ errors.push({
267
+ file: path.relative(baseDir, fullPath),
268
+ message: errorMessage,
269
+ hooks: clientHooksUsed,
270
+ });
271
+ }
272
+ results.push({
273
+ absolutePath: fullPath,
274
+ relativePath: path.relative(baseDir, fullPath),
275
+ isClient: analysis.isClient,
276
+ directiveLine: analysis.directiveLine,
277
+ exports,
278
+ clientHooksUsed,
279
+ hasError,
280
+ errorMessage,
281
+ });
282
+ }
283
+ }
284
+ return results;
285
+ }
286
+ /**
287
+ * Scan the app directory and return categorized files
288
+ * Uses Rust-powered RSC scanner when available for ~10-100x faster performance
289
+ */
290
+ function scanAppDirectory(appDir) {
291
+ // Try native RSC scanner first (Rust-powered, much faster)
292
+ if ((0, native_scanner_1.isNativeAvailable)()) {
293
+ const nativeResult = (0, native_scanner_1.scanAppNative)(appDir);
294
+ if (nativeResult) {
295
+ const converted = (0, native_scanner_1.convertScanResult)(nativeResult);
296
+ // Transform to our ScanResult format
297
+ const result = {
298
+ clientComponents: converted.clientComponents.map((c) => ({
299
+ absolutePath: c.absolutePath,
300
+ relativePath: c.relativePath,
301
+ isClient: c.isClient,
302
+ directiveLine: c.directiveLine,
303
+ exports: c.exports,
304
+ clientHooksUsed: c.clientHooksUsed,
305
+ hasError: false,
306
+ errorMessage: undefined,
307
+ })),
308
+ serverComponents: converted.serverComponents.map((c) => ({
309
+ absolutePath: c.absolutePath,
310
+ relativePath: c.relativePath,
311
+ isClient: false,
312
+ directiveLine: 0,
313
+ exports: c.exports,
314
+ clientHooksUsed: [],
315
+ hasError: false,
316
+ errorMessage: undefined,
317
+ })),
318
+ layouts: nativeResult.layouts.map((c) => ({
319
+ absolutePath: c.absolutePath,
320
+ relativePath: c.relativePath,
321
+ isClient: c.isClient,
322
+ directiveLine: c.directiveLine,
323
+ exports: c.exports,
324
+ clientHooksUsed: c.clientHooksUsed,
325
+ hasError: false,
326
+ errorMessage: undefined,
327
+ })),
328
+ pages: nativeResult.pages.map((c) => ({
329
+ absolutePath: c.absolutePath,
330
+ relativePath: c.relativePath,
331
+ isClient: c.isClient,
332
+ directiveLine: c.directiveLine,
333
+ exports: c.exports,
334
+ clientHooksUsed: c.clientHooksUsed,
335
+ hasError: false,
336
+ errorMessage: undefined,
337
+ })),
338
+ errors: nativeResult.errors.map((e) => ({
339
+ file: e.file,
340
+ message: e.message,
341
+ hooks: e.hooks,
342
+ })),
343
+ };
344
+ return result;
345
+ }
346
+ }
347
+ // Fallback to JS-based scanner
348
+ const errors = [];
349
+ const files = scanDirectory(appDir, appDir, errors);
350
+ const result = {
351
+ clientComponents: [],
352
+ serverComponents: [],
353
+ layouts: [],
354
+ pages: [],
355
+ errors,
356
+ };
357
+ for (const file of files) {
358
+ const basename = path.basename(file.relativePath, path.extname(file.relativePath));
359
+ // Categorize by file convention
360
+ if (basename === 'root' || basename === 'layout') {
361
+ result.layouts.push(file);
362
+ }
363
+ else if (basename === 'index' || basename === 'page') {
364
+ result.pages.push(file);
365
+ }
366
+ else if (basename === 'not-found') {
367
+ result.notFound = file;
368
+ }
369
+ else if (basename === 'error') {
370
+ result.error = file;
371
+ }
372
+ else if (basename === 'loading') {
373
+ result.loading = file;
374
+ }
375
+ // Also categorize by client/server
376
+ if (file.isClient) {
377
+ result.clientComponents.push(file);
378
+ }
379
+ else {
380
+ result.serverComponents.push(file);
381
+ }
382
+ }
383
+ return result;
384
+ }
385
+ /**
386
+ * Check if Rust native bindings are available
387
+ */
388
+ function isNativeAvailable() {
389
+ return rustNative !== null || (0, native_scanner_1.isNativeAvailable)();
390
+ }
391
+ /**
392
+ * Get version info
393
+ */
394
+ function getVersion() {
395
+ if (rustNative && rustNative.version) {
396
+ return rustNative.version();
397
+ }
398
+ return '0.1.0-js-fallback';
399
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Vista Server Component Webpack Plugin
3
+ *
4
+ * Checks for server component violations on every webpack compilation.
5
+ * Fails the build if client hooks are used without 'client load' directive.
6
+ */
7
+ import type { Compiler } from 'webpack';
8
+ export declare class VistaServerComponentPlugin {
9
+ private appDir;
10
+ constructor(options: {
11
+ appDir: string;
12
+ });
13
+ apply(compiler: Compiler): void;
14
+ private checkServerComponents;
15
+ private scanDirectory;
16
+ }
17
+ export default VistaServerComponentPlugin;
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ /**
3
+ * Vista Server Component Webpack Plugin
4
+ *
5
+ * Checks for server component violations on every webpack compilation.
6
+ * Fails the build if client hooks are used without 'client load' directive.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.VistaServerComponentPlugin = void 0;
43
+ const fs = __importStar(require("fs"));
44
+ const path = __importStar(require("path"));
45
+ // Client-only hooks and APIs that require 'client load' directive
46
+ const CLIENT_HOOKS = [
47
+ 'useState', 'useEffect', 'useLayoutEffect', 'useReducer', 'useRef',
48
+ 'useImperativeHandle', 'useCallback', 'useMemo', 'useContext',
49
+ 'useDebugValue', 'useDeferredValue', 'useTransition', 'useId',
50
+ 'useSyncExternalStore', 'useInsertionEffect',
51
+ ];
52
+ function hasClientDirective(source) {
53
+ const trimmed = source.trim();
54
+ return trimmed.startsWith("'client load'") || trimmed.startsWith('"client load"');
55
+ }
56
+ function detectClientHooks(source) {
57
+ const usedHooks = [];
58
+ for (const hook of CLIENT_HOOKS) {
59
+ const regex = new RegExp(`\\b${hook}\\s*[(<]`, 'g');
60
+ if (regex.test(source)) {
61
+ usedHooks.push(hook);
62
+ }
63
+ }
64
+ // Check for event handlers
65
+ if (/\bon[A-Z][a-zA-Z]*\s*=/g.test(source)) {
66
+ usedHooks.push('event handlers');
67
+ }
68
+ return [...new Set(usedHooks)];
69
+ }
70
+ class VistaServerComponentPlugin {
71
+ appDir;
72
+ constructor(options) {
73
+ this.appDir = options.appDir;
74
+ }
75
+ apply(compiler) {
76
+ // Use afterCompile hook so we can add errors to compilation
77
+ compiler.hooks.afterCompile.tap('VistaServerComponentPlugin', (compilation) => {
78
+ const errors = this.checkServerComponents();
79
+ if (errors.length > 0) {
80
+ console.log('');
81
+ console.log('\x1b[41m\x1b[37m ERROR \x1b[0m \x1b[31mServer Component Error\x1b[0m');
82
+ console.log('');
83
+ for (const error of errors) {
84
+ console.log(`\x1b[31m✗\x1b[0m ${error.file}`);
85
+ console.log(` You're using \x1b[33m${error.hooks.join(', ')}\x1b[0m in a Server Component.`);
86
+ console.log('');
87
+ console.log(` \x1b[36mTo fix:\x1b[0m Add \x1b[33m'client load'\x1b[0m at the top of your file:`);
88
+ console.log('');
89
+ console.log(` \x1b[32m'client load';\x1b[0m`);
90
+ console.log('');
91
+ // Add webpack error so it shows in overlay
92
+ const WebpackError = require('webpack').WebpackError;
93
+ const err = new WebpackError(`Server Component Error: ${error.file}\n` +
94
+ `You're using ${error.hooks.join(', ')} in a Server Component.\n` +
95
+ `Add 'client load' at the top of your file to make it a Client Component.`);
96
+ err.file = error.file;
97
+ compilation.errors.push(err);
98
+ }
99
+ }
100
+ });
101
+ }
102
+ checkServerComponents() {
103
+ const errors = [];
104
+ this.scanDirectory(this.appDir, errors);
105
+ return errors;
106
+ }
107
+ scanDirectory(dir, errors) {
108
+ if (!fs.existsSync(dir))
109
+ return;
110
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
111
+ for (const entry of entries) {
112
+ const fullPath = path.join(dir, entry.name);
113
+ if (entry.isDirectory()) {
114
+ if (!entry.name.startsWith('.') && entry.name !== 'node_modules') {
115
+ this.scanDirectory(fullPath, errors);
116
+ }
117
+ }
118
+ else if (entry.isFile() && /\.(tsx?|jsx?)$/.test(entry.name)) {
119
+ const source = fs.readFileSync(fullPath, 'utf-8');
120
+ const isClient = hasClientDirective(source);
121
+ const clientHooks = detectClientHooks(source);
122
+ if (!isClient && clientHooks.length > 0) {
123
+ errors.push({
124
+ file: path.relative(this.appDir, fullPath),
125
+ hooks: clientHooks
126
+ });
127
+ }
128
+ }
129
+ }
130
+ }
131
+ }
132
+ exports.VistaServerComponentPlugin = VistaServerComponentPlugin;
133
+ exports.default = VistaServerComponentPlugin;
@@ -0,0 +1,6 @@
1
+ import webpack from 'webpack';
2
+ export interface WebpackConfigOptions {
3
+ cwd: string;
4
+ isDev: boolean;
5
+ }
6
+ export declare function createWebpackConfig(options: WebpackConfigOptions): webpack.Configuration;
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createWebpackConfig = createWebpackConfig;
7
+ const path_1 = __importDefault(require("path"));
8
+ const webpack_1 = __importDefault(require("webpack"));
9
+ const react_refresh_webpack_plugin_1 = __importDefault(require("@pmmmwh/react-refresh-webpack-plugin"));
10
+ const server_component_plugin_1 = require("./server-component-plugin");
11
+ const vista_flight_plugin_1 = require("../build/webpack/plugins/vista-flight-plugin");
12
+ function createWebpackConfig(options) {
13
+ const { cwd, isDev } = options;
14
+ const vistaDir = path_1.default.join(cwd, '.vista');
15
+ const entryPoint = path_1.default.join(vistaDir, 'client.tsx');
16
+ // Find React - check local node_modules first, then traverse up for monorepo hoisting
17
+ const findModulePath = (moduleName) => {
18
+ const localPath = path_1.default.resolve(cwd, 'node_modules', moduleName);
19
+ if (require('fs').existsSync(localPath)) {
20
+ return localPath;
21
+ }
22
+ // Try to resolve from cwd (will traverse up)
23
+ try {
24
+ return path_1.default.dirname(require.resolve(`${moduleName}/package.json`, { paths: [cwd] }));
25
+ }
26
+ catch {
27
+ // Fallback to framework's node_modules
28
+ return path_1.default.dirname(require.resolve(`${moduleName}/package.json`));
29
+ }
30
+ };
31
+ const reactPath = findModulePath('react');
32
+ const reactDomPath = findModulePath('react-dom');
33
+ return {
34
+ mode: isDev ? 'development' : 'production',
35
+ entry: isDev
36
+ ? [
37
+ // HMR runtime - reload=true for full page reload on unaccepted modules
38
+ // noInfo to reduce console noise, overlay=false to use Vista's error overlay
39
+ 'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000&reload=true&noInfo=true&overlay=false',
40
+ entryPoint
41
+ ]
42
+ : entryPoint,
43
+ output: {
44
+ path: vistaDir,
45
+ filename: 'client.js',
46
+ publicPath: '/',
47
+ clean: false, // Don't clean .vista folder
48
+ },
49
+ // Webpack 5 Persistent Caching - dramatically faster rebuilds
50
+ cache: isDev ? {
51
+ type: 'filesystem',
52
+ cacheDirectory: path_1.default.join(cwd, 'node_modules', '.cache', 'vista-webpack'),
53
+ buildDependencies: {
54
+ config: [__filename],
55
+ },
56
+ } : false,
57
+ // Optimize module resolution for faster HMR
58
+ snapshot: isDev ? {
59
+ managedPaths: [path_1.default.resolve(cwd, 'node_modules')],
60
+ immutablePaths: [],
61
+ } : undefined,
62
+ resolve: {
63
+ extensions: ['.tsx', '.ts', '.jsx', '.js'],
64
+ alias: {
65
+ 'react': reactPath,
66
+ 'react-dom': reactDomPath,
67
+ 'react/jsx-runtime': path_1.default.join(reactPath, 'jsx-runtime'),
68
+ 'react/jsx-dev-runtime': path_1.default.join(reactPath, 'jsx-dev-runtime'),
69
+ },
70
+ modules: [
71
+ path_1.default.resolve(cwd, 'node_modules'),
72
+ 'node_modules'
73
+ ]
74
+ },
75
+ module: {
76
+ rules: [
77
+ {
78
+ test: /\.[jt]sx?$/,
79
+ exclude: /node_modules/,
80
+ use: [
81
+ {
82
+ loader: 'swc-loader',
83
+ options: {
84
+ jsc: {
85
+ parser: {
86
+ syntax: 'typescript',
87
+ tsx: true,
88
+ dynamicImport: true,
89
+ },
90
+ transform: {
91
+ react: {
92
+ runtime: 'automatic',
93
+ development: isDev,
94
+ refresh: isDev, // Enable React Fast Refresh
95
+ }
96
+ },
97
+ target: 'es2020',
98
+ },
99
+ module: {
100
+ type: 'es6'
101
+ }
102
+ }
103
+ },
104
+ {
105
+ // Vista Flight Loader - marks modules with RSC info
106
+ // Runs FIRST (loaders execute bottom-to-top) to see original source
107
+ loader: require.resolve('../build/webpack/loaders/vista-flight-loader'),
108
+ },
109
+ ]
110
+ },
111
+ {
112
+ test: /\.css$/,
113
+ use: 'null-loader' // Ignore CSS in client bundle (handled by PostCSS)
114
+ }
115
+ ]
116
+ },
117
+ plugins: [
118
+ // Server Component enforcement - runs on every compile
119
+ new server_component_plugin_1.VistaServerComponentPlugin({ appDir: path_1.default.join(cwd, 'app') }),
120
+ // Vista Flight Plugin - RSC bundle separation and manifest
121
+ new vista_flight_plugin_1.VistaFlightPlugin({ appDir: path_1.default.join(cwd, 'app'), dev: isDev }),
122
+ ...(isDev ? [
123
+ new webpack_1.default.HotModuleReplacementPlugin(),
124
+ new react_refresh_webpack_plugin_1.default({
125
+ overlay: false, // Disable built-in overlay, use Vista's error overlay
126
+ }),
127
+ ] : []),
128
+ new webpack_1.default.DefinePlugin({
129
+ 'process.env.NODE_ENV': JSON.stringify(isDev ? 'development' : 'production'),
130
+ }),
131
+ ],
132
+ devtool: isDev ? 'eval-cheap-module-source-map' : 'source-map',
133
+ stats: 'minimal',
134
+ infrastructureLogging: {
135
+ level: 'warn',
136
+ },
137
+ };
138
+ }