UNPKG

@re-shell/cli

Version:

Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja

523 lines (522 loc) 19.9 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.configManager = exports.ConfigManager = exports.CONFIG_PATHS = exports.DEFAULT_PROJECT_CONFIG = exports.DEFAULT_GLOBAL_CONFIG = void 0; exports.getGlobalConfig = getGlobalConfig; exports.getProjectConfig = getProjectConfig; exports.getMergedConfig = getMergedConfig; exports.initializeGlobalConfig = initializeGlobalConfig; const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const os = __importStar(require("os")); const yaml = __importStar(require("yaml")); const error_handler_1 = require("./error-handler"); // Schema validation const GLOBAL_CONFIG_SCHEMA = { version: 'string', packageManager: ['npm', 'yarn', 'pnpm', 'bun'], defaultFramework: 'string', defaultTemplate: 'string', presets: 'object', user: { name: 'string?', email: 'string?', organization: 'string?' }, cli: { autoUpdate: 'boolean', telemetry: 'boolean', verbose: 'boolean', theme: ['auto', 'light', 'dark'] }, paths: { templates: 'string', cache: 'string', plugins: 'string' }, plugins: { enabled: 'array', marketplace: { registry: 'string', autoUpdate: 'boolean' } } }; // Default configurations exports.DEFAULT_GLOBAL_CONFIG = { version: '1.0.0', packageManager: 'pnpm', defaultFramework: 'react-ts', defaultTemplate: 'blank', presets: {}, user: {}, cli: { autoUpdate: true, telemetry: true, verbose: false, theme: 'auto' }, paths: { templates: path.join(os.homedir(), '.re-shell', 'templates'), cache: path.join(os.homedir(), '.re-shell', 'cache'), plugins: path.join(os.homedir(), '.re-shell', 'plugins') }, plugins: { enabled: [], marketplace: { registry: 'https://registry.npmjs.org', autoUpdate: false } } }; exports.DEFAULT_PROJECT_CONFIG = { type: 'monorepo', packageManager: 'pnpm', framework: 'react-ts', template: 'blank', environments: { development: { name: 'development', variables: {}, build: { mode: 'development', optimization: false, sourcemaps: true }, deployment: {} }, staging: { name: 'staging', variables: {}, build: { mode: 'staging', optimization: true, sourcemaps: true }, deployment: {} }, production: { name: 'production', variables: {}, build: { mode: 'production', optimization: true, sourcemaps: false }, deployment: {} } }, workspaces: { root: '.', patterns: ['apps/*', 'packages/*', 'libs/*', 'tools/*'], types: ['app', 'package', 'lib', 'tool'] }, git: { submodules: true, hooks: true, conventionalCommits: true }, build: { target: 'es2020', optimize: true, analyze: false }, dev: { port: 3000, host: 'localhost', open: false, hmr: true }, quality: { linting: true, testing: true, coverage: { enabled: true, threshold: 80 }, security: { enabled: true, autoFix: false } } }; // Path utilities exports.CONFIG_PATHS = { GLOBAL_DIR: path.join(os.homedir(), '.re-shell'), GLOBAL_CONFIG: path.join(os.homedir(), '.re-shell', 'config.yaml'), PROJECT_CONFIG: '.re-shell/config.yaml', WORKSPACE_CONFIG: 're-shell.workspaces.yaml', WORKSPACE_DIR_CONFIG: '.re-shell/workspace.yaml' }; // Configuration manager class class ConfigManager { constructor() { this.globalConfig = null; this.projectConfig = null; } // Global configuration management async loadGlobalConfig() { if (this.globalConfig) { return this.globalConfig; } try { if (await fs.pathExists(exports.CONFIG_PATHS.GLOBAL_CONFIG)) { const content = await fs.readFile(exports.CONFIG_PATHS.GLOBAL_CONFIG, 'utf8'); const config = yaml.parse(content); this.validateGlobalConfig(config); this.globalConfig = config; return config; } } catch (error) { throw new error_handler_1.ValidationError(`Failed to load global config: ${error.message}`); } // Create default config if none exists this.globalConfig = exports.DEFAULT_GLOBAL_CONFIG; await this.saveGlobalConfig(this.globalConfig); return this.globalConfig; } async saveGlobalConfig(config) { try { await fs.ensureDir(exports.CONFIG_PATHS.GLOBAL_DIR); this.validateGlobalConfig(config); const content = yaml.stringify(config); await fs.writeFile(exports.CONFIG_PATHS.GLOBAL_CONFIG, content, 'utf8'); this.globalConfig = config; } catch (error) { throw new error_handler_1.ValidationError(`Failed to save global config: ${error.message}`); } } async updateGlobalConfig(updates) { const config = await this.loadGlobalConfig(); const updatedConfig = this.mergeConfig(config, updates); await this.saveGlobalConfig(updatedConfig); return updatedConfig; } // Project configuration management async loadProjectConfig(projectPath = process.cwd()) { const configPath = path.join(projectPath, exports.CONFIG_PATHS.PROJECT_CONFIG); try { if (await fs.pathExists(configPath)) { const content = await fs.readFile(configPath, 'utf8'); const config = yaml.parse(content); this.validateProjectConfig(config); return config; } } catch (error) { throw new error_handler_1.ValidationError(`Failed to load project config: ${error.message}`); } return null; } async saveProjectConfig(config, projectPath = process.cwd()) { try { const configDir = path.join(projectPath, '.re-shell'); const configPath = path.join(projectPath, exports.CONFIG_PATHS.PROJECT_CONFIG); await fs.ensureDir(configDir); this.validateProjectConfig(config); const content = yaml.stringify(config); await fs.writeFile(configPath, content, 'utf8'); this.projectConfig = config; } catch (error) { throw new error_handler_1.ValidationError(`Failed to save project config: ${error.message}`); } } async createProjectConfig(name, options = {}, projectPath = process.cwd()) { const globalConfig = await this.loadGlobalConfig(); const config = { name, version: '1.0.0', ...exports.DEFAULT_PROJECT_CONFIG, ...options }; // Apply global defaults config.packageManager = options.packageManager || globalConfig.packageManager; config.framework = options.framework || globalConfig.defaultFramework; config.template = options.template || globalConfig.defaultTemplate; await this.saveProjectConfig(config, projectPath); return config; } // Workspace configuration management async loadWorkspaceConfig(workspacePath) { const configPath = path.join(workspacePath, exports.CONFIG_PATHS.WORKSPACE_DIR_CONFIG); try { if (await fs.pathExists(configPath)) { const content = await fs.readFile(configPath, 'utf8'); const config = yaml.parse(content); this.validateWorkspaceConfig(config); return config; } } catch (error) { throw new error_handler_1.ValidationError(`Failed to load workspace config: ${error.message}`); } return null; } async saveWorkspaceConfig(config, workspacePath) { try { const configDir = path.join(workspacePath, '.re-shell'); const configPath = path.join(workspacePath, exports.CONFIG_PATHS.WORKSPACE_DIR_CONFIG); await fs.ensureDir(configDir); this.validateWorkspaceConfig(config); const content = yaml.stringify(config); await fs.writeFile(configPath, content, 'utf8'); } catch (error) { throw new error_handler_1.ValidationError(`Failed to save workspace config: ${error.message}`); } } async createWorkspaceConfig(name, type, options = {}, workspacePath) { const config = { name, type, ...options }; await this.saveWorkspaceConfig(config, workspacePath); return config; } // Configuration merging with inheritance (global → project → workspace) async getMergedConfig(projectPath = process.cwd()) { const globalConfig = await this.loadGlobalConfig(); const projectConfig = await this.loadProjectConfig(projectPath); // Start with defaults let merged = { ...exports.DEFAULT_PROJECT_CONFIG }; // Apply global config inheritance if (globalConfig) { merged = { ...merged, packageManager: globalConfig.packageManager, framework: globalConfig.defaultFramework, template: globalConfig.defaultTemplate, }; } // Apply project-specific config (overrides global) if (projectConfig) { merged = this.mergeConfig(merged, projectConfig); } return { global: globalConfig, project: projectConfig, merged: merged }; } // Enhanced configuration merging including workspace config async getMergedWorkspaceConfig(workspacePath, projectPath = process.cwd()) { const { global, project, merged } = await this.getMergedConfig(projectPath); const workspaceConfig = await this.loadWorkspaceConfig(workspacePath); const workspaceMerged = { ...merged }; // Apply workspace-specific config (overrides project and global) if (workspaceConfig) { // Merge workspace settings into the result if (workspaceConfig.packageManager) workspaceMerged.packageManager = workspaceConfig.packageManager; if (workspaceConfig.framework) workspaceMerged.framework = workspaceConfig.framework; if (workspaceConfig.template) workspaceMerged.template = workspaceConfig.template; // Deep merge complex objects if (workspaceConfig.build) { workspaceMerged.build = this.mergeConfig(workspaceMerged.build || {}, workspaceConfig.build); } if (workspaceConfig.dev) { workspaceMerged.dev = this.mergeConfig(workspaceMerged.dev || {}, workspaceConfig.dev); } if (workspaceConfig.quality) { workspaceMerged.quality = this.mergeConfig(workspaceMerged.quality || {}, workspaceConfig.quality); } } return { global, project, workspace: workspaceConfig, merged: workspaceMerged }; } // Preset management async savePreset(name, config) { const globalConfig = await this.loadGlobalConfig(); const preset = { name, description: `Preset for ${name}`, config, tags: [], createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }; globalConfig.presets[name] = preset; await this.saveGlobalConfig(globalConfig); } async loadPreset(name) { const globalConfig = await this.loadGlobalConfig(); return globalConfig.presets[name] || null; } async listPresets() { const globalConfig = await this.loadGlobalConfig(); return Object.values(globalConfig.presets); } async deletePreset(name) { const globalConfig = await this.loadGlobalConfig(); delete globalConfig.presets[name]; await this.saveGlobalConfig(globalConfig); } // Configuration migration async migrateConfig(fromVersion, toVersion) { // Implementation for config migrations between versions // This will be expanded as the config schema evolves console.log(`Migrating config from ${fromVersion} to ${toVersion}`); } // Validation methods validateGlobalConfig(config) { const { validateGlobalConfig } = require('./validation'); const result = validateGlobalConfig(config); if (!result.valid) { const errorMessages = result.errors .filter((e) => e.severity === 'error') .map((e) => `${e.field}: ${e.message}`) .join('; '); throw new error_handler_1.ValidationError(`Global configuration validation failed: ${errorMessages}`); } } validateProjectConfig(config) { const { validateProjectConfig } = require('./validation'); const result = validateProjectConfig(config); if (!result.valid) { const errorMessages = result.errors .filter((e) => e.severity === 'error') .map((e) => `${e.field}: ${e.message}`) .join('; '); throw new error_handler_1.ValidationError(`Project configuration validation failed: ${errorMessages}`); } } validateWorkspaceConfig(config) { // Basic workspace config validation if (!config.name || typeof config.name !== 'string') { throw new error_handler_1.ValidationError('Workspace configuration must have a valid name'); } if (!config.type || !['app', 'package', 'lib', 'tool'].includes(config.type)) { throw new error_handler_1.ValidationError('Workspace configuration must have a valid type: app, package, lib, or tool'); } if (config.packageManager && !['npm', 'yarn', 'pnpm', 'bun'].includes(config.packageManager)) { throw new error_handler_1.ValidationError('Invalid package manager specified in workspace configuration'); } } validateSchema(obj, schema, context) { // Basic schema validation - can be expanded with a proper validation library for (const [key, type] of Object.entries(schema)) { if (typeof type === 'string') { const isOptional = type.endsWith('?'); const expectedType = isOptional ? type.slice(0, -1) : type; if (!isOptional && !(key in obj)) { throw new error_handler_1.ValidationError(`${context}: Missing required field '${key}'`); } if (key in obj && typeof obj[key] !== expectedType) { throw new error_handler_1.ValidationError(`${context}: Field '${key}' must be of type ${expectedType}`); } } else if (type === 'array') { if (key in obj && !Array.isArray(obj[key])) { throw new error_handler_1.ValidationError(`${context}: Field '${key}' must be of type array`); } } else if (Array.isArray(type)) { if (key in obj && !type.includes(obj[key])) { throw new error_handler_1.ValidationError(`${context}: Field '${key}' must be one of: ${type.join(', ')}`); } } else if (typeof type === 'object') { if (key in obj) { this.validateSchema(obj[key], type, `${context}.${key}`); } } } } // Deep merge utility mergeConfig(base, override) { const result = { ...base }; for (const [key, value] of Object.entries(override)) { if (value !== null && typeof value === 'object' && !Array.isArray(value)) { result[key] = this.mergeConfig(result[key] || {}, value); } else { result[key] = value; } } return result; } // Configuration backup and restore async backupConfig() { const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const backupDir = path.join(exports.CONFIG_PATHS.GLOBAL_DIR, 'backups'); const backupPath = path.join(backupDir, `config-backup-${timestamp}.yaml`); await fs.ensureDir(backupDir); const globalConfig = await this.loadGlobalConfig(); const content = yaml.stringify(globalConfig); await fs.writeFile(backupPath, content, 'utf8'); return backupPath; } async restoreConfig(backupPath) { if (!await fs.pathExists(backupPath)) { throw new error_handler_1.ValidationError(`Backup file not found: ${backupPath}`); } const content = await fs.readFile(backupPath, 'utf8'); const config = yaml.parse(content); await this.saveGlobalConfig(config); } } exports.ConfigManager = ConfigManager; // Export singleton instance exports.configManager = new ConfigManager(); // Helper functions for easy access async function getGlobalConfig() { return exports.configManager.loadGlobalConfig(); } async function getProjectConfig(projectPath) { return exports.configManager.loadProjectConfig(projectPath); } async function getMergedConfig(projectPath) { return exports.configManager.getMergedConfig(projectPath); } async function initializeGlobalConfig() { const configDir = exports.CONFIG_PATHS.GLOBAL_DIR; // Ensure directories exist await fs.ensureDir(configDir); await fs.ensureDir(path.join(configDir, 'templates')); await fs.ensureDir(path.join(configDir, 'cache')); await fs.ensureDir(path.join(configDir, 'plugins')); await fs.ensureDir(path.join(configDir, 'backups')); return exports.configManager.loadGlobalConfig(); }