UNPKG

qraft

Version:

A powerful CLI tool to qraft structured project setups from GitHub template repositories

299 lines • 13.5 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.CreateWorkflow = void 0; const chalk_1 = __importDefault(require("chalk")); const fs = __importStar(require("fs/promises")); const path = __importStar(require("path")); const confirmationWorkflows_1 = require("../interactive/confirmationWorkflows"); const progressIndicator_1 = require("../interactive/progressIndicator"); class CreateWorkflow { constructor() { this.confirmationWorkflows = new confirmationWorkflows_1.ConfirmationWorkflows(); this.progressIndicator = new progressIndicator_1.ProgressIndicator(); } async execute(options) { const startTime = Date.now(); const result = { success: false, boxName: options.boxName || path.basename(options.sourcePath), metadata: {}, filesProcessed: 0, warnings: [], errors: [], duration: 0, skipped: [] }; try { console.log(chalk_1.default.cyan('\nšŸš€ Starting box creation workflow...')); console.log(chalk_1.default.gray(`Source: ${options.sourcePath}`)); console.log(chalk_1.default.gray(`Target: ${options.targetRepository || 'local'}`)); // Initialize progress tracking this.progressIndicator.start(); // Step 1: Validate source path await this.executeStep('validate-source', async () => { await this.validateSourcePath(options.sourcePath); }); // Step 2: Collect metadata with smart defaults await this.executeStep('collect-metadata', async () => { // Mock metadata collection - in real implementation, this would use MetadataOverrides result.metadata = { name: options.boxName || path.basename(options.sourcePath), description: `Template box created from ${path.basename(options.sourcePath)}`, language: 'unknown', version: '1.0.0', license: 'MIT', tags: [], keywords: [], ...options.customDefaults }; result.boxName = result.metadata.name || result.boxName; }); // Step 3: Analyze source files (sensitive file detection) if (!options.skipSensitiveCheck) { await this.executeStep('analyze-sensitive-files', async () => { const sensitiveFiles = await this.detectSensitiveFiles(options.sourcePath); if (sensitiveFiles.length > 0) { const confirmation = await this.confirmationWorkflows.confirmSensitiveFiles(sensitiveFiles); if (!confirmation.confirmed) { throw new Error('Operation cancelled due to sensitive files'); } result.warnings.push(`${sensitiveFiles.length} sensitive file(s) detected but proceeding as confirmed`); } }); } else { this.progressIndicator.skipStep('analyze-sensitive-files', 'Skipped by user option'); result.skipped.push('Sensitive file analysis'); } // Step 4: Check for conflicts if (!options.skipConflictCheck && options.targetRepository) { await this.executeStep('check-conflicts', async () => { const conflicts = await this.detectConflicts(options.sourcePath, options.targetRepository); if (conflicts.length > 0) { const confirmation = await this.confirmationWorkflows.confirmConflictResolution(conflicts); if (!confirmation.confirmed) { throw new Error('Operation cancelled due to conflicts'); } result.warnings.push(`${conflicts.length} conflict(s) detected but proceeding as confirmed`); } }); } else { this.progressIndicator.skipStep('check-conflicts', 'Skipped - no target repository or disabled by user'); result.skipped.push('Conflict detection'); } // Step 5: Dry run preview (if requested) if (options.dryRun) { await this.executeStep('dry-run-preview', async () => { const dryRunSummary = await this.generateDryRunSummary(options, result); const confirmation = await this.confirmationWorkflows.confirmDryRunResults(dryRunSummary); if (!confirmation.confirmed) { throw new Error('Operation cancelled after dry run preview'); } }); } else { this.progressIndicator.skipStep('dry-run-preview', 'Dry run not requested'); result.skipped.push('Dry run preview'); } // Step 6: Process files await this.executeStep('process-files', async () => { result.filesProcessed = await this.processFiles(options.sourcePath, result.metadata, options.outputPath); }); // Step 7: Generate box metadata file await this.executeStep('generate-metadata', async () => { await this.generateBoxMetadata(result.metadata, options.outputPath || options.sourcePath); }); // Step 8: Finalize and cleanup await this.executeStep('finalize', async () => { result.outputPath = options.outputPath || options.sourcePath; await this.finalizeBox(result); }); result.success = true; result.duration = Date.now() - startTime; // Show completion summary this.showCompletionSummary(result); } catch (error) { result.success = false; result.duration = Date.now() - startTime; result.errors.push(error instanceof Error ? error.message : String(error)); this.progressIndicator.failStep(error instanceof Error ? error.message : String(error)); console.error(chalk_1.default.red('\nāŒ Box creation failed:'), error instanceof Error ? error.message : String(error)); } finally { this.progressIndicator.complete(); this.confirmationWorkflows.close(); } return result; } async executeStep(_stepName, operation) { try { await operation(); } catch (error) { this.progressIndicator.failStep(error instanceof Error ? error.message : String(error)); throw error; } } async validateSourcePath(sourcePath) { try { const stats = await fs.stat(sourcePath); if (!stats.isDirectory()) { throw new Error(`Source path must be a directory: ${sourcePath}`); } } catch (error) { if (error instanceof Error && 'code' in error && error.code === 'ENOENT') { throw new Error(`Source path does not exist: ${sourcePath}`); } throw error; } } async detectSensitiveFiles(_sourcePath) { // Mock implementation - in real implementation, this would use SensitiveFileDetector const warnings = []; // This is a simplified implementation for demonstration // Real implementation would recursively scan files and match patterns return warnings; } async detectConflicts(_sourcePath, _targetRepository) { // Mock implementation - in real implementation, this would use ConflictDetector const conflicts = []; // This is a simplified implementation for demonstration // Real implementation would compare with target repository return conflicts; } async generateDryRunSummary(options, result) { const fileCount = await this.countFiles(options.sourcePath); return { operation: `Create box "${result.boxName}"`, filesAffected: fileCount, estimatedTime: this.estimateProcessingTime(fileCount), riskLevel: this.assessRiskLevel(result.warnings || []), warnings: result.warnings || [] }; } async countFiles(sourcePath) { // Simplified file counting - real implementation would recursively count files try { const entries = await fs.readdir(sourcePath); return entries.length; } catch { return 0; } } estimateProcessingTime(fileCount) { const secondsPerFile = 0.1; const totalSeconds = Math.max(1, Math.round(fileCount * secondsPerFile)); if (totalSeconds < 60) { return `${totalSeconds} second${totalSeconds === 1 ? '' : 's'}`; } else { const minutes = Math.round(totalSeconds / 60); return `${minutes} minute${minutes === 1 ? '' : 's'}`; } } assessRiskLevel(warnings) { if (warnings.some(w => w.includes('critical'))) return 'critical'; if (warnings.some(w => w.includes('sensitive'))) return 'high'; if (warnings.length > 0) return 'medium'; return 'low'; } async processFiles(sourcePath, _metadata, _outputPath) { // Mock implementation - real implementation would copy/process files const fileCount = await this.countFiles(sourcePath); // Simulate processing time await new Promise(resolve => setTimeout(resolve, Math.min(1000, fileCount * 10))); return fileCount; } async generateBoxMetadata(metadata, outputPath) { const metadataFile = path.join(outputPath, 'box.json'); const boxMetadata = { ...metadata, createdAt: new Date().toISOString(), version: metadata.version || '1.0.0' }; await fs.writeFile(metadataFile, JSON.stringify(boxMetadata, null, 2)); } async finalizeBox(_result) { // Mock implementation - real implementation would handle final steps // like creating git repository, pushing to remote, etc. await new Promise(resolve => setTimeout(resolve, 100)); } showCompletionSummary(result) { console.log(chalk_1.default.green('\nāœ… Box creation completed successfully!')); console.log(chalk_1.default.gray('─'.repeat(50))); console.log(`${chalk_1.default.yellow('Box Name:')} ${result.boxName}`); console.log(`${chalk_1.default.yellow('Files Processed:')} ${result.filesProcessed}`); console.log(`${chalk_1.default.yellow('Duration:')} ${(result.duration / 1000).toFixed(1)}s`); if (result.warnings.length > 0) { console.log(`${chalk_1.default.yellow('Warnings:')} ${result.warnings.length}`); result.warnings.forEach(warning => { console.log(` ${chalk_1.default.yellow('⚠')} ${warning}`); }); } if (result.skipped.length > 0) { console.log(`${chalk_1.default.gray('Skipped:')} ${result.skipped.join(', ')}`); } if (result.outputPath) { console.log(`${chalk_1.default.yellow('Output:')} ${result.outputPath}`); } console.log(chalk_1.default.gray('─'.repeat(50))); } // Test helper methods async workflowDryRun(options) { return this.execute({ ...options, dryRun: true }); } async validateWorkflowOptions(options) { try { await this.validateSourcePath(options.sourcePath); return true; } catch { return false; } } } exports.CreateWorkflow = CreateWorkflow; //# sourceMappingURL=createWorkflow.js.map