woaru
Version:
Universal Project Setup Autopilot - Analyze and automatically configure development tools for ANY programming language
265 lines ⢠11 kB
JavaScript
/**
* WOARU Init Command - Interactive Project Scaffolding
* Implements the main init command with full project generation capabilities
*/
import chalk from 'chalk';
import * as path from 'path';
import { spawn } from 'child_process';
import { TemplateRegistry } from './TemplateRegistry.js';
import { ProjectSelector } from './ProjectSelector.js';
import { TemplateEngine } from './TemplateEngine.js';
export class InitCommand {
registry;
selector;
engine;
constructor() {
this.registry = new TemplateRegistry();
this.selector = new ProjectSelector(this.registry);
this.engine = new TemplateEngine();
}
/**
* Execute the init command
*/
async execute(options = {}) {
try {
console.log(chalk.cyan.bold('š WOARU Project Initializer'));
console.log(chalk.gray('Creating your project with best practices...\\n'));
let config;
if (options.interactive === false) {
// Non-interactive mode
config = await this.createConfigFromOptions(options);
}
else {
// Interactive mode (default)
config = await this.runInteractiveFlow(options);
}
// Dry run mode - show what would be generated
if (options.dryRun) {
await this.showDryRun(config);
return;
}
// Generate the project
const generatedProject = await this.engine.processTemplate(config);
// Post-generation steps
await this.runPostGenerationSteps(config, generatedProject);
// Show success message and next steps
this.showSuccessMessage(config, generatedProject);
}
catch (error) {
this.handleError(error);
}
}
/**
* Run interactive project creation flow
*/
async runInteractiveFlow(options) {
// Step 1: Select project template
const template = options.template
? this.registry.get(options.template)
: await this.selector.selectProjectType();
if (!template) {
throw new Error(`Template "${options.template}" not found`);
}
// Step 2: Select features
const features = options.features
? options.features.split(',').map(f => f.trim())
: await this.selector.selectFeatures(template);
// Step 3: Configure project
const config = await this.selector.configureProject(template, features);
// Override with command line options
if (options.directory) {
config.directory = options.directory;
}
if (options.skipInstall) {
config.installDeps = false;
}
// Step 4: Confirm generation
const confirmed = await this.selector.confirmGeneration(config);
if (!confirmed) {
console.log(chalk.yellow('\\nā Project generation cancelled.'));
process.exit(0);
}
return config;
}
/**
* Create config from command line options (non-interactive)
*/
async createConfigFromOptions(options) {
if (!options.template) {
throw new Error('Template is required in non-interactive mode. Use --template option.');
}
const template = this.registry.get(options.template);
if (!template) {
throw new Error(`Template "${options.template}" not found`);
}
const features = options.features
? options.features.split(',').map(f => f.trim())
: [];
const projectName = path.basename(options.directory || 'my-project');
return {
name: projectName,
template,
features,
directory: options.directory || `./${projectName}`,
packageManager: template.packageManager,
gitInit: true,
installDeps: !options.skipInstall,
variables: {
projectName,
features: features.reduce((acc, f) => ({ ...acc, [f]: true }), {}),
packageManager: template.packageManager,
year: new Date().getFullYear(),
date: new Date().toISOString().split('T')[0],
},
};
}
/**
* Show dry run output
*/
async showDryRun(config) {
console.log(chalk.cyan.bold('\\nš Dry Run Mode - Preview'));
console.log(chalk.gray('ā'.repeat(50)));
console.log(chalk.white(`Project: ${chalk.green(config.name)}`));
console.log(chalk.white(`Template: ${chalk.green(config.template.name)}`));
console.log(chalk.white(`Directory: ${chalk.green(config.directory)}`));
console.log(chalk.white(`Features: ${chalk.green(config.features.join(', ') || 'None')}`));
console.log(chalk.cyan('\\nš Directories to create:'));
config.template.structure.directories.forEach(dir => {
console.log(chalk.gray(` ${dir.path}/`));
});
console.log(chalk.cyan('\\nš Files to generate:'));
config.template.structure.files.forEach(file => {
console.log(chalk.gray(` ${file.destination}`));
});
config.template.structure.templates.forEach(template => {
console.log(chalk.gray(` ${template.destination} (from template)`));
});
Object.keys(config.template.configuration).forEach(filename => {
console.log(chalk.gray(` ${filename} (configuration)`));
});
console.log(chalk.yellow('\\nš” Run without --dry-run to generate the project.'));
}
/**
* Run post-generation steps
*/
async runPostGenerationSteps(config, _project) {
const { directory } = config;
// Initialize Git repository
if (config.gitInit) {
console.log(chalk.blue('\\nš Initializing Git repository...'));
try {
await this.runCommand('git', ['init'], directory);
await this.runCommand('git', ['add', '.'], directory);
await this.runCommand('git', ['commit', '-m', 'Initial commit from WOARU'], directory);
console.log(chalk.green(' ā Git repository initialized'));
}
catch {
console.log(chalk.yellow(' ā ļø Failed to initialize Git repository'));
}
}
// Install dependencies
if (config.installDeps) {
console.log(chalk.blue('\\nš¦ Installing dependencies...'));
try {
const installCmd = this.getInstallCommand(config.packageManager);
await this.runCommand(installCmd.command, installCmd.args, directory);
console.log(chalk.green(' ā Dependencies installed successfully'));
}
catch {
console.log(chalk.yellow(' ā ļø Failed to install dependencies'));
console.log(chalk.gray(` Run "${this.getInstallCommand(config.packageManager).command} ${this.getInstallCommand(config.packageManager).args.join(' ')}" manually`));
}
}
}
/**
* Get install command for package manager
*/
getInstallCommand(packageManager) {
const commands = {
npm: { command: 'npm', args: ['install'] },
yarn: { command: 'yarn', args: ['install'] },
pnpm: { command: 'pnpm', args: ['install'] },
pip: { command: 'pip', args: ['install', '-r', 'requirements.txt'] },
poetry: { command: 'poetry', args: ['install'] },
pipenv: { command: 'pipenv', args: ['install'] },
};
return commands[packageManager] || commands.npm;
}
/**
* Run shell command
*/
runCommand(command, args, cwd) {
return new Promise((resolve, reject) => {
const process = spawn(command, args, {
cwd,
stdio: 'pipe',
shell: true,
});
let stdout = '';
let stderr = '';
process.stdout?.on('data', data => {
stdout += data.toString();
});
process.stderr?.on('data', data => {
stderr += data.toString();
});
process.on('close', code => {
if (code === 0) {
resolve();
}
else {
reject(new Error(`Command failed with code ${code}: ${stderr || stdout}`));
}
});
process.on('error', error => {
reject(error);
});
});
}
/**
* Show success message and next steps
*/
showSuccessMessage(config, project) {
console.log(chalk.green.bold('\\nš Project created successfully!'));
console.log(chalk.gray('ā'.repeat(50)));
console.log(chalk.white(`š Location: ${chalk.cyan(config.directory)}`));
console.log(chalk.white(`š Files: ${chalk.cyan(project.files.length.toString())}`));
console.log(chalk.white(`š Directories: ${chalk.cyan(project.directories.length.toString())}`));
if (project.summary.features.length > 0) {
console.log(chalk.white(`āļø Features: ${chalk.cyan(project.summary.features.join(', '))}`));
}
console.log(chalk.cyan.bold('\\nš Next Steps:'));
project.summary.nextSteps.forEach((step, index) => {
console.log(chalk.white(`${index + 1}. ${chalk.gray(step)}`));
});
console.log(chalk.cyan('\\nš” Additional Commands:'));
console.log(chalk.gray(' woaru analyze - Analyze your project'));
console.log(chalk.gray(' woaru audit - Run security audit'));
console.log(chalk.gray(' woaru setup - Configure development tools'));
console.log(chalk.green('\\n⨠Happy coding!'));
}
/**
* Handle errors gracefully
*/
handleError(error) {
console.error(chalk.red.bold('\\nā Error creating project:'));
if ('code' in error && error.code === 'ENOENT') {
console.error(chalk.red(' Directory or file not found'));
}
else if ('code' in error && error.code === 'EACCES') {
console.error(chalk.red(' Permission denied'));
}
else if (error.message) {
console.error(chalk.red(` ${error.message}`));
}
else {
console.error(chalk.red(' Unknown error occurred'));
}
console.error(chalk.gray('\\nš” Troubleshooting:'));
console.error(chalk.gray(' ⢠Check that the target directory exists and is writable'));
console.error(chalk.gray(' ⢠Ensure you have the necessary permissions'));
console.error(chalk.gray(' ⢠Try running with --dry-run to preview changes'));
process.exit(1);
}
}
//# sourceMappingURL=InitCommand.js.map