/** * BuildOrchestrator - Utilities for managing TypeScript server build pipelines * * Provides: * - Dependency installation checking * - TypeScript compilation * - Custom build hooks */ import { spawnSync } from 'node:child_process'; import fs from 'node:fs'; import path from 'node:path'; /** * Check if npm dependencies need to be installed and run npm install */ export const checkAndInstallDependencies = (targetDir: string): void => { const nodeModulesPath = path.join(targetDir, 'node_modules'); // Check if node_modules exists at all if (!fs.existsSync(nodeModulesPath)) { console.log('📦 node_modules not found, installing dependencies...'); } else { console.log('📦 Ensuring dependencies are up to date...'); } // Always run npm install to ensure symlinks for local dependencies are correct // npm install is fast when nothing has changed (only updates what's needed) const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm'; console.log(`Running: ${npmCommand} install in ${targetDir}`); const result = spawnSync(npmCommand, ['install'], { cwd: targetDir, stdio: 'inherit', shell: process.platform === 'win32' }); if (result.error) { throw new Error(`Failed to install dependencies: ${result.error.message}`); } if (result.status !== 0) { throw new Error(`npm install exited with code ${result.status}`); } console.log('✅ Dependencies installed successfully\n'); }; /** * Build the TypeScript project */ export const buildTypeScript = (targetDir: string, buildCommand: string[] = ['npm', 'run', 'build']): void => { console.log(`🔨 Building TypeScript in ${targetDir}...\n`); const npmCommand = process.platform === 'win32' && buildCommand[0] === 'npm' ? 'npm.cmd' : buildCommand[0]; const result = spawnSync(npmCommand, buildCommand.slice(1), { cwd: targetDir, stdio: 'inherit', shell: process.platform === 'win32' }); if (result.error) { throw new Error(`Failed to build: ${result.error.message}`); } if (result.status !== 0) { throw new Error(`Build exited with code ${result.status}`); } console.log('✅ Build completed successfully\n'); }; /** * Run a custom preparation script if it exists */ export const runCustomPrepareScript = (scriptPath: string, workingDir: string): void => { const resolvedScriptPath = path.isAbsolute(scriptPath) ? scriptPath : path.resolve(workingDir, scriptPath); if (!fs.existsSync(resolvedScriptPath)) { console.log(`⚠️ Custom prepare script not found at ${resolvedScriptPath} - skipping\n`); return; } console.log(`📦 Running custom prepare script: ${resolvedScriptPath}...\n`); const result = spawnSync('node', [resolvedScriptPath], { cwd: workingDir, stdio: 'inherit', shell: process.platform === 'win32' }); if (result.error) { throw new Error(`Failed to run prepare script: ${result.error.message}`); } if (result.status !== 0) { throw new Error(`Prepare script exited with code ${result.status}`); } console.log('✅ Prepare script completed\n'); }; /** * Parse port from environment variable */ export const parsePortEnv = (value: string | undefined, label: string): number | undefined => { if (!value) { return undefined; } const trimmed = value.trim(); if (trimmed.length === 0) { return undefined; } const parsed = Number(trimmed); const isIntegerPort = (port: number): boolean => Number.isInteger(port) && port > 0 && port <= 65535; if (!isIntegerPort(parsed)) { console.warn(`${label} is not a valid port: ${value}. Falling back to automatic selection.`); return undefined; } return parsed; };