Spaces:
Paused
Paused
| import { NextRequest, NextResponse } from 'next/server'; | |
| declare global { | |
| var activeSandbox: any; | |
| } | |
| export async function POST(request: NextRequest) { | |
| try { | |
| const { files } = await request.json(); | |
| if (!files || typeof files !== 'object') { | |
| return NextResponse.json({ | |
| success: false, | |
| error: 'Files object is required' | |
| }, { status: 400 }); | |
| } | |
| if (!global.activeSandbox) { | |
| return NextResponse.json({ | |
| success: false, | |
| error: 'No active sandbox' | |
| }, { status: 404 }); | |
| } | |
| console.log('[detect-and-install-packages] Processing files:', Object.keys(files)); | |
| // Extract all import statements from the files | |
| const imports = new Set<string>(); | |
| const importRegex = /import\s+(?:(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)\s*,?\s*)*(?:from\s+)?['"]([^'"]+)['"]/g; | |
| const requireRegex = /require\s*\(['"]([^'"]+)['"]\)/g; | |
| for (const [filePath, content] of Object.entries(files)) { | |
| if (typeof content !== 'string') continue; | |
| // Skip non-JS/JSX/TS/TSX files | |
| if (!filePath.match(/\.(jsx?|tsx?)$/)) continue; | |
| // Find ES6 imports | |
| let match; | |
| while ((match = importRegex.exec(content)) !== null) { | |
| imports.add(match[1]); | |
| } | |
| // Find CommonJS requires | |
| while ((match = requireRegex.exec(content)) !== null) { | |
| imports.add(match[1]); | |
| } | |
| } | |
| console.log('[detect-and-install-packages] Found imports:', Array.from(imports)); | |
| // Log specific heroicons imports | |
| const heroiconImports = Array.from(imports).filter(imp => imp.includes('heroicons')); | |
| if (heroiconImports.length > 0) { | |
| console.log('[detect-and-install-packages] Heroicon imports:', heroiconImports); | |
| } | |
| // Filter out relative imports and built-in modules | |
| const packages = Array.from(imports).filter(imp => { | |
| // Skip relative imports | |
| if (imp.startsWith('.') || imp.startsWith('/')) return false; | |
| // Skip built-in Node modules | |
| const builtins = ['fs', 'path', 'http', 'https', 'crypto', 'stream', 'util', 'os', 'url', 'querystring', 'child_process']; | |
| if (builtins.includes(imp)) return false; | |
| return true; | |
| }); | |
| // Extract just the package names (without subpaths) | |
| const packageNames = packages.map(pkg => { | |
| if (pkg.startsWith('@')) { | |
| // Scoped package: @scope/package or @scope/package/subpath | |
| const parts = pkg.split('/'); | |
| return parts.slice(0, 2).join('/'); | |
| } else { | |
| // Regular package: package or package/subpath | |
| return pkg.split('/')[0]; | |
| } | |
| }); | |
| // Remove duplicates | |
| const uniquePackages = [...new Set(packageNames)]; | |
| console.log('[detect-and-install-packages] Packages to install:', uniquePackages); | |
| if (uniquePackages.length === 0) { | |
| return NextResponse.json({ | |
| success: true, | |
| packagesInstalled: [], | |
| message: 'No new packages to install' | |
| }); | |
| } | |
| // Check which packages are already installed | |
| const installed: string[] = []; | |
| const missing: string[] = []; | |
| for (const packageName of uniquePackages) { | |
| try { | |
| const checkResult = await global.activeSandbox.runCommand({ | |
| cmd: 'test', | |
| args: ['-d', `node_modules/${packageName}`] | |
| }); | |
| if (checkResult.exitCode === 0) { | |
| installed.push(packageName); | |
| } else { | |
| missing.push(packageName); | |
| } | |
| } catch (checkError) { | |
| // If test command fails, assume package is missing | |
| console.debug(`Package check failed for ${packageName}:`, checkError); | |
| missing.push(packageName); | |
| } | |
| } | |
| console.log('[detect-and-install-packages] Package status:', { installed, missing }); | |
| if (missing.length === 0) { | |
| return NextResponse.json({ | |
| success: true, | |
| packagesInstalled: [], | |
| packagesAlreadyInstalled: installed, | |
| message: 'All packages already installed' | |
| }); | |
| } | |
| // Install missing packages | |
| console.log('[detect-and-install-packages] Installing packages:', missing); | |
| const installResult = await global.activeSandbox.runCommand({ | |
| cmd: 'npm', | |
| args: ['install', '--save', ...missing] | |
| }); | |
| const stdout = await installResult.stdout(); | |
| const stderr = await installResult.stderr(); | |
| console.log('[detect-and-install-packages] Install stdout:', stdout); | |
| if (stderr) { | |
| console.log('[detect-and-install-packages] Install stderr:', stderr); | |
| } | |
| // Verify installation | |
| const finalInstalled: string[] = []; | |
| const failed: string[] = []; | |
| for (const packageName of missing) { | |
| try { | |
| const verifyResult = await global.activeSandbox.runCommand({ | |
| cmd: 'test', | |
| args: ['-d', `node_modules/${packageName}`] | |
| }); | |
| if (verifyResult.exitCode === 0) { | |
| finalInstalled.push(packageName); | |
| console.log(`✓ Verified installation of ${packageName}`); | |
| } else { | |
| failed.push(packageName); | |
| console.log(`✗ Failed to verify installation of ${packageName}`); | |
| } | |
| } catch (error) { | |
| failed.push(packageName); | |
| console.log(`✗ Error verifying ${packageName}:`, error); | |
| } | |
| } | |
| if (failed.length > 0) { | |
| console.error('[detect-and-install-packages] Failed to install:', failed); | |
| } | |
| return NextResponse.json({ | |
| success: true, | |
| packagesInstalled: finalInstalled, | |
| packagesFailed: failed, | |
| packagesAlreadyInstalled: installed, | |
| message: `Installed ${finalInstalled.length} packages`, | |
| logs: stdout | |
| }); | |
| } catch (error) { | |
| console.error('[detect-and-install-packages] Error:', error); | |
| return NextResponse.json({ | |
| success: false, | |
| error: (error as Error).message | |
| }, { status: 500 }); | |
| } | |
| } |