@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.
- package/bin/vista.js +98 -0
- package/dist/auth/index.d.ts +8 -0
- package/dist/auth/index.js +16 -0
- package/dist/bin/build-rsc.d.ts +17 -0
- package/dist/bin/build-rsc.js +320 -0
- package/dist/bin/build.d.ts +4 -0
- package/dist/bin/build.js +336 -0
- package/dist/bin/file-scanner.d.ts +66 -0
- package/dist/bin/file-scanner.js +399 -0
- package/dist/bin/server-component-plugin.d.ts +17 -0
- package/dist/bin/server-component-plugin.js +133 -0
- package/dist/bin/webpack.config.d.ts +6 -0
- package/dist/bin/webpack.config.js +138 -0
- package/dist/build/manifest.d.ts +95 -0
- package/dist/build/manifest.js +168 -0
- package/dist/build/rsc/client-manifest.d.ts +48 -0
- package/dist/build/rsc/client-manifest.js +191 -0
- package/dist/build/rsc/client-reference-plugin.d.ts +37 -0
- package/dist/build/rsc/client-reference-plugin.js +185 -0
- package/dist/build/rsc/compiler.d.ts +36 -0
- package/dist/build/rsc/compiler.js +311 -0
- package/dist/build/rsc/index.d.ts +16 -0
- package/dist/build/rsc/index.js +32 -0
- package/dist/build/rsc/native-scanner.d.ts +123 -0
- package/dist/build/rsc/native-scanner.js +165 -0
- package/dist/build/rsc/rsc-renderer.d.ts +99 -0
- package/dist/build/rsc/rsc-renderer.js +269 -0
- package/dist/build/rsc/server-component-loader.d.ts +19 -0
- package/dist/build/rsc/server-component-loader.js +147 -0
- package/dist/build/rsc/server-manifest.d.ts +63 -0
- package/dist/build/rsc/server-manifest.js +268 -0
- package/dist/build/webpack/loaders/vista-flight-loader.d.ts +17 -0
- package/dist/build/webpack/loaders/vista-flight-loader.js +93 -0
- package/dist/build/webpack/plugins/vista-flight-plugin.d.ts +36 -0
- package/dist/build/webpack/plugins/vista-flight-plugin.js +133 -0
- package/dist/client/dynamic.d.ts +25 -0
- package/dist/client/dynamic.js +68 -0
- package/dist/client/font.d.ts +98 -0
- package/dist/client/font.js +109 -0
- package/dist/client/head.d.ts +79 -0
- package/dist/client/head.js +261 -0
- package/dist/client/hydration.d.ts +45 -0
- package/dist/client/hydration.js +291 -0
- package/dist/client/link.d.ts +30 -0
- package/dist/client/link.js +188 -0
- package/dist/client/navigation.d.ts +28 -0
- package/dist/client/navigation.js +116 -0
- package/dist/client/router.d.ts +41 -0
- package/dist/client/router.js +190 -0
- package/dist/client/script.d.ts +51 -0
- package/dist/client/script.js +118 -0
- package/dist/components/client-island.d.ts +34 -0
- package/dist/components/client-island.js +75 -0
- package/dist/components/client.d.ts +29 -0
- package/dist/components/client.js +102 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +8 -0
- package/dist/components/link.d.ts +6 -0
- package/dist/components/link.js +13 -0
- package/dist/config.d.ts +10 -0
- package/dist/config.js +31 -0
- package/dist/dev-error.d.ts +35 -0
- package/dist/dev-error.js +310 -0
- package/dist/image/get-img-props.d.ts +28 -0
- package/dist/image/get-img-props.js +49 -0
- package/dist/image/image-config.d.ts +20 -0
- package/dist/image/image-config.js +20 -0
- package/dist/image/image-loader.d.ts +7 -0
- package/dist/image/image-loader.js +14 -0
- package/dist/image/index.d.ts +6 -0
- package/dist/image/index.js +110 -0
- package/dist/image.d.ts +10 -0
- package/dist/image.js +7 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +53 -0
- package/dist/metadata/generate.d.ts +22 -0
- package/dist/metadata/generate.js +324 -0
- package/dist/metadata/index.d.ts +7 -0
- package/dist/metadata/index.js +26 -0
- package/dist/metadata/types.d.ts +325 -0
- package/dist/metadata/types.js +15 -0
- package/dist/router/context.d.ts +8 -0
- package/dist/router/context.js +13 -0
- package/dist/router/index.d.ts +2 -0
- package/dist/router/index.js +18 -0
- package/dist/router/provider.d.ts +5 -0
- package/dist/router/provider.js +31 -0
- package/dist/server/client-boundary.d.ts +48 -0
- package/dist/server/client-boundary.js +133 -0
- package/dist/server/engine.d.ts +4 -0
- package/dist/server/engine.js +651 -0
- package/dist/server/index.d.ts +95 -0
- package/dist/server/index.js +177 -0
- package/dist/server/rsc-engine.d.ts +20 -0
- package/dist/server/rsc-engine.js +588 -0
- package/dist/server/rsc-module-system.d.ts +33 -0
- package/dist/server/rsc-module-system.js +119 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.js +2 -0
- 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,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
|
+
}
|