UNPKG

@expressots/cli

Version:

Expressots CLI - modern, fast, lightweight nodejs web framework (@cli)

237 lines (236 loc) 9.8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.projectForm = void 0; const chalk_1 = __importDefault(require("chalk")); const cli_progress_1 = require("cli-progress"); const degit_1 = __importDefault(require("degit")); const inquirer_1 = __importDefault(require("inquirer")); const node_child_process_1 = require("node:child_process"); const node_fs_1 = __importDefault(require("node:fs")); const node_path_1 = __importDefault(require("node:path")); const cli_1 = require("../cli"); const center_text_1 = require("../utils/center-text"); const change_package_info_1 = require("../utils/change-package-info"); const cli_ui_1 = require("../utils/cli-ui"); async function packageManagerInstall({ packageManager, directory, progressBar, }) { const command = process.platform === "win32" ? `${packageManager}.cmd` : packageManager; const args = ["install", "--silent"]; if (packageManager === "yarn") { args.push("--ignore-engines"); args.splice(args.indexOf("--prefer-offline"), 1); } return new Promise((resolve, reject) => { const installProcess = (0, node_child_process_1.spawn)(command, args, { cwd: directory, shell: true, timeout: 600000, }); // Simulate incremental progress let progress = 0; const interval = setInterval(() => { if (progress < 90) { progress += 5; progressBar.update(progress); } }, 1000); // Handle stdout for meaningful output or progress feedback installProcess.stdout?.on("data", (data) => { const output = data.toString().trim(); // Remove all data from || to the end of the line const cleanedOutput = output.replace(/\|\|.*$/g, ""); // Match and handle npm-specific progress const npmProgressMatch = cleanedOutput.match(/\[(\d+)\/(\d+)\] (?:npm )?([\w\s]+)\.{3}/); if (npmProgressMatch) { const [, current, total, task] = npmProgressMatch; progress = Math.round((parseInt(current) / parseInt(total)) * 100); progressBar.update(progress, { doing: task }); } else { // Update "task" without changing the progress progressBar.update(progress, { doing: cleanedOutput }); } }); // Handle errors installProcess.on("error", (error) => { clearInterval(interval); // Stop interval on error progressBar.stop(); reject(new Error(`Failed to start subprocess: ${error.message}`)); }); // Finalize progress on close installProcess.on("close", (code) => { clearInterval(interval); // Stop interval when the process ends if (code === 0) { progressBar.update(100, { doing: "Complete!" }); // Finalize progress progressBar.stop(); resolve("Installation Done!"); } else { progressBar.stop(); reject(new Error(`${packageManager} install exited with code ${code}`)); } }); }); } async function checkIfPackageManagerExists(packageManager) { try { (0, node_child_process_1.execSync)(`${packageManager} --version`); return true; } catch (error) { (0, cli_ui_1.printError)("Package manager not found!", packageManager); process.exit(1); } } function renameEnvFile(directory) { try { const envExamplePath = node_path_1.default.join(directory, ".env.example"); const envPath = node_path_1.default.join(directory, ".env"); if (!node_fs_1.default.existsSync(envExamplePath)) { throw new Error(`File not found: ${envExamplePath}`); } node_fs_1.default.renameSync(envExamplePath, envPath); } catch (error) { (0, cli_ui_1.printError)("Error renaming .env.example file", ".env.example to .env"); process.exit(1); } } var Template; (function (Template) { Template["nonopinionated"] = "Non-Opinionated :: Start with a clean slate and build your project from scratch."; Template["opinionated"] = "Opinionated :: Automatically scaffolds resources into a preset project structure. (Recommended)"; Template["micro"] = "Micro :: A minimalistic template for building micro api's."; })(Template || (Template = {})); const projectForm = async (projectName, args) => { let answer; const [packageManager, template, directory] = args; if (packageManager && template) { answer = { name: projectName, packageManager: packageManager, template: Template[template], confirm: true, }; } else { answer = await inquirer_1.default.prompt([ { type: "input", name: "name", message: "Project name", default: projectName, transformer: (input) => { return chalk_1.default.yellow(chalk_1.default.bold(input)); }, }, { type: "list", name: "packageManager", message: "Package manager", choices: ["npm", "yarn", "pnpm", "bun"], }, { type: "list", name: "template", message: "Select a template", choices: [ `Opinionated :: Automatically scaffolds resources into a preset project structure. (${chalk_1.default.yellow("Recommended")})`, "NonOpinionated :: Allows users to choose where to scaffold resources, offering flexible project organization.", "Micro :: A minimalistic template for building micro api's.", ], }, { type: "confirm", name: "confirm", message: "Do you want to create this project?", default: true, }, ]); } if (directory) { if (!node_fs_1.default.existsSync(node_path_1.default.join(directory, answer.name))) { answer.name = node_path_1.default.join(directory, answer.name); } else { (0, cli_ui_1.printError)("Directory already exists", directory); process.exit(1); } } // Hashmap of templates and their directories const templates = { NonOpinionated: "non_opinionated", Opinionated: "opinionated", Micro: "micro", }; if (answer.confirm) { // Check if package manager is bun and OS is Windows if (answer.packageManager === "bun" && process.platform === "win32") { (0, cli_ui_1.printError)("bun is not supported on Windows. Please use", "npm, yarn or pnpm"); process.exit(1); } await checkIfPackageManagerExists(answer.packageManager); console.log("\n"); const progressBar = new cli_progress_1.SingleBar({ format: "Progress |" + chalk_1.default.green("{bar}") + "| {percentage}% || {doing}", hideCursor: true, }, cli_progress_1.Presets.rect); progressBar.start(100, 0, { doing: "Cloning project", }); const [_, template] = answer.template.match(/(.*) ::/); const repo = `expressots/templates/${templates[template]}#${cli_1.BUNDLE_VERSION}`; try { const emitter = (0, degit_1.default)(repo); await emitter.clone(answer.name); } catch (err) { console.log("\n"); (0, cli_ui_1.printError)("Project already exists or Folder is not empty", answer.name); process.exit(1); } progressBar.update(50, { doing: "Installing dependencies", }); await packageManagerInstall({ packageManager: answer.packageManager, directory: answer.name, progressBar, }); progressBar.update(90); (0, change_package_info_1.changePackageName)({ directory: answer.name, name: projectName, }); progressBar.update(100); progressBar.stop(); console.log("\n"); console.log("🐎 Project", chalk_1.default.green(answer.name), "created successfully!"); console.log("🤙 Run the following commands to start the project:\n"); console.log(chalk_1.default.bold.gray(`$ cd ${answer.name}`)); switch (answer.packageManager) { case "npm": console.log(chalk_1.default.bold.gray("$ npm run dev")); break; case "yarn": console.log(chalk_1.default.bold.gray("$ yarn dev")); break; case "pnpm": console.log(chalk_1.default.bold.gray("$ pnpm run dev")); break; case "bun": console.log(chalk_1.default.bold.gray("$ bun dev")); break; } console.log("\n"); console.log(chalk_1.default.bold.green((0, center_text_1.centerText)("Happy coding!"))); console.log(chalk_1.default.bold.gray((0, center_text_1.centerText)("Please consider donating to support the project.\n"))); console.log(chalk_1.default.bold.white((0, center_text_1.centerText)("💖 Sponsor: https://github.com/sponsors/expressots"))); console.log("\n"); } }; exports.projectForm = projectForm;