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

671 lines (670 loc) 23.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.UserPreferenceManager = void 0; exports.createUserPreferenceManager = createUserPreferenceManager; exports.getGlobalUserPreferences = getGlobalUserPreferences; exports.setGlobalUserPreferences = setGlobalUserPreferences; const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const os = __importStar(require("os")); const events_1 = require("events"); class UserPreferenceManager extends events_1.EventEmitter { constructor() { super(); this.updateHistory = []; const configDir = path.join(os.homedir(), '.re-shell'); this.globalConfigPath = path.join(configDir, 'preferences.json'); this.userProfilesPath = path.join(configDir, 'profiles'); this.schema = this.createSchema(); this.loadOrCreateProfile(); } createSchema() { return { 'cli.packageManager': { type: 'string', default: 'auto', description: 'Default package manager for new projects', choices: ['npm', 'yarn', 'pnpm', 'bun', 'auto'], category: 'CLI Behavior' }, 'cli.defaultFramework': { type: 'string', default: 'react', description: 'Default framework for new microfrontends', choices: ['react', 'vue', 'svelte', 'angular', 'vanilla'], category: 'CLI Behavior' }, 'cli.typescript': { type: 'boolean', default: true, description: 'Use TypeScript by default', category: 'CLI Behavior' }, 'cli.skipConfirmations': { type: 'boolean', default: false, description: 'Skip confirmation prompts when possible', category: 'CLI Behavior' }, 'cli.verboseOutput': { type: 'boolean', default: false, description: 'Enable verbose output by default', category: 'CLI Behavior' }, 'cli.colorOutput': { type: 'boolean', default: true, description: 'Enable colored terminal output', category: 'CLI Behavior' }, 'cli.autoUpdate': { type: 'boolean', default: true, description: 'Automatically check for CLI updates', category: 'CLI Behavior' }, 'cli.telemetry': { type: 'boolean', default: true, description: 'Allow anonymous usage telemetry', category: 'CLI Behavior' }, 'development.defaultTemplate': { type: 'string', default: 'basic', description: 'Default project template', choices: ['basic', 'ecommerce', 'dashboard', 'saas', 'blog'], category: 'Development' }, 'development.autoInstallDependencies': { type: 'boolean', default: true, description: 'Automatically install dependencies after project creation', category: 'Development' }, 'development.generateDocumentation': { type: 'boolean', default: true, description: 'Generate README and documentation files', category: 'Development' }, 'development.setupTesting': { type: 'boolean', default: true, description: 'Set up testing framework and files', category: 'Development' }, 'development.setupLinting': { type: 'boolean', default: true, description: 'Configure ESLint and code quality tools', category: 'Development' }, 'development.setupFormatting': { type: 'boolean', default: true, description: 'Configure Prettier and code formatting', category: 'Development' }, 'development.gitInitialization': { type: 'boolean', default: true, description: 'Initialize Git repository for new projects', category: 'Development' }, 'development.dockerConfiguration': { type: 'boolean', default: false, description: 'Generate Docker configuration files', category: 'Development' }, 'editor.preferred': { type: 'string', default: 'vscode', description: 'Preferred code editor', choices: ['vscode', 'webstorm', 'atom', 'sublime', 'vim', 'none'], category: 'Editor' }, 'editor.generateConfig': { type: 'boolean', default: true, description: 'Generate editor-specific configuration files', category: 'Editor' }, 'editor.installExtensions': { type: 'boolean', default: false, description: 'Suggest installing recommended extensions', category: 'Editor', advanced: true }, 'project.workspaceLayout': { type: 'string', default: 'monorepo', description: 'Default workspace layout structure', choices: ['nested', 'flat', 'monorepo'], category: 'Project Structure' }, 'project.namingConvention': { type: 'string', default: 'kebab-case', description: 'File and directory naming convention', choices: ['camelCase', 'kebab-case', 'snake_case', 'PascalCase'], category: 'Project Structure' }, 'build.bundler': { type: 'string', default: 'auto', description: 'Preferred build tool', choices: ['webpack', 'vite', 'rollup', 'parcel', 'auto'], category: 'Build & Deploy' }, 'build.target': { type: 'string', default: 'es2020', description: 'JavaScript target version', choices: ['es2015', 'es2018', 'es2020', 'esnext'], category: 'Build & Deploy' }, 'performance.monitoring': { type: 'boolean', default: false, description: 'Enable performance monitoring', category: 'Performance', advanced: true }, 'performance.caching': { type: 'boolean', default: true, description: 'Enable build and operation caching', category: 'Performance' }, 'performance.memoryLimit': { type: 'number', default: 2048, description: 'Memory limit for build processes (MB)', min: 512, max: 8192, category: 'Performance', advanced: true }, 'plugins.autoInstall': { type: 'boolean', default: false, description: 'Automatically install recommended plugins', category: 'Plugins', advanced: true }, 'plugins.autoUpdate': { type: 'boolean', default: true, description: 'Automatically update installed plugins', category: 'Plugins' }, 'ui.theme': { type: 'string', default: 'auto', description: 'CLI output theme', choices: ['auto', 'light', 'dark'], category: 'UI/UX' }, 'ui.animations': { type: 'boolean', default: true, description: 'Enable loading animations and spinners', category: 'UI/UX' } }; } loadOrCreateProfile() { try { if (fs.existsSync(this.globalConfigPath)) { const data = fs.readJsonSync(this.globalConfigPath); this.currentProfile = this.migrateProfile(data); } else { this.currentProfile = this.createDefaultProfile(); this.saveProfile(); } } catch (error) { console.warn('Failed to load user preferences, using defaults:', error); this.currentProfile = this.createDefaultProfile(); } } createDefaultProfile() { const preferences = this.createDefaultPreferences(); return { id: this.generateProfileId(), name: 'Default', preferences, metadata: { created: new Date(), lastUpdated: new Date(), version: '1.0.0' } }; } createDefaultPreferences() { const preferences = { cli: {}, development: {}, editor: {}, project: {}, build: {}, ui: {}, performance: {}, plugins: {}, custom: {} }; // Apply schema defaults for (const [key, config] of Object.entries(this.schema)) { this.setNestedValue(preferences, key, config.default); } return preferences; } migrateProfile(data) { // Handle migration from older versions if (!data.metadata) { data.metadata = { created: new Date(), lastUpdated: new Date(), version: '1.0.0' }; } if (!data.preferences) { data.preferences = this.createDefaultPreferences(); } // Merge with defaults to ensure all properties exist const defaultPrefs = this.createDefaultPreferences(); data.preferences = this.mergeDeep(defaultPrefs, data.preferences); return data; } generateProfileId() { return `profile_${Date.now()}_${Math.random().toString(36).substr(2, 8)}`; } mergeDeep(target, source) { const result = { ...target }; for (const key in source) { if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) { result[key] = this.mergeDeep(target[key] || {}, source[key]); } else { result[key] = source[key]; } } return result; } get(key, defaultValue) { if (!this.currentProfile) { return defaultValue; } const value = this.getNestedValue(this.currentProfile.preferences, key); return value !== undefined ? value : defaultValue; } set(key, value, source = 'user') { if (!this.currentProfile) { throw new Error('No active profile'); } // Validate against schema const schemaConfig = this.schema[key]; if (schemaConfig) { const validation = this.validateValue(value, schemaConfig); if (validation !== true) { throw new Error(`Invalid value for ${key}: ${validation}`); } } const oldValue = this.getNestedValue(this.currentProfile.preferences, key); this.setNestedValue(this.currentProfile.preferences, key, value); // Record update const update = { key, oldValue, newValue: value, timestamp: new Date(), source }; this.updateHistory.push(update); // Keep only last 100 updates if (this.updateHistory.length > 100) { this.updateHistory = this.updateHistory.slice(-100); } // Update metadata this.currentProfile.metadata.lastUpdated = new Date(); // Save and emit this.saveProfile(); this.emit('preference:changed', update); } validateValue(value, config) { // Type validation const actualType = Array.isArray(value) ? 'array' : typeof value; if (actualType !== config.type) { return `Expected ${config.type}, got ${actualType}`; } // Choice validation if (config.choices && !config.choices.includes(value)) { return `Must be one of: ${config.choices.join(', ')}`; } // Range validation if (config.type === 'number') { if (config.min !== undefined && value < config.min) { return `Must be at least ${config.min}`; } if (config.max !== undefined && value > config.max) { return `Must be at most ${config.max}`; } } // Custom validation if (config.validator) { const result = config.validator(value); if (result !== true) { return result; } } return true; } getNestedValue(obj, key) { const keys = key.split('.'); let current = obj; for (const k of keys) { if (current && typeof current === 'object' && k in current) { current = current[k]; } else { return undefined; } } return current; } setNestedValue(obj, key, value) { const keys = key.split('.'); let current = obj; for (let i = 0; i < keys.length - 1; i++) { const k = keys[i]; if (!(k in current) || typeof current[k] !== 'object') { current[k] = {}; } current = current[k]; } current[keys[keys.length - 1]] = value; } has(key) { return this.get(key) !== undefined; } delete(key) { if (!this.currentProfile) return; const oldValue = this.getNestedValue(this.currentProfile.preferences, key); this.deleteNestedValue(this.currentProfile.preferences, key); const update = { key, oldValue, newValue: undefined, timestamp: new Date(), source: 'user' }; this.updateHistory.push(update); this.currentProfile.metadata.lastUpdated = new Date(); this.saveProfile(); this.emit('preference:deleted', update); } deleteNestedValue(obj, key) { const keys = key.split('.'); let current = obj; for (let i = 0; i < keys.length - 1; i++) { const k = keys[i]; if (!(k in current) || typeof current[k] !== 'object') { return; } current = current[k]; } delete current[keys[keys.length - 1]]; } reset(category) { if (!this.currentProfile) return; if (category) { // Reset specific category const defaultPrefs = this.createDefaultPreferences(); const categoryDefaults = this.getNestedValue(defaultPrefs, category); if (categoryDefaults) { this.setNestedValue(this.currentProfile.preferences, category, categoryDefaults); } } else { // Reset all preferences this.currentProfile.preferences = this.createDefaultPreferences(); } this.currentProfile.metadata.lastUpdated = new Date(); this.saveProfile(); this.emit('preferences:reset', { category }); } getAll() { return this.currentProfile ? { ...this.currentProfile.preferences } : this.createDefaultPreferences(); } getCurrentProfile() { return this.currentProfile ? { ...this.currentProfile } : undefined; } createProfile(name, basedOn) { const baseProfile = basedOn ? this.loadProfile(basedOn) : this.currentProfile; const preferences = baseProfile ? { ...baseProfile.preferences } : this.createDefaultPreferences(); const profile = { id: this.generateProfileId(), name, preferences, metadata: { created: new Date(), lastUpdated: new Date(), version: '1.0.0' } }; this.saveProfileToFile(profile); return profile; } loadProfile(id) { try { const profilePath = path.join(this.userProfilesPath, `${id}.json`); if (fs.existsSync(profilePath)) { return fs.readJsonSync(profilePath); } } catch (error) { console.warn(`Failed to load profile ${id}:`, error); } return null; } switchProfile(id) { const profile = this.loadProfile(id); if (profile) { this.currentProfile = profile; this.saveProfile(); this.emit('profile:switched', profile); return true; } return false; } listProfiles() { try { if (!fs.existsSync(this.userProfilesPath)) { return []; } const files = fs.readdirSync(this.userProfilesPath) .filter(f => f.endsWith('.json')); const profiles = []; for (const file of files) { try { const profile = fs.readJsonSync(path.join(this.userProfilesPath, file)); profiles.push({ id: profile.id, name: profile.name, lastUpdated: new Date(profile.metadata.lastUpdated) }); } catch { // Skip corrupted profiles } } return profiles.sort((a, b) => b.lastUpdated.getTime() - a.lastUpdated.getTime()); } catch { return []; } } deleteProfile(id) { try { const profilePath = path.join(this.userProfilesPath, `${id}.json`); if (fs.existsSync(profilePath)) { fs.unlinkSync(profilePath); this.emit('profile:deleted', { id }); return true; } } catch (error) { console.warn(`Failed to delete profile ${id}:`, error); } return false; } exportProfile(id) { const profile = id ? this.loadProfile(id) : this.currentProfile; if (!profile) { throw new Error('Profile not found'); } return JSON.stringify(profile, null, 2); } importProfile(data) { try { const profile = JSON.parse(data); // Validate and migrate const migrated = this.migrateProfile(profile); migrated.id = this.generateProfileId(); // Generate new ID migrated.metadata.lastUpdated = new Date(); this.saveProfileToFile(migrated); this.emit('profile:imported', migrated); return migrated; } catch (error) { throw new Error(`Failed to import profile: ${error.message}`); } } saveProfile() { if (!this.currentProfile) return; try { fs.ensureDirSync(path.dirname(this.globalConfigPath)); fs.writeJsonSync(this.globalConfigPath, this.currentProfile, { spaces: 2 }); this.saveProfileToFile(this.currentProfile); } catch (error) { console.warn('Failed to save user preferences:', error); } } saveProfileToFile(profile) { try { fs.ensureDirSync(this.userProfilesPath); const profilePath = path.join(this.userProfilesPath, `${profile.id}.json`); fs.writeJsonSync(profilePath, profile, { spaces: 2 }); } catch (error) { console.warn(`Failed to save profile ${profile.id}:`, error); } } getSchema() { return { ...this.schema }; } getSchemaByCategory(category) { const filtered = {}; for (const [key, config] of Object.entries(this.schema)) { if (config.category === category) { filtered[key] = config; } } return filtered; } getCategories() { const categories = new Set(); for (const config of Object.values(this.schema)) { categories.add(config.category); } return Array.from(categories).sort(); } getUpdateHistory() { return [...this.updateHistory]; } // Utility methods for common preferences getPackageManager() { return this.get('cli.packageManager', 'auto'); } getDefaultFramework() { return this.get('cli.defaultFramework', 'react'); } isTypescriptDefault() { return this.get('cli.typescript', true); } shouldSkipConfirmations() { return this.get('cli.skipConfirmations', false); } isVerboseOutput() { return this.get('cli.verboseOutput', false); } isColorOutputEnabled() { return this.get('cli.colorOutput', true); } isTelemetryEnabled() { return this.get('cli.telemetry', true); } getPreferredEditor() { return this.get('editor.preferred', 'vscode'); } shouldAutoInstallDependencies() { return this.get('development.autoInstallDependencies', true); } } exports.UserPreferenceManager = UserPreferenceManager; // Global user preference manager let globalUserPreferences = null; function createUserPreferenceManager() { return new UserPreferenceManager(); } function getGlobalUserPreferences() { if (!globalUserPreferences) { globalUserPreferences = new UserPreferenceManager(); } return globalUserPreferences; } function setGlobalUserPreferences(preferences) { globalUserPreferences = preferences; }