Spaces:
Paused
Paused
| import path from 'path'; | |
| export type RuntimeType = 'node' | 'python' | 'static'; | |
| export interface RuntimeConfig { | |
| type: RuntimeType; | |
| installCommand: string[]; | |
| startCommand: string[]; | |
| image: string; | |
| packageManager: 'npm' | 'yarn' | 'pnpm' | 'bun' | 'pip'; | |
| } | |
| export class RuntimeDetector { | |
| static detect(files: { path: string, content: string }[]): RuntimeConfig { | |
| const filenames = files.map(f => f.path); | |
| // Check for package manager hints | |
| const hasYarn = filenames.includes('yarn.lock'); | |
| const hasPnpm = filenames.includes('pnpm-lock.yaml'); | |
| const hasBun = filenames.includes('bun.lockb'); | |
| const hasPackageLock = filenames.includes('package-lock.json'); | |
| // Default to npm, only use other package managers if lock file exists | |
| let packageManager: 'npm' | 'yarn' | 'pnpm' | 'bun' = 'npm'; | |
| if (hasYarn) packageManager = 'yarn'; | |
| else if (hasPnpm) packageManager = 'pnpm'; | |
| else if (hasBun) packageManager = 'bun'; | |
| // 1. Node.js detection | |
| if (filenames.includes('package.json')) { | |
| const packageJsonFile = files.find(f => f.path === 'package.json'); | |
| let startCommand = ['bun', 'run', 'start']; | |
| if (packageJsonFile) { | |
| try { | |
| const pkg = JSON.parse(packageJsonFile.content); | |
| // Always use bun to run scripts — it's pre-installed and fast in the worker image | |
| if (pkg.scripts?.dev) { | |
| startCommand = ['bun', 'run', 'dev']; | |
| } else if (pkg.scripts?.start) { | |
| startCommand = ['bun', 'run', 'start']; | |
| } | |
| } catch (e) { } | |
| } | |
| // Always use bun for installation — it's pre-installed and much faster than npm in Docker | |
| const installCmd = ['bun', 'install']; | |
| return { | |
| type: 'node', | |
| installCommand: installCmd, | |
| startCommand: startCommand, | |
| image: 'coder-node', | |
| packageManager: 'bun', // Always bun in Docker | |
| }; | |
| } | |
| // 2. Python detection | |
| if (filenames.includes('requirements.txt') || filenames.some(f => f.endsWith('.py'))) { | |
| let entryPoint = 'main.py'; | |
| if (filenames.includes('app.py')) entryPoint = 'app.py'; | |
| return { | |
| type: 'python', | |
| installCommand: filenames.includes('requirements.txt') ? [ | |
| 'python3', '-m', 'venv', 'venv', | |
| '&&', 'source', 'venv/bin/activate', | |
| '&&', 'pip', 'install', '-r', 'requirements.txt' | |
| ] : [], | |
| startCommand: [ | |
| 'source', 'venv/bin/activate', | |
| '&&', 'python3', '-u', entryPoint | |
| ], | |
| image: 'coder-python', | |
| packageManager: 'pip' as const, | |
| }; | |
| } | |
| // 3. C++ detection | |
| if (filenames.some(f => f.endsWith('.cpp') || f.endsWith('.c'))) { | |
| const cppFile = filenames.find(f => f.endsWith('.cpp') || f.endsWith('.c'))!; | |
| return { | |
| type: 'static', | |
| installCommand: ['g++', cppFile, '-o', 'app'], | |
| startCommand: ['./app'], | |
| image: 'coder-node', | |
| packageManager: 'npm' as const, | |
| }; | |
| } | |
| // 4. Static detection (Fallback) | |
| return { | |
| type: 'static', | |
| installCommand: [], | |
| startCommand: ['npx', 'serve', '.', '-p', '8000'], | |
| image: 'coder-node', | |
| packageManager: 'npm', | |
| }; | |
| } | |
| } | |