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

809 lines (808 loc) • 32.2 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; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.manageTemplates = manageTemplates; const chalk_1 = __importDefault(require("chalk")); const prompts_1 = __importDefault(require("prompts")); const path = __importStar(require("path")); const template_engine_1 = require("../utils/template-engine"); const config_1 = require("../utils/config"); const error_handler_1 = require("../utils/error-handler"); async function manageTemplates(options = {}) { const { spinner, verbose, json } = options; try { if (options.list) { await listTemplates(options, spinner); return; } if (options.create) { await createTemplate(options, spinner); return; } if (options.delete && options.template) { await deleteTemplate(options.template, options, spinner); return; } if (options.apply && options.template) { await applyTemplate(options.template, options, spinner); return; } if (options.show && options.template) { await showTemplate(options.template, options, spinner); return; } if (options.interactive) { await interactiveTemplateManagement(options, spinner); return; } // Default: list templates await listTemplates(options, spinner); } catch (error) { if (spinner) spinner.fail(chalk_1.default.red('Template operation failed')); throw error; } } async function listTemplates(options, spinner) { if (spinner) spinner.setText('Loading templates...'); const templates = await template_engine_1.templateEngine.listTemplates(); if (spinner) spinner.stop(); if (templates.length === 0) { console.log(chalk_1.default.yellow('āš ļø No templates found.')); console.log(chalk_1.default.gray('Create your first template with: re-shell template create')); return; } if (options.json) { console.log(JSON.stringify(templates, null, 2)); } else { console.log(chalk_1.default.cyan('\\nšŸ“‹ Available Configuration Templates')); console.log(chalk_1.default.gray('═'.repeat(50))); for (const template of templates) { console.log(chalk_1.default.cyan(`\\nšŸ“„ ${template.name} (v${template.version})`)); console.log(` ${template.description}`); if (template.tags.length > 0) { console.log(` ${chalk_1.default.gray('Tags:')} ${template.tags.map(tag => chalk_1.default.blue(tag)).join(', ')}`); } console.log(` ${chalk_1.default.gray('Variables:')} ${template.variables.length}`); if (template.author) { console.log(` ${chalk_1.default.gray('Author:')} ${template.author}`); } if (options.verbose) { console.log(` ${chalk_1.default.gray('Created:')} ${new Date(template.createdAt).toLocaleDateString()}`); console.log(` ${chalk_1.default.gray('Updated:')} ${new Date(template.updatedAt).toLocaleDateString()}`); } } console.log(chalk_1.default.gray(`\\nTotal: ${templates.length} template(s)`)); } } async function createTemplate(options, spinner) { if (spinner) spinner.stop(); const response = await (0, prompts_1.default)([ { type: 'select', name: 'source', message: 'How would you like to create the template?', choices: [ { title: 'šŸ“‹ From existing configuration file', value: 'file' }, { title: 'šŸ—ļø From project configuration', value: 'project' }, { title: 'šŸ¢ From workspace configuration', value: 'workspace' }, { title: '⚔ Quick template (built-in)', value: 'builtin' }, { title: 'āœļø Custom template (manual)', value: 'custom' } ] } ]); if (!response.source) return; switch (response.source) { case 'file': await createTemplateFromFile(options); break; case 'project': await createTemplateFromProject(options); break; case 'workspace': await createTemplateFromWorkspace(options); break; case 'builtin': await createBuiltinTemplate(options); break; case 'custom': await createCustomTemplate(options); break; } } async function createTemplateFromFile(options) { const response = await (0, prompts_1.default)([ { type: 'text', name: 'filePath', message: 'Path to configuration file:', validate: (value) => value.trim() ? true : 'File path is required' }, { type: 'text', name: 'templateName', message: 'Template name:', validate: (value) => { if (!value.trim()) return 'Template name is required'; if (!/^[a-z0-9-]+$/.test(value)) return 'Use lowercase letters, numbers, and hyphens only'; return true; } }, { type: 'text', name: 'description', message: 'Template description:', validate: (value) => value.trim() ? true : 'Description is required' } ]); if (!response.templateName) return; // For now, create a simple template without variables // In a real implementation, this would analyze the config file and suggest variables const variables = [ { name: 'name', type: 'string', description: 'Configuration name', required: true } ]; const template = await template_engine_1.templateEngine.createTemplate(response.templateName, { name: '${name}' }, // Simplified for demo variables, { description: response.description, version: '1.0.0', tags: ['custom', 'file-based'] }); console.log(chalk_1.default.green(`āœ… Template '${template.name}' created successfully!`)); } async function createTemplateFromProject(options) { const projectConfig = await config_1.configManager.loadProjectConfig(); if (!projectConfig) { console.log(chalk_1.default.yellow('āš ļø No project configuration found.')); return; } const response = await (0, prompts_1.default)([ { type: 'text', name: 'templateName', message: 'Template name:', initial: `${projectConfig.name}-template`, validate: (value) => { if (!value.trim()) return 'Template name is required'; if (!/^[a-z0-9-]+$/.test(value)) return 'Use lowercase letters, numbers, and hyphens only'; return true; } }, { type: 'text', name: 'description', message: 'Template description:', initial: `Template based on ${projectConfig.name} project configuration` }, { type: 'multiselect', name: 'variableFields', message: 'Which fields should be variables?', choices: [ { title: 'Project name', value: 'name', selected: true }, { title: 'Package manager', value: 'packageManager' }, { title: 'Framework', value: 'framework' }, { title: 'Development port', value: 'dev.port' }, { title: 'Coverage threshold', value: 'quality.coverage.threshold' } ] } ]); if (!response.templateName) return; // Create variables based on selection const variables = response.variableFields.map((field) => { switch (field) { case 'name': return { name: 'projectName', type: 'string', description: 'Name of the project', required: true, validation: { pattern: '^[a-z0-9-]+$' } }; case 'packageManager': return { name: 'packageManager', type: 'string', description: 'Package manager to use', default: projectConfig.packageManager, validation: { options: ['npm', 'yarn', 'pnpm', 'bun'] } }; case 'framework': return { name: 'framework', type: 'string', description: 'Framework to use', default: projectConfig.framework }; case 'dev.port': return { name: 'devPort', type: 'number', description: 'Development server port', default: projectConfig.dev?.port || 3000, validation: { min: 1000, max: 65535 } }; case 'quality.coverage.threshold': return { name: 'coverageThreshold', type: 'number', description: 'Code coverage threshold', default: projectConfig.quality?.coverage?.threshold || 80, validation: { min: 0, max: 100 } }; default: return { name: field.replace('.', '_'), type: 'string', description: `Configuration for ${field}`, required: false }; } }); // Create template config with variable substitutions const templateConfig = JSON.parse(JSON.stringify(projectConfig)); response.variableFields.forEach((field) => { switch (field) { case 'name': templateConfig.name = '${projectName}'; break; case 'packageManager': templateConfig.packageManager = '${packageManager}'; break; case 'framework': templateConfig.framework = '${framework}'; break; case 'dev.port': if (templateConfig.dev) templateConfig.dev.port = '${devPort}'; break; case 'quality.coverage.threshold': if (templateConfig.quality?.coverage) templateConfig.quality.coverage.threshold = '${coverageThreshold}'; break; } }); const template = await template_engine_1.templateEngine.createTemplate(response.templateName, templateConfig, variables, { description: response.description, version: '1.0.0', tags: ['project', projectConfig.framework, projectConfig.packageManager] }); console.log(chalk_1.default.green(`āœ… Template '${template.name}' created from project configuration!`)); } async function createTemplateFromWorkspace(options) { const response = await (0, prompts_1.default)([ { type: 'text', name: 'workspacePath', message: 'Workspace path:', initial: process.cwd(), validate: (value) => value.trim() ? true : 'Workspace path is required' } ]); if (!response.workspacePath) return; const workspaceConfig = await config_1.configManager.loadWorkspaceConfig(response.workspacePath); if (!workspaceConfig) { console.log(chalk_1.default.yellow('āš ļø No workspace configuration found at the specified path.')); return; } const templateResponse = await (0, prompts_1.default)([ { type: 'text', name: 'templateName', message: 'Template name:', initial: `${workspaceConfig.name}-workspace-template`, validate: (value) => { if (!value.trim()) return 'Template name is required'; if (!/^[a-z0-9-]+$/.test(value)) return 'Use lowercase letters, numbers, and hyphens only'; return true; } }, { type: 'text', name: 'description', message: 'Template description:', initial: `Template for ${workspaceConfig.type} workspace` } ]); if (!templateResponse.templateName) return; const variables = [ { name: 'workspaceName', type: 'string', description: 'Name of the workspace', required: true, validation: { pattern: '^[a-z0-9-]+$' } }, { name: 'framework', type: 'string', description: 'Framework for the workspace', default: workspaceConfig.framework } ]; const templateConfig = JSON.parse(JSON.stringify(workspaceConfig)); templateConfig.name = '${workspaceName}'; if (templateConfig.framework) templateConfig.framework = '${framework}'; const template = await template_engine_1.templateEngine.createTemplate(templateResponse.templateName, templateConfig, variables, { description: templateResponse.description, version: '1.0.0', tags: ['workspace', workspaceConfig.type, workspaceConfig.framework || 'generic'] }); console.log(chalk_1.default.green(`āœ… Template '${template.name}' created from workspace configuration!`)); } async function createBuiltinTemplate(options) { const response = await (0, prompts_1.default)([ { type: 'select', name: 'type', message: 'Choose a built-in template type:', choices: [ { title: 'šŸš€ React Project Template', value: 'react-project' }, { title: 'šŸŽÆ Vue Project Template', value: 'vue-project' }, { title: '⚔ Svelte Project Template', value: 'svelte-project' }, { title: 'šŸ“¦ Package Workspace Template', value: 'package-workspace' }, { title: 'šŸ—ļø App Workspace Template', value: 'app-workspace' }, { title: 'šŸ“š Library Workspace Template', value: 'lib-workspace' } ] }, { type: 'select', name: 'packageManager', message: 'Default package manager:', choices: [ { title: 'pnpm (recommended)', value: 'pnpm' }, { title: 'npm', value: 'npm' }, { title: 'yarn', value: 'yarn' }, { title: 'bun', value: 'bun' } ] } ]); if (!response.type) return; let template; switch (response.type) { case 'react-project': case 'vue-project': case 'svelte-project': const framework = response.type.split('-')[0]; template = template_engine_1.TemplateHelpers.createProjectTemplate(`${framework}-project`, `${framework}-ts`, response.packageManager); break; case 'package-workspace': case 'app-workspace': case 'lib-workspace': const workspaceType = response.type.split('-')[0]; template = template_engine_1.TemplateHelpers.createWorkspaceTemplate(workspaceType); break; default: throw new error_handler_1.ValidationError(`Unknown template type: ${response.type}`); } await template_engine_1.templateEngine.saveTemplate(template); console.log(chalk_1.default.green(`āœ… Built-in template '${template.name}' created successfully!`)); } async function createCustomTemplate(options) { console.log(chalk_1.default.cyan('\\nāœļø Custom Template Creator')); console.log(chalk_1.default.gray('Create a template from scratch with custom variables')); const response = await (0, prompts_1.default)([ { type: 'text', name: 'name', message: 'Template name:', validate: (value) => { if (!value.trim()) return 'Template name is required'; if (!/^[a-z0-9-]+$/.test(value)) return 'Use lowercase letters, numbers, and hyphens only'; return true; } }, { type: 'text', name: 'description', message: 'Template description:', validate: (value) => value.trim() ? true : 'Description is required' }, { type: 'text', name: 'version', message: 'Template version:', initial: '1.0.0' }, { type: 'list', name: 'tags', message: 'Tags (comma-separated):', separator: ',' } ]); if (!response.name) return; // Create variables interactively const variables = []; let addMoreVariables = true; while (addMoreVariables) { const varResponse = await (0, prompts_1.default)([ { type: 'text', name: 'name', message: `Variable ${variables.length + 1} name:`, validate: (value) => { if (!value.trim()) return 'Variable name is required'; if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(value)) return 'Use valid identifier (letters, numbers, underscore)'; return true; } }, { type: 'select', name: 'type', message: 'Variable type:', choices: [ { title: 'String', value: 'string' }, { title: 'Number', value: 'number' }, { title: 'Boolean', value: 'boolean' }, { title: 'Array', value: 'array' }, { title: 'Object', value: 'object' } ] }, { type: 'text', name: 'description', message: 'Variable description:', validate: (value) => value.trim() ? true : 'Description is required' }, { type: 'toggle', name: 'required', message: 'Is this variable required?', initial: false, active: 'yes', inactive: 'no' }, { type: 'text', name: 'defaultValue', message: 'Default value (optional):' }, { type: 'toggle', name: 'addMore', message: 'Add another variable?', initial: true, active: 'yes', inactive: 'no' } ]); if (varResponse.name) { const variable = { name: varResponse.name, type: varResponse.type, description: varResponse.description, required: varResponse.required }; if (varResponse.defaultValue) { try { variable.default = JSON.parse(varResponse.defaultValue); } catch { variable.default = varResponse.defaultValue; } } variables.push(variable); } addMoreVariables = varResponse.addMore; } // Create basic template structure const templateConfig = { name: '${name}', type: 'custom', // Add more fields based on variables }; const template = await template_engine_1.templateEngine.createTemplate(response.name, templateConfig, variables, { description: response.description, version: response.version, tags: response.tags || [] }); console.log(chalk_1.default.green(`āœ… Custom template '${template.name}' created successfully!`)); console.log(chalk_1.default.gray(`Edit the template file to customize the configuration structure.`)); } async function deleteTemplate(templateName, options, spinner) { if (spinner) spinner.setText(`Deleting template: ${templateName}`); const template = await template_engine_1.templateEngine.getTemplate(templateName); if (!template) { if (spinner) spinner.fail(chalk_1.default.red(`Template '${templateName}' not found`)); return; } if (spinner) spinner.stop(); const confirmation = await (0, prompts_1.default)([ { type: 'confirm', name: 'confirmed', message: `Are you sure you want to delete template '${templateName}'?`, initial: false } ]); if (!confirmation.confirmed) { console.log(chalk_1.default.yellow('Operation cancelled.')); return; } await template_engine_1.templateEngine.deleteTemplate(templateName); console.log(chalk_1.default.green(`āœ… Template '${templateName}' deleted successfully!`)); } async function applyTemplate(templateName, options, spinner) { if (spinner) spinner.setText(`Loading template: ${templateName}`); const template = await template_engine_1.templateEngine.getTemplate(templateName); if (!template) { if (spinner) spinner.fail(chalk_1.default.red(`Template '${templateName}' not found`)); return; } if (spinner) spinner.stop(); console.log(chalk_1.default.cyan(`\\nšŸŽÆ Applying Template: ${template.name}`)); console.log(chalk_1.default.gray(template.description)); // Collect variable values const variables = {}; if (options.variables) { // Parse variables from command line (JSON format) try { const parsed = JSON.parse(options.variables); Object.assign(variables, parsed); } catch (error) { throw new error_handler_1.ValidationError('Invalid variables JSON format'); } } else { // Interactive variable collection for (const varDef of template.variables) { const response = await (0, prompts_1.default)([ { type: varDef.type === 'boolean' ? 'toggle' : varDef.type === 'number' ? 'number' : 'text', name: 'value', message: `${varDef.description}:`, initial: varDef.default, validate: varDef.required ? (value) => value !== undefined && value !== '' ? true : `${varDef.name} is required` : undefined, active: varDef.type === 'boolean' ? 'yes' : undefined, inactive: varDef.type === 'boolean' ? 'no' : undefined } ]); if (response.value !== undefined) { variables[varDef.name] = response.value; } } } // Get project info for context const projectConfig = await config_1.configManager.loadProjectConfig(); const globalConfig = await config_1.configManager.loadGlobalConfig(); const context = { projectInfo: projectConfig ? { name: projectConfig.name, type: projectConfig.type, framework: projectConfig.framework, packageManager: projectConfig.packageManager } : undefined, userInfo: { name: globalConfig.user.name, email: globalConfig.user.email, organization: globalConfig.user.organization } }; // Render template const result = await template_engine_1.templateEngine.renderTemplate(templateName, variables, context); // Output result if (options.output) { // Save to file const outputPath = path.resolve(options.output); await require('fs-extra').writeFile(outputPath, JSON.stringify(result, null, 2)); console.log(chalk_1.default.green(`āœ… Configuration saved to: ${outputPath}`)); } else if (options.json) { console.log(JSON.stringify(result, null, 2)); } else { console.log(chalk_1.default.cyan('\\nšŸ“„ Generated Configuration:')); console.log(chalk_1.default.gray('═'.repeat(40))); console.log(JSON.stringify(result, null, 2)); } } async function showTemplate(templateName, options, spinner) { if (spinner) spinner.setText(`Loading template: ${templateName}`); const template = await template_engine_1.templateEngine.getTemplate(templateName); if (!template) { if (spinner) spinner.fail(chalk_1.default.red(`Template '${templateName}' not found`)); return; } if (spinner) spinner.stop(); if (options.json) { console.log(JSON.stringify(template, null, 2)); } else { console.log(chalk_1.default.cyan(`\\nšŸ“„ Template: ${template.name} (v${template.version})`)); console.log(chalk_1.default.gray('═'.repeat(50))); console.log(`\\n${chalk_1.default.cyan('Description:')} ${template.description}`); if (template.author) { console.log(`${chalk_1.default.cyan('Author:')} ${template.author}`); } if (template.tags.length > 0) { console.log(`${chalk_1.default.cyan('Tags:')} ${template.tags.map(tag => chalk_1.default.blue(tag)).join(', ')}`); } console.log(`${chalk_1.default.cyan('Created:')} ${new Date(template.createdAt).toLocaleDateString()}`); console.log(`${chalk_1.default.cyan('Updated:')} ${new Date(template.updatedAt).toLocaleDateString()}`); console.log(chalk_1.default.cyan('\\nšŸ“‹ Variables:')); template.variables.forEach((variable, index) => { console.log(` ${index + 1}. ${chalk_1.default.yellow(variable.name)} (${variable.type})`); console.log(` ${variable.description}`); if (variable.required) console.log(` ${chalk_1.default.red('Required')}`); if (variable.default !== undefined) console.log(` Default: ${variable.default}`); if (variable.validation) { const rules = []; if (variable.validation.pattern) rules.push(`Pattern: ${variable.validation.pattern}`); if (variable.validation.min !== undefined) rules.push(`Min: ${variable.validation.min}`); if (variable.validation.max !== undefined) rules.push(`Max: ${variable.validation.max}`); if (variable.validation.options) rules.push(`Options: ${variable.validation.options.join(', ')}`); if (rules.length > 0) console.log(` ${chalk_1.default.gray('Validation:')} ${rules.join(', ')}`); } console.log(''); }); if (options.verbose) { console.log(chalk_1.default.cyan('Template Structure:')); console.log(JSON.stringify(template.template, null, 2)); } } } async function interactiveTemplateManagement(options, spinner) { if (spinner) spinner.stop(); const response = await (0, prompts_1.default)([ { type: 'select', name: 'action', message: 'What would you like to do?', choices: [ { title: 'šŸ“‹ List templates', value: 'list' }, { title: 'šŸ†• Create new template', value: 'create' }, { title: 'šŸ‘ļø Show template details', value: 'show' }, { title: 'šŸŽÆ Apply template', value: 'apply' }, { title: 'šŸ—‘ļø Delete template', value: 'delete' } ] } ]); if (!response.action) return; switch (response.action) { case 'list': await listTemplates(options); break; case 'create': await createTemplate(options); break; case 'show': const templates = await template_engine_1.templateEngine.listTemplates(); if (templates.length === 0) { console.log(chalk_1.default.yellow('No templates available.')); return; } const showResponse = await (0, prompts_1.default)([ { type: 'select', name: 'template', message: 'Select template to show:', choices: templates.map(t => ({ title: `${t.name} - ${t.description}`, value: t.name })) } ]); if (showResponse.template) { await showTemplate(showResponse.template, options); } break; case 'apply': const applyTemplates = await template_engine_1.templateEngine.listTemplates(); if (applyTemplates.length === 0) { console.log(chalk_1.default.yellow('No templates available.')); return; } const applyResponse = await (0, prompts_1.default)([ { type: 'select', name: 'template', message: 'Select template to apply:', choices: applyTemplates.map(t => ({ title: `${t.name} - ${t.description}`, value: t.name })) } ]); if (applyResponse.template) { await applyTemplate(applyResponse.template, options); } break; case 'delete': const deleteTemplates = await template_engine_1.templateEngine.listTemplates(); if (deleteTemplates.length === 0) { console.log(chalk_1.default.yellow('No templates available.')); return; } const deleteResponse = await (0, prompts_1.default)([ { type: 'select', name: 'template', message: 'Select template to delete:', choices: deleteTemplates.map(t => ({ title: `${t.name} - ${t.description}`, value: t.name })) } ]); if (deleteResponse.template) { await deleteTemplate(deleteResponse.template, options); } break; } }