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