express-hale
Version:
š Interactive Express.js scaffold CLI with comprehensive error handling, TypeScript/JavaScript, database integrations, Git Flow, and development tools
276 lines ⢠11.5 kB
JavaScript
#!/usr/bin/env node
"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 });
const inquirer_1 = __importDefault(require("inquirer"));
const chalk_1 = __importDefault(require("chalk"));
const commander_1 = require("commander");
const ora_1 = __importDefault(require("ora"));
const path_1 = __importDefault(require("path"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const generator_1 = require("../core/generator");
const error_handler_1 = require("../core/error-handler");
const startup_banner_1 = require("./startup-banner");
// Get version from package.json
const packageJson = __importStar(require("../../package.json"));
// Setup global error handling
(0, error_handler_1.setupGlobalErrorHandlers)();
const program = new commander_1.Command();
const errorHandler = error_handler_1.ErrorHandler.getInstance();
const startupBanner = startup_banner_1.StartupBanner.getInstance();
program
.name('express-hale')
.description('Interactive Express.js scaffold CLI')
.version(packageJson.version)
.argument('[project-name]', 'Name of the project')
.action(async (projectName) => {
let projectPath;
let spinner;
try {
// Display startup banner
await startupBanner.displayStartupBanner({
name: 'express-hale',
version: packageJson.version,
environment: process.env.NODE_ENV || 'development',
author: 'Gaowenhan',
});
console.log(chalk_1.default.blue.bold('š Welcome to Express Hale CLI'));
console.log(chalk_1.default.gray('Create a modern Express.js application with your preferred stack\n'));
// Get project name if not provided
if (!projectName) {
try {
const namePrompt = await inquirer_1.default.prompt([
{
type: 'input',
name: 'projectName',
message: 'Project name:',
default: 'my-express-app',
validate: (input) => {
if (!input.trim()) {
return 'Project name is required';
}
if (!/^[a-zA-Z0-9-_]+$/.test(input)) {
return 'Project name can only contain letters, numbers, hyphens, and underscores';
}
return true;
},
},
]);
projectName = namePrompt.projectName;
}
catch (promptError) {
throw new error_handler_1.CLIError('Failed to get project name from user input', 'INPUT_ERROR', {
operation: 'project-name-prompt',
phase: 'initialization',
timestamp: new Date(),
}, promptError instanceof Error ? promptError : undefined);
}
}
// Interactive configuration prompts
let config;
try {
config = await inquirer_1.default.prompt([
{
type: 'list',
name: 'language',
message: 'Select language:',
choices: [
{ name: 'TypeScript', value: 'typescript' },
{ name: 'JavaScript', value: 'javascript' },
],
default: 'typescript',
},
{
type: 'checkbox',
name: 'databases',
message: 'Select databases (use space to select):',
choices: [
{ name: 'MySQL', value: 'mysql', checked: false },
{ name: 'MongoDB', value: 'mongodb', checked: false },
{ name: 'Redis', value: 'redis', checked: false },
],
},
{
type: 'list',
name: 'packageManager',
message: 'Select package manager:',
choices: [
{ name: 'pnpm (ęØč)', value: 'pnpm' },
{ name: 'npm', value: 'npm' },
{ name: 'yarn', value: 'yarn' },
],
default: 'pnpm',
},
{
type: 'confirm',
name: 'eslint',
message: 'Add ESLint for code linting?',
default: true,
},
{
type: 'confirm',
name: 'prettier',
message: 'Add Prettier for code formatting?',
default: true,
},
{
type: 'list',
name: 'testing',
message: 'Select testing framework:',
choices: [
{ name: 'Jest', value: 'jest' },
{ name: 'Mocha', value: 'mocha' },
{ name: 'None', value: 'none' },
],
default: 'jest',
},
{
type: 'confirm',
name: 'docker',
message: 'Add Docker configuration?',
default: false,
},
{
type: 'confirm',
name: 'gitInit',
message: 'Initialize git repository?',
default: true,
},
{
type: 'confirm',
name: 'gitFlow',
message: 'Initialize Git Flow workflow?',
default: false,
when: (answers) => answers.gitInit,
},
{
type: 'confirm',
name: 'errorHandling',
message: 'Add advanced error handling middleware and utilities?',
default: true,
},
]);
}
catch (configError) {
throw new error_handler_1.CLIError('Failed to collect project configuration', 'CONFIG_ERROR', {
operation: 'configuration-prompt',
phase: 'initialization',
timestamp: new Date(),
projectName,
}, configError instanceof Error ? configError : undefined);
}
projectPath = path_1.default.resolve(process.cwd(), projectName);
// Setup error handler with project context
errorHandler.setLogFile(projectPath);
// Check if directory already exists
if (await fs_extra_1.default.pathExists(projectPath)) {
try {
const { overwrite } = await inquirer_1.default.prompt([
{
type: 'confirm',
name: 'overwrite',
message: `Directory ${projectName} already exists. Overwrite?`,
default: false,
},
]);
if (!overwrite) {
console.log(chalk_1.default.yellow('Operation cancelled.'));
process.exit(0);
}
await fs_extra_1.default.remove(projectPath);
}
catch (overwriteError) {
throw new error_handler_1.CLIError(`Failed to handle existing directory: ${projectPath}`, 'PROJECT_EXISTS', {
operation: 'directory-cleanup',
phase: 'initialization',
timestamp: new Date(),
projectName,
projectPath,
}, overwriteError instanceof Error ? overwriteError : undefined);
}
}
// Generate project
spinner = (0, ora_1.default)('Generating project...').start();
try {
const generator = new generator_1.ProjectGenerator(projectName, projectPath, config, packageJson.version);
await generator.generate();
spinner.succeed(chalk_1.default.green('Project generated successfully!'));
console.log(chalk_1.default.blue('\nš Next steps:'));
console.log(` cd ${projectName}`);
console.log(` ${config.packageManager} install`);
if (config.language === 'typescript') {
console.log(` ${config.packageManager} run build`);
}
console.log(` ${config.packageManager} run dev`);
console.log(chalk_1.default.gray('\nHappy coding! š'));
}
catch (generationError) {
spinner?.fail(chalk_1.default.red('Failed to generate project'));
throw new error_handler_1.CLIError('Project generation failed', 'GENERATION_ERROR', {
operation: 'project-generation',
phase: 'generation',
timestamp: new Date(),
projectName,
projectPath,
}, generationError instanceof Error ? generationError : undefined);
}
}
catch (error) {
// Stop spinner if it's running
if (spinner && spinner.isSpinning) {
spinner.fail(chalk_1.default.red('Process failed'));
}
// Cleanup on error
if (projectPath) {
await errorHandler.cleanup(projectPath);
}
// Handle the error
await errorHandler.handleError(error instanceof Error ? error : new Error(String(error)));
}
});
// Handle process termination gracefully
process.on('SIGINT', () => {
startupBanner.displayShutdownMessage();
process.exit(0);
});
process.on('SIGTERM', () => {
startupBanner.displayShutdownMessage();
process.exit(0);
});
program.parse();
//# sourceMappingURL=index.js.map