dna-template-cli
Version:
DNA Template CLI v0.3.4 - Enhanced Commands Added (enhanced-create, enhanced-list, enhanced-validate)
243 lines • 11.4 kB
JavaScript
;
/**
* @fileoverview Create command - Template instantiation
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.createCommand = void 0;
const tslib_1 = require("tslib");
const commander_1 = require("commander");
const inquirer_compat_1 = tslib_1.__importDefault(require("../utils/inquirer-compat"));
const chalk_compat_1 = tslib_1.__importDefault(require("../utils/chalk-compat"));
const path_1 = tslib_1.__importDefault(require("path"));
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
// Mock TemplateEngine for CLI functionality
class TemplateEngine {
static getInstance() {
return new TemplateEngine();
}
}
const logger_1 = require("../utils/logger");
const error_handler_1 = require("../utils/error-handler");
const template_registry_1 = require("../lib/template-registry");
const template_selector_1 = require("../lib/template-selector");
const project_generator_1 = require("../lib/project-generator");
const progress_tracker_1 = require("../lib/progress-tracker");
exports.createCommand = new commander_1.Command('create')
.description('Create a new project from a DNA template')
.argument('[name]', 'project name')
.option('-t, --template <name>', 'template to use')
.option('-f, --framework <framework>', 'target framework')
.option('-o, --output <path>', 'output directory', process.cwd())
.option('-d, --dna <modules>', 'DNA modules to include (comma-separated)')
.option('-p, --package-manager <manager>', 'package manager to use', 'npm')
.option('--skip-install', 'skip dependency installation')
.option('--skip-git', 'skip git repository initialization')
.option('--dry-run', 'preview changes without creating files')
.option('--overwrite', 'overwrite existing files')
.option('--no-progress', 'disable progress indicators')
.option('-y, --yes', 'skip interactive prompts and use defaults')
.action(async (projectName, options) => {
try {
const config = await gatherProjectConfig(projectName, options);
const generationOptions = {
interactive: !options.yes,
dryRun: options.dryRun || false,
overwrite: options.overwrite || false,
backup: true,
progress: options.progress !== false,
};
await createProject(config, generationOptions);
}
catch (error) {
throw (0, error_handler_1.createCLIError)(error instanceof Error ? error.message : 'Failed to create project', 'PROJECT_CREATION_FAILED', 'Check the error details and try again with different options');
}
});
async function gatherProjectConfig(projectName, options = {}) {
const registry = new template_registry_1.TemplateRegistry();
await registry.load();
let config = {
packageManager: options.packageManager || 'npm',
skipInstall: options.skipInstall || false,
skipGit: options.skipGit || false,
variables: {},
};
// Get project name
if (!projectName) {
const { name } = await inquirer_compat_1.default.prompt([
{
type: 'input',
name: 'name',
message: 'What is your project name?',
validate: (input) => {
const error = (0, error_handler_1.validateProjectName)(input);
return error || true;
},
},
]);
config.name = name;
}
else {
const nameError = (0, error_handler_1.validateProjectName)(projectName);
if (nameError) {
throw (0, error_handler_1.createCLIError)(nameError, 'INVALID_PROJECT_NAME');
}
config.name = projectName;
}
// Get output path
const outputPath = options.output ? path_1.default.resolve(options.output) : path_1.default.resolve(process.cwd(), config.name);
const pathError = (0, error_handler_1.validatePath)(outputPath);
if (pathError) {
throw (0, error_handler_1.createCLIError)(pathError, 'INVALID_PATH');
}
config.path = outputPath;
// Check if directory exists
if (await fs_extra_1.default.pathExists(outputPath)) {
if (!options.overwrite) {
if (options.yes) {
throw (0, error_handler_1.createCLIError)(`Directory "${outputPath}" already exists`, 'DIRECTORY_EXISTS', 'Use --overwrite flag to replace existing files or choose a different name');
}
const { shouldOverwrite } = await inquirer_compat_1.default.prompt([
{
type: 'confirm',
name: 'shouldOverwrite',
message: `Directory "${outputPath}" already exists. Overwrite?`,
default: false,
},
]);
if (!shouldOverwrite) {
throw (0, error_handler_1.createCLIError)('Project creation cancelled', 'CANCELLED');
}
}
}
// Get template selection
if (!options.template) {
const selector = new template_selector_1.TemplateSelector(registry);
const selectedTemplate = await selector.selectTemplate({
showCategories: true,
showFilters: true,
showRecommended: true,
allowSearch: true,
maxResults: 20,
});
if (!selectedTemplate) {
throw (0, error_handler_1.createCLIError)('Template selection cancelled', 'CANCELLED');
}
config.template = selectedTemplate.id;
}
else {
const template = registry.getTemplate(options.template);
if (!template) {
throw (0, error_handler_1.createCLIError)(`Template "${options.template}" not found`, 'TEMPLATE_NOT_FOUND', 'Use "dna-cli list" to see available templates');
}
config.template = options.template;
}
const selectedTemplate = registry.getTemplate(config.template);
config.framework = selectedTemplate.framework;
// Get DNA modules
if (!options.dna && selectedTemplate.dnaModules.length > 0) {
const selector = new template_selector_1.TemplateSelector(registry);
const selectedModules = await selector.selectDnaModules(selectedTemplate.dnaModules, selectedTemplate.dnaModules // Pre-select all recommended modules
);
config.dnaModules = selectedModules;
}
else {
config.dnaModules = options.dna ? options.dna.split(',').map((m) => m.trim()) : selectedTemplate.dnaModules;
}
// Gather template-specific variables
if (!options.yes) {
const selector = new template_selector_1.TemplateSelector(registry);
const templateVariables = await selector.collectTemplateVariables(selectedTemplate, config.name);
config.variables = { ...config.variables, ...templateVariables };
}
return config;
}
async function createProject(config, options) {
const progressTracker = new progress_tracker_1.ProgressTracker(options.progress);
const generator = new project_generator_1.ProjectGenerator(config, options, progressTracker);
progressTracker.start('Creating project', 6);
try {
// Stage 1: Validate configuration
progressTracker.update(1, 'Validating configuration...');
await generator.validateConfiguration();
// Stage 2: Prepare project directory
progressTracker.update(2, 'Preparing project directory...');
await generator.prepareDirectory();
// Stage 3: Generate template files
progressTracker.update(3, 'Generating template files...');
await generator.generateFiles();
// Stage 4: Install dependencies
if (!config.skipInstall) {
progressTracker.update(4, 'Installing dependencies...');
await generator.installDependencies();
}
else {
progressTracker.update(4, 'Skipping dependency installation...');
}
// Stage 5: Initialize git repository
if (!config.skipGit) {
progressTracker.update(5, 'Initializing git repository...');
await generator.initializeGit();
}
else {
progressTracker.update(5, 'Skipping git initialization...');
}
// Stage 6: Finalize project
progressTracker.update(6, 'Finalizing project...');
await generator.finalize();
progressTracker.succeed('Project created successfully!');
// Show next steps
showNextSteps(config);
}
catch (error) {
progressTracker.fail('Project creation failed');
// Attempt rollback if not in dry-run mode
if (!options.dryRun) {
logger_1.logger.step('Attempting to clean up...');
await generator.rollback();
}
throw error;
}
}
function showNextSteps(config) {
const { name, path: projectPath, skipInstall, skipGit } = config;
const relativePath = path_1.default.relative(process.cwd(), projectPath);
logger_1.logger.plain('');
logger_1.logger.success(`✨ Project "${name}" created successfully!`);
logger_1.logger.plain('');
logger_1.logger.plain(chalk_compat_1.default.bold('📁 Project location:'));
logger_1.logger.plain(` ${chalk_compat_1.default.cyan(projectPath)}`);
logger_1.logger.plain('');
logger_1.logger.plain(chalk_compat_1.default.bold('🚀 Next steps:'));
if (relativePath !== '.') {
logger_1.logger.plain(` ${chalk_compat_1.default.cyan(`cd ${relativePath}`)}`);
}
if (skipInstall) {
logger_1.logger.plain(` ${chalk_compat_1.default.cyan('npm install')} ${chalk_compat_1.default.gray('# Install dependencies')}`);
}
logger_1.logger.plain(` ${chalk_compat_1.default.cyan('npm run dev')} ${chalk_compat_1.default.gray('# Start development server')}`);
logger_1.logger.plain(` ${chalk_compat_1.default.cyan('npm run build')} ${chalk_compat_1.default.gray('# Build for production')}`);
logger_1.logger.plain(` ${chalk_compat_1.default.cyan('npm test')} ${chalk_compat_1.default.gray('# Run tests')}`);
if (skipGit) {
logger_1.logger.plain(` ${chalk_compat_1.default.cyan('git init')} ${chalk_compat_1.default.gray('# Initialize git repository')}`);
logger_1.logger.plain(` ${chalk_compat_1.default.cyan('git add .')} ${chalk_compat_1.default.gray('# Stage all files')}`);
logger_1.logger.plain(` ${chalk_compat_1.default.cyan('git commit -m "Initial commit"')} ${chalk_compat_1.default.gray('# Create first commit')}`);
}
logger_1.logger.plain('');
logger_1.logger.plain(chalk_compat_1.default.bold('📚 Documentation:'));
logger_1.logger.plain(` ${chalk_compat_1.default.cyan('./README.md')} ${chalk_compat_1.default.gray('# Project-specific guide')}`);
logger_1.logger.plain(` ${chalk_compat_1.default.cyan('./docs/')} ${chalk_compat_1.default.gray('# Additional documentation')}`);
logger_1.logger.plain('');
}
// Utility functions
function toPascalCase(str) {
return str
.replace(/[-_](.)/g, (_, char) => char.toUpperCase())
.replace(/^(.)/, char => char.toUpperCase());
}
function toKebabCase(str) {
return str
.replace(/([A-Z])/g, '-$1')
.toLowerCase()
.replace(/^-/, '');
}
//# sourceMappingURL=create.js.map