Spaces:
Paused
Paused
File size: 6,421 Bytes
d530f14 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | import { NextResponse } from 'next/server';
import { parseJavaScriptFile, buildComponentTree } from '@/lib/file-parser';
import { FileManifest, FileInfo, RouteInfo } from '@/types/file-manifest';
// SandboxState type used implicitly through global.activeSandbox
declare global {
var activeSandbox: any;
}
export async function GET() {
try {
if (!global.activeSandbox) {
return NextResponse.json({
success: false,
error: 'No active sandbox'
}, { status: 404 });
}
console.log('[get-sandbox-files] Fetching and analyzing file structure...');
// Get list of all relevant files
const findResult = await global.activeSandbox.runCommand({
cmd: 'find',
args: [
'.',
'-name', 'node_modules', '-prune', '-o',
'-name', '.git', '-prune', '-o',
'-name', 'dist', '-prune', '-o',
'-name', 'build', '-prune', '-o',
'-type', 'f',
'(',
'-name', '*.jsx',
'-o', '-name', '*.js',
'-o', '-name', '*.tsx',
'-o', '-name', '*.ts',
'-o', '-name', '*.css',
'-o', '-name', '*.json',
')',
'-print'
]
});
if (findResult.exitCode !== 0) {
throw new Error('Failed to list files');
}
const fileList = (await findResult.stdout()).split('\n').filter((f: string) => f.trim());
console.log('[get-sandbox-files] Found', fileList.length, 'files');
// Read content of each file (limit to reasonable sizes)
const filesContent: Record<string, string> = {};
for (const filePath of fileList) {
try {
// Check file size first
const statResult = await global.activeSandbox.runCommand({
cmd: 'stat',
args: ['-f', '%z', filePath]
});
if (statResult.exitCode === 0) {
const fileSize = parseInt(await statResult.stdout());
// Only read files smaller than 10KB
if (fileSize < 10000) {
const catResult = await global.activeSandbox.runCommand({
cmd: 'cat',
args: [filePath]
});
if (catResult.exitCode === 0) {
const content = await catResult.stdout();
// Remove leading './' from path
const relativePath = filePath.replace(/^\.\//, '');
filesContent[relativePath] = content;
}
}
}
} catch (parseError) {
console.debug('Error parsing component info:', parseError);
// Skip files that can't be read
continue;
}
}
// Get directory structure
const treeResult = await global.activeSandbox.runCommand({
cmd: 'find',
args: ['.', '-type', 'd', '-not', '-path', '*/node_modules*', '-not', '-path', '*/.git*']
});
let structure = '';
if (treeResult.exitCode === 0) {
const dirs = (await treeResult.stdout()).split('\n').filter((d: string) => d.trim());
structure = dirs.slice(0, 50).join('\n'); // Limit to 50 lines
}
// Build enhanced file manifest
const fileManifest: FileManifest = {
files: {},
routes: [],
componentTree: {},
entryPoint: '',
styleFiles: [],
timestamp: Date.now(),
};
// Process each file
for (const [relativePath, content] of Object.entries(filesContent)) {
const fullPath = `/${relativePath}`;
// Create base file info
const fileInfo: FileInfo = {
content: content,
type: 'utility',
path: fullPath,
relativePath,
lastModified: Date.now(),
};
// Parse JavaScript/JSX files
if (relativePath.match(/\.(jsx?|tsx?)$/)) {
const parseResult = parseJavaScriptFile(content, fullPath);
Object.assign(fileInfo, parseResult);
// Identify entry point
if (relativePath === 'src/main.jsx' || relativePath === 'src/index.jsx') {
fileManifest.entryPoint = fullPath;
}
// Identify App.jsx
if (relativePath === 'src/App.jsx' || relativePath === 'App.jsx') {
fileManifest.entryPoint = fileManifest.entryPoint || fullPath;
}
}
// Track style files
if (relativePath.endsWith('.css')) {
fileManifest.styleFiles.push(fullPath);
fileInfo.type = 'style';
}
fileManifest.files[fullPath] = fileInfo;
}
// Build component tree
fileManifest.componentTree = buildComponentTree(fileManifest.files);
// Extract routes (simplified - looks for Route components or page pattern)
fileManifest.routes = extractRoutes(fileManifest.files);
// Update global file cache with manifest
if (global.sandboxState?.fileCache) {
global.sandboxState.fileCache.manifest = fileManifest;
}
return NextResponse.json({
success: true,
files: filesContent,
structure,
fileCount: Object.keys(filesContent).length,
manifest: fileManifest,
});
} catch (error) {
console.error('[get-sandbox-files] Error:', error);
return NextResponse.json({
success: false,
error: (error as Error).message
}, { status: 500 });
}
}
function extractRoutes(files: Record<string, FileInfo>): RouteInfo[] {
const routes: RouteInfo[] = [];
// Look for React Router usage
for (const [path, fileInfo] of Object.entries(files)) {
if (fileInfo.content.includes('<Route') || fileInfo.content.includes('createBrowserRouter')) {
// Extract route definitions (simplified)
const routeMatches = fileInfo.content.matchAll(/path=["']([^"']+)["'].*(?:element|component)={([^}]+)}/g);
for (const match of routeMatches) {
const [, routePath] = match;
// componentRef available in match but not used currently
routes.push({
path: routePath,
component: path,
});
}
}
// Check for Next.js style pages
if (fileInfo.relativePath.startsWith('pages/') || fileInfo.relativePath.startsWith('src/pages/')) {
const routePath = '/' + fileInfo.relativePath
.replace(/^(src\/)?pages\//, '')
.replace(/\.(jsx?|tsx?)$/, '')
.replace(/index$/, '');
routes.push({
path: routePath,
component: path,
});
}
}
return routes;
} |