From 8688a56b39c154633f92be748c26f0f6f7f74f28 Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Thu, 5 Feb 2026 19:31:44 +0000 Subject: [PATCH] Initial upload with CI/CD workflow --- src/commands/template.ts | 310 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 310 insertions(+) create mode 100644 src/commands/template.ts diff --git a/src/commands/template.ts b/src/commands/template.ts new file mode 100644 index 0000000..9972f50 --- /dev/null +++ b/src/commands/template.ts @@ -0,0 +1,310 @@ +import inquirer from 'inquirer'; +import { fileUtils, templateUtils } from '../utils'; +import { Template, Layout } from '../models/types'; + +interface TemplateOptions { + name?: string; + list?: boolean; + create?: boolean; + delete?: boolean; + apply?: string; + variables?: Record; +} + +export async function manageTemplates(options: TemplateOptions): Promise { + await fileUtils.initialize(); + + if (options.list) { + await listTemplates(); + return; + } + + if (options.delete) { + await deleteTemplate(options.name!); + return; + } + + if (options.create) { + await createTemplateInteractive(); + return; + } + + if (options.apply) { + await applyTemplate(options.apply, options.variables); + return; + } + + await listTemplatesInteractive(); +} + +async function listTemplates(): Promise { + const templates = await fileUtils.listTemplates(); + if (templates.length === 0) { + console.log('No templates found. Use "template --create" to create one.'); + return; + } + + console.log('Available Templates:'); + console.log('-'.repeat(50)); + + for (const templateFile of templates) { + try { + const template = await fileUtils.readTemplate(templateFile) as Template; + console.log(` ${template.displayName}`); + console.log(` Name: ${template.name}`); + console.log(` Description: ${template.description}`); + console.log(` Variables: ${template.variables.length}`); + console.log(''); + } catch { + console.log(` ${templateFile} (error reading)`); + } + } +} + +async function listTemplatesInteractive(): Promise { + const templates = await fileUtils.listTemplates(); + if (templates.length === 0) { + console.log('No templates found. Use "template --create" to create one.'); + return; + } + + const { selectedTemplate } = await inquirer.prompt([ + { + type: 'list', + name: 'selectedTemplate', + message: 'Select a template:', + choices: [ + ...templates.map((t) => ({ name: t.replace(/\.(json|yaml|yml)$/, ''), value: t })), + new inquirer.Separator(), + { name: 'Create new template', value: '__create__' }, + ], + }, + ]); + + if (selectedTemplate === '__create__') { + await createTemplateInteractive(); + return; + } + + await showTemplateDetails(selectedTemplate); +} + +async function showTemplateDetails(templateFile: string): Promise { + const template = await fileUtils.readTemplate(templateFile) as Template; + + console.log('\nTemplate Details:'); + console.log(` Name: ${template.name}`); + console.log(` Display Name: ${template.displayName}`); + console.log(` Description: ${template.description}`); + console.log(` Variables: ${template.variables.length}`); + + if (template.variables.length > 0) { + console.log('\nVariables:'); + for (const variable of template.variables) { + const required = variable.required ? ' (required)' : ''; + const defaultValue = variable.defaultValue ? ` = ${variable.defaultValue}` : ''; + console.log(` - ${variable.name}${required}${defaultValue}`); + console.log(` ${variable.description}`); + } + } + + const { action } = await inquirer.prompt([ + { + type: 'list', + name: 'action', + message: 'What would you like to do?', + choices: [ + { name: 'Apply template', value: 'apply' }, + { name: 'View layout', value: 'view' }, + { name: 'Back', value: 'back' }, + ], + }, + ]); + + if (action === 'apply') { + await applyTemplate(template.name); + } else if (action === 'view') { + console.log('\nTemplate Layout:'); + console.log(JSON.stringify(template.layout, null, 2)); + } +} + +async function createTemplateInteractive(): Promise { + const { templateName, displayName, description } = await inquirer.prompt([ + { + type: 'input', + name: 'templateName', + message: 'Enter template name (kebab-case):', + validate: (input: string) => /^[a-z0-9-]+$/.test(input) || 'Use kebab-case (lowercase with hyphens)', + }, + { + type: 'input', + name: 'displayName', + message: 'Enter display name:', + }, + { + type: 'input', + name: 'description', + message: 'Enter description:', + }, + ]); + + const layouts = await fileUtils.listLayouts(); + if (layouts.length === 0) { + console.log('No layouts found. Create a layout first, then save it as a template.'); + return; + } + + const { sourceLayout } = await inquirer.prompt([ + { + type: 'list', + name: 'sourceLayout', + message: 'Select a layout to use as template:', + choices: layouts, + }, + ]); + + const layout = await fileUtils.readLayout(sourceLayout) as Layout; + + const variables: Record = {}; + const { addVariable } = await inquirer.prompt([ + { + type: 'confirm', + name: 'addVariable', + message: 'Would you like to add template variables?', + default: false, + }, + ]); + + if (addVariable) { + const variableList: Array<{ name: string; description: string; defaultValue: string }> = []; + + // eslint-disable-next-line no-constant-condition + while (true) { + const { name } = await inquirer.prompt([ + { + type: 'input', + name: 'name', + message: 'Enter variable name (e.g., PROJECT_DIR):', + }, + ]); + + const { description } = await inquirer.prompt([ + { + type: 'input', + name: 'description', + message: 'Enter variable description:', + }, + ]); + + const { hasDefault } = await inquirer.prompt([ + { + type: 'confirm', + name: 'hasDefault', + message: 'Does this variable have a default value?', + default: false, + }, + ]); + + let defaultValue = ''; + if (hasDefault) { + const { value } = await inquirer.prompt([ + { + type: 'input', + name: 'value', + message: 'Enter default value:', + }, + ]); + defaultValue = value; + } + + variableList.push({ name, description, defaultValue }); + + const { shouldAddMore } = await inquirer.prompt([ + { + type: 'confirm', + name: 'shouldAddMore', + message: 'Add another variable?', + default: false, + }, + ]); + + if (!shouldAddMore) break; + } + + for (const v of variableList) { + variables[v.name] = `\${${v.name}}`; + } + } + + const substitutedLayout = templateUtils.substituteVariables(layout, variables); + + const template: Template = { + name: templateName, + displayName, + description, + variables: Object.entries(variables).map(([name]) => ({ + name, + description: '', + required: true, + })), + layout: substitutedLayout, + }; + + const filePath = await fileUtils.saveTemplate(templateName, template); + console.log(`Template created: ${filePath}`); +} + +async function deleteTemplate(name: string): Promise { + const templates = await fileUtils.listTemplates(); + const templateFile = templates.find((t) => t.includes(name)); + + if (!templateFile) { + throw new Error(`Template not found: ${name}`); + } + + const { confirm } = await inquirer.prompt([ + { + type: 'confirm', + name: 'confirm', + message: `Delete template "${name}"?`, + default: false, + }, + ]); + + if (confirm) { + await fileUtils.deleteTemplate(templateFile); + console.log(`Template deleted: ${name}`); + } +} + +async function applyTemplate(templateName: string, variables?: Record): Promise { + const templates = await fileUtils.listTemplates(); + const templateFile = templates.find((t) => t.includes(templateName)); + + if (!templateFile) { + throw new Error(`Template not found: ${templateName}`); + } + + const template = await fileUtils.readTemplate(templateFile) as Template; + + const missingVars = templateUtils.validateTemplateVariables(template, variables || {}); + if (missingVars.length > 0) { + console.log(`Missing required variables: ${missingVars.join(', ')}`); + return; + } + + const layout = templateUtils.substituteVariables(template.layout, variables || {}); + + const { saveAs } = await inquirer.prompt([ + { + type: 'input', + name: 'saveAs', + message: 'Save as layout name:', + default: `${template.name}-instance`, + }, + ]); + + const filePath = await fileUtils.saveLayout(saveAs, layout, 'json'); + console.log(`Layout saved from template: ${filePath}`); +}