Files
lifeforge/scripts/forge/commands/dev-commands.ts
Melvin Chia 9f17cfd33b feat(cli): implement module management commands (add, remove, list) and enhance CLI logging
Former-commit-id: a27df1846c1fbc898c62a97e34f5f30eaa25325d [formerly ffc7fa7d80b1036f7f0fb4021fc166a58c413d40] [formerly e65c86c6674076e461d8cb804c009161aad7006d [formerly 64368a36e380a93f2d62808d6255826f11da9fee]]
Former-commit-id: c0efbb95a1243aa8c1316a34ce852c98b9b0cd66 [formerly 2c7aa0860c82d25d90901e89a7d70f161308d027]
Former-commit-id: c3e346e2d6b78be92c66c348e5f9df003a15dadf
2025-10-10 13:20:33 +08:00

169 lines
3.8 KiB
TypeScript

import concurrently from 'concurrently'
import {
PROJECTS_ALLOWED,
TOOLS_ALLOWED,
VALID_SERVICES
} from '../constants/constants'
import type { ConcurrentServiceConfig, ServiceType } from '../types'
import {
executeCommand,
killExistingProcess,
validateEnvironment
} from '../utils/helpers'
import { CLILoggingService } from '../utils/logging'
/**
* Service command configurations
*/
interface ServiceConfig {
command: string | (() => string)
cwd?: () => string | undefined
requiresEnv?: string[]
}
const SERVICE_COMMANDS: Record<string, ServiceConfig> = {
db: {
command: './pocketbase serve',
cwd: () => process.env.PB_DIR,
requiresEnv: ['PB_DIR']
},
server: {
command: () => {
killExistingProcess('lifeforge/node_modules/.bin/tsx')
return 'cd server && bun run dev'
}
},
client: {
command: () => {
killExistingProcess('lifeforge/node_modules/.bin/vite')
return 'cd client && bun run dev'
}
},
ui: {
command: () => {
killExistingProcess('lifeforge/node_modules/.bin/storybook')
return 'cd packages/lifeforge-ui && bun run dev'
}
}
}
/**
* Creates service configurations for concurrent execution
*/
const createConcurrentServices = (): ConcurrentServiceConfig<
string | (() => string)
>[] => [
{
name: 'db',
command: SERVICE_COMMANDS.db.command,
cwd: SERVICE_COMMANDS.db.cwd?.()
},
{
name: 'server',
command: SERVICE_COMMANDS.server.command
},
{
name: 'client',
command: SERVICE_COMMANDS.client.command
}
]
/**
* Starts a single service based on its configuration
*/
function startSingleService(service: string): void {
// Handle core services
if (service in SERVICE_COMMANDS) {
const config = SERVICE_COMMANDS[service]
if (config.requiresEnv) {
validateEnvironment(config.requiresEnv)
}
executeCommand(config.command, {
cwd: config.cwd?.()
})
return
}
// Handle tool services
if (service in TOOLS_ALLOWED) {
const projectPath =
PROJECTS_ALLOWED[service as keyof typeof PROJECTS_ALLOWED]
executeCommand(`cd ${projectPath} && bun run dev`)
return
}
throw new Error(`Unknown service: ${service}`)
}
/**
* Starts all development services concurrently
*/
function startAllServices(): void {
validateEnvironment(['PB_DIR'])
CLILoggingService.info('Starting all services: db, server, client...')
try {
const services = createConcurrentServices()
services.forEach(service => {
if (typeof service.command === 'function') {
service.command = service.command()
}
})
concurrently(services as ConcurrentServiceConfig[], {
killOthers: ['failure', 'success'],
restartTries: 3,
prefix: 'name',
prefixColors: ['cyan', 'green', 'magenta']
})
} catch (error) {
CLILoggingService.error('Failed to start all services.')
CLILoggingService.error(`${error}`)
process.exit(1)
}
}
/**
* Validates if a service is valid
*/
function validateService(service: string): void {
if (!VALID_SERVICES.includes(service as any)) {
CLILoggingService.error(`Invalid service: ${service}`)
CLILoggingService.error(`Available services: ${VALID_SERVICES.join(', ')}`)
process.exit(1)
}
}
/**
* Main development command handler
*/
export function devHandler(service: string): void {
validateService(service)
if (service === 'all') {
startAllServices()
return
}
CLILoggingService.info(`Starting service: ${service}...`)
try {
startSingleService(service)
} catch (error) {
CLILoggingService.error(`Failed to start service: ${service}`)
CLILoggingService.error(`${error}`)
process.exit(1)
}
}
/**
* Gets the list of available services
*/
export function getAvailableServices(): readonly ServiceType[] {
return VALID_SERVICES
}