UNPKG

@interopio/iocd-cli

Version:

CLI tool for setting up, building and packaging io.Connect Desktop platforms

328 lines 14.5 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AppService = void 0; const child_process_1 = require("child_process"); const fs_1 = require("fs"); const path_1 = require("path"); const logger_1 = require("../utils/logger"); const error_handler_1 = require("../utils/error.handler"); const concurrently_1 = __importDefault(require("concurrently")); /** * Service for managing template applications. * * This service: * 1. Discovers all apps in the apps/ folder of generated projects * 2. Installs dependencies for all apps (npm install) * 3. Builds apps and copies them to modifications folder (npm run build) * 4. Provides progress feedback during operations */ class AppService { logger = logger_1.Logger.getInstance(); /** * Install dependencies for all applications found in the apps/ directory */ async installAllApps() { const appsDir = (0, path_1.join)(process.cwd(), 'apps'); if (!(0, fs_1.existsSync)(appsDir)) { this.logger.debug('No apps/ directory found - skipping app installs'); return; } const apps = this.discoverApps(appsDir); if (apps.length === 0) { this.logger.info('No applications found in apps/ directory'); return; } this.logger.info(`Found ${apps.length} applications to install dependencies for:`); apps.forEach(app => this.logger.info(` - ${app.name}`)); for (const app of apps) { await this.installApp(app); } } /** * Build all applications found in the apps/ directory */ async buildAllApps() { const appsDir = (0, path_1.join)(process.cwd(), 'apps'); if (!(0, fs_1.existsSync)(appsDir)) { this.logger.debug('No apps/ directory found - skipping app builds'); return; } const apps = this.discoverApps(appsDir); if (apps.length === 0) { this.logger.debug('No applications found in apps/ directory'); return; } this.logger.info(`Found ${apps.length} template applications to build: ${apps.map(a => a.name).join(", ")}`); for (const app of apps) { await this.buildApp(app); await this.copyModifications(app, "prod"); } this.logger.info('All template applications built and moved successfully!'); } async startAllAppsInDevMode() { const appsDir = (0, path_1.join)(process.cwd(), 'apps'); if (!(0, fs_1.existsSync)(appsDir)) { this.logger.info('No apps/ directory found - skipping app starts'); return; } const apps = this.discoverApps(appsDir); if (apps.length === 0) { this.logger.info('No applications found in apps/ directory'); return; } this.logger.info(`Found ${apps.length} template applications to start in dev mode: ${apps.map(a => a.name).join(", ")}`); // read the iocd.app.json and copy modifications apps.forEach(app => this.copyModifications(app, "dev")); this.startAppsInParallel(apps); this.logger.info('All template applications started in dev mode successfully!'); } /** * Build a specific application */ async buildApp(appConfig) { const appPath = (0, path_1.join)(process.cwd(), 'apps', appConfig.name); this.logger.info(`Building application ${appConfig.name}...`); try { // Check if build script exists const packageJsonPath = (0, path_1.join)(appPath, 'package.json'); const packageJson = JSON.parse((0, fs_1.readFileSync)(packageJsonPath, 'utf-8')); if (!packageJson.scripts?.build) { this.logger.warn(`No build script found for ${appConfig.name} - skipping`); return; } // Execute npm run build in the app directory this.logger.debug(`Executing: npm run build in ${appPath}`); (0, child_process_1.execSync)('npm run build', { cwd: appPath, stdio: 'pipe', encoding: 'utf-8' }); this.logger.info(`Application ${appConfig.name} built successfully!`); } catch (error) { this.logger.error(`Failed to build application ${appConfig.name}: ${error}`); throw new error_handler_1.CLIError(`Failed to build application: ${appConfig.name}. ${error}`, { code: error_handler_1.ErrorCode.BUILD_ERROR }); } } /** * Start a specific application in development mode */ async startAppInDevMode(appName) { const appPath = (0, path_1.join)(process.cwd(), 'apps', appName); this.logger.info(`Starting ${appName} in dev mode...`); try { // Check if build script exists const packageJsonPath = (0, path_1.join)(appPath, 'package.json'); const packageJson = JSON.parse((0, fs_1.readFileSync)(packageJsonPath, 'utf-8')); if (!packageJson.scripts?.build) { this.logger.warn(`No build script found for ${appName} - skipping`); return; } // Execute npm run dev in the app directory this.logger.debug(`Executing: npm run dev in ${appPath}`); (0, child_process_1.exec)('npm run dev', { cwd: appPath, encoding: 'utf-8' }); this.logger.info(`${appName} started successfully!`); } catch (error) { this.logger.error(`Failed to start ${appName}: ${error}`); throw new error_handler_1.CLIError(`Failed to start application: ${appName}. ${error}`, { code: error_handler_1.ErrorCode.BUILD_ERROR }); } } async getAllApps() { const appsDir = (0, path_1.join)(process.cwd(), 'apps'); if (!(0, fs_1.existsSync)(appsDir)) { this.logger.debug('No apps/ directory found - skipping app listing'); return []; } const apps = this.discoverApps(appsDir); if (apps.length === 0) { this.logger.debug('No applications found in apps/ directory'); return []; } return apps; } /** * Discover all app directories in the apps/ folder and return detailed app information */ discoverApps(appsDir) { try { return (0, fs_1.readdirSync)(appsDir) .map(item => { const appPath = (0, path_1.join)(appsDir, item); const isDirectory = (0, fs_1.statSync)(appPath).isDirectory(); if (!isDirectory) { return null; } const hasPackageJson = (0, fs_1.existsSync)((0, path_1.join)(appPath, 'package.json')); const iocdAppJsonPath = (0, path_1.join)(appPath, 'iocd.app.json'); const hasAppJson = (0, fs_1.existsSync)(iocdAppJsonPath); if (!hasPackageJson) { this.logger.debug(`Skipping ${item}: no package.json found`); return null; } if (!hasAppJson) { this.logger.debug(`Skipping ${item}: no iocd.app.json found`); return null; } try { // Parse the iocd.app.json configuration const configContent = (0, fs_1.readFileSync)(iocdAppJsonPath, 'utf-8'); const config = JSON.parse(configContent); this.logger.debug(`Discovered app: ${item} with config: ${JSON.stringify(config)}`); return { name: item, path: appPath, config: config }; } catch (error) { this.logger.error(`Failed to parse iocd.app.json for ${item}: ${error}`); return null; } }) .filter((app) => app !== null); } catch (error) { this.logger.error(`Error discovering apps: ${error}`); return []; } } /** * Install dependencies for a specific application */ async installApp(appConfig) { const appPath = appConfig.path; this.logger.info(`Installing dependencies for ${appConfig.name}...`); try { // Check if package.json exists const packageJsonPath = (0, path_1.join)(appPath, 'package.json'); if (!(0, fs_1.existsSync)(packageJsonPath)) { this.logger.warn(` No package.json found for ${appConfig.name} - skipping`); return; } // Execute npm install in the app directory this.logger.debug(`Executing: npm install in ${appPath}`); (0, child_process_1.execSync)('npm install', { cwd: appPath, stdio: 'pipe', encoding: 'utf-8' }); this.logger.info(`Dependencies for ${appConfig.name} installed successfully!`); } catch (error) { this.logger.error(`Failed to install dependencies for ${appConfig.name}: ${error}`); throw new error_handler_1.CLIError(`Failed to install dependencies for application: ${appConfig.name}. ${error}`, { code: error_handler_1.ErrorCode.BUILD_ERROR }); } } async startAppsInParallel(apps) { const tasks = apps.map(app => ({ command: 'npm run start', name: app.name, cwd: app.path })); this.logger.debug(JSON.stringify(tasks)); // Start all apps (0, concurrently_1.default)(tasks, { prefix: 'name', // add app name before each log line killOthersOn: ['failure', 'success'], // stop all if one fails restartTries: 0, }).result.then(() => console.log('All processes exited successfully!'), () => console.error('One of the processes failed')); } copyModifications(appConfig, mode) { const modificationsConfigs = appConfig.config[mode]?.modifications; if (!modificationsConfigs) { this.logger.warn(`No modifications found for ${appConfig.name} in ${mode} mode - skipping`); return; } this.logger.info(`Copying ${modificationsConfigs.length} modification(s) for ${appConfig.name} in ${mode} mode`); // Copy each modification for (const config of modificationsConfigs) { const srcPath = (0, path_1.join)(appConfig.path, config.source); const destPath = (0, path_1.join)(process.cwd(), config.destination); this.logger.debug(`Copying modification from ${srcPath} to ${destPath}`); try { // Check if source exists if (!(0, fs_1.existsSync)(srcPath)) { this.logger.warn(`Source path does not exist: ${srcPath} - skipping`); continue; } // Get source stats to determine if it's a file or directory const srcStats = (0, fs_1.statSync)(srcPath); if (srcStats.isFile()) { // Handle file copying this.copyFile(srcPath, destPath); } else if (srcStats.isDirectory()) { // Handle directory copying this.copyDirectory(srcPath, destPath); } else { this.logger.warn(`Source is neither file nor directory: ${srcPath} - skipping`); continue; } this.logger.debug(`Successfully copied ${config.source} to ${config.destination}`); } catch (error) { this.logger.error(`Failed to copy ${config.source} to ${config.destination}: ${error}`); throw new error_handler_1.CLIError(`Failed to copy modification for ${appConfig.name}: ${error}`, { code: error_handler_1.ErrorCode.FILE_SYSTEM_ERROR }); } } this.logger.info(`Modifications copied successfully!`); } /** * Copy a single file, creating destination directory if needed */ copyFile(srcPath, destPath) { // Ensure destination directory exists const destDir = (0, path_1.dirname)(destPath); if (!(0, fs_1.existsSync)(destDir)) { (0, fs_1.mkdirSync)(destDir, { recursive: true }); this.logger.debug(`Created destination directory: ${destDir}`); } // Copy the file (0, fs_1.copyFileSync)(srcPath, destPath); this.logger.debug(`Copied file: ${srcPath}${destPath}`); } /** * Copy a directory recursively, wiping out target first if it exists */ copyDirectory(srcPath, destPath) { // If destination exists, remove it completely if ((0, fs_1.existsSync)(destPath)) { this.logger.debug(`Removing existing destination directory: ${destPath}`); (0, fs_1.rmSync)(destPath, { recursive: true, force: true }); } // Create destination directory (0, fs_1.mkdirSync)(destPath, { recursive: true }); this.logger.debug(`Created destination directory: ${destPath}`); // Copy all contents recursively this.copyDirectoryContents(srcPath, destPath); } /** * Recursively copy directory contents */ copyDirectoryContents(srcDir, destDir) { const items = (0, fs_1.readdirSync)(srcDir); for (const item of items) { const srcItemPath = (0, path_1.join)(srcDir, item); const destItemPath = (0, path_1.join)(destDir, item); const itemStats = (0, fs_1.statSync)(srcItemPath); if (itemStats.isFile()) { (0, fs_1.copyFileSync)(srcItemPath, destItemPath); this.logger.debug(`Copied file: ${srcItemPath}${destItemPath}`); } else if (itemStats.isDirectory()) { (0, fs_1.mkdirSync)(destItemPath, { recursive: true }); this.copyDirectoryContents(srcItemPath, destItemPath); } } } } exports.AppService = AppService; //# sourceMappingURL=app.service.js.map