@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
JavaScript
;
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();
}