qraft
Version:
A powerful CLI tool to qraft structured project setups from GitHub template repositories
299 lines ⢠13.5 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.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