dev-hub-cli
Version:
CLI tool for creating development project boilerplates
321 lines (295 loc) • 12 kB
JavaScript
#!/usr/bin/env node
const { program } = require("commander");
const inquirer = require("inquirer");
const chalk = require("chalk");
const path = require("path");
const fs = require("fs-extra");
const { exec } = require("child_process");
const os = require("os");
const ora = require("ora");
const https = require("https");
const semver = require("semver");
const packageJson = require("../package.json");
async function checkForUpdates() {
return new Promise((resolve) => {
https
.get(
"https://registry.npmjs.org/dev-hub-cli/latest",
{ headers: { "User-Agent": "node" } },
(res) => {
let data = "";
res.on("data", (chunk) => {
data += chunk;
});
res.on("end", () => {
try {
const latestVersion = JSON.parse(data).version;
const currentVersion = packageJson.version;
if (semver.gt(latestVersion, currentVersion)) {
console.log(
chalk.yellow("\n┌────────────────────────────────────────┐")
);
console.log(
chalk.yellow("│ Update Available! │")
);
console.log(
chalk.yellow("└────────────────────────────────────────┘")
);
console.log(chalk.white(`Current version: ${currentVersion}`));
console.log(chalk.green(`Latest version: ${latestVersion}`));
console.log(
chalk.blue("\nRun the following command to update:")
);
console.log(chalk.white("npm install -g dev-hub-cli@latest\n"));
} else {
console.log(
chalk.green("\n✓ CLI is up to date ") +
chalk.gray(`(version ${currentVersion})`)
);
}
resolve();
} catch (error) {
resolve();
}
});
}
)
.on("error", () => {
resolve();
});
});
}
//runCommand function
async function runCommand(command, cwd) {
return new Promise((resolve, reject) => {
exec(command, { cwd }, (error, stdout, stderr) => {
if (error) {
reject(error);
} else {
resolve(stdout);
}
});
});
}
const { createHtmlProject } = require("../src/projects/htmlproject");
const { createReactProject } = require("../src/projects/reactproject");
const { createElectronProject } = require("../src/projects/electronproject");
const { createFiveMProject } = require("../src/projects/fiveproject");
const { createRedMProject } = require("../src/projects/redproject");
const { createVueProject } = require("../src/projects/vueproject");
// ASCII Art Banner
console.log(
chalk.blue(`
/$$$$$$$ /$$ /$$ /$$
| $$__ $$ | $$ | $$ | $$
| $$ \ $$ /$$$$$$ /$$ /$$ | $$ | $$ /$$ /$$| $$$$$$$
| $$ | $$ /$$__ $$| $$ /$$/ /$$$$$$ | $$$$$$$$| $$ | $$| $$__ $$
| $$ | $$| $$$$$$$$ \ $$/$$/ |______/ | $$__ $$| $$ | $$| $$ | $$
| $$ | $$| $$_____/ \ $$$/ | $$ | $$| $$ | $$| $$ | $$
| $$$$$$$/| $$$$$$$ \ $/ | $$ | $$| $$$$$$/| $$$$$$$/
|_______/ \_______/ \_/ |__/ |__/ \______/ |_______/
-- Frsk Development --
`)
);
program
.version(packageJson.version)
.description("CLI tool for creating development project boilerplates");
program
.command("create")
.description("Create a new project")
.action(async () => {
// Checking for updates before showing prompts
await checkForUpdates();
const questions = [
{
type: "input",
name: "projectName",
message: "What is your project name?",
validate: (input) => {
if (/^([A-Za-z\-_\d])+$/.test(input)) return true;
return "Project name may only include letters, numbers, underscores and hashes.";
},
},
{
type: "list",
name: "template",
message: "What project template would you like to use?",
choices: [
{ name: "HTML/CSS - Simple website", value: "html" },
{ name: "React - Modern web application", value: "react" },
{ name: "Electron - Desktop application", value: "electron" },
{ name: "FiveM - GTA V multiplayer resource", value: "fivem" },
{
name: "RedM - Red Dead Redemption 2 multiplayer resource",
value: "redm",
},
{ name: "Vue.js - Progressive JavaScript Framework", value: "vue" },
],
},
];
const { projectName, template } = await inquirer.prompt(questions);
let advancedOptions = {};
if (template === "html") {
const htmlOptions = await inquirer.prompt([
{
type: "checkbox",
name: "frameworks",
message:
"Select CSS frameworks: (Use ↑↓ to move, Space to select, Enter to confirm)",
choices: [
{ name: "Pure CSS", value: "pure-css" },
{ name: "Tailwind CSS", value: "tailwind" },
{ name: "Bootstrap", value: "bootstrap" },
{ name: "Sass/SCSS", value: "sass" },
],
},
{
type: "checkbox",
name: "meta",
message:
"Select meta tags: (Use ↑↓ to move, Space to select, Enter to confirm)",
choices: [
{ name: "SEO Optimization", value: "seo" },
{ name: "Social Media Tags", value: "social" },
],
},
]);
advancedOptions = {
"css-framework": {
"pure-css": htmlOptions.frameworks.includes("pure-css"),
tailwind: htmlOptions.frameworks.includes("tailwind"),
bootstrap: htmlOptions.frameworks.includes("bootstrap"),
sass: htmlOptions.frameworks.includes("sass"),
},
meta: {
seo: htmlOptions.meta.includes("seo"),
social: htmlOptions.meta.includes("social"),
},
};
} else if (template === "electron") {
const electronOptions = await inquirer.prompt([
{
type: "checkbox",
name: "features",
message:
"Select Electron features: (Use ↑↓ to move, Space to select, Enter to confirm)",
choices: [
{ name: "Frameless Window", value: "frameless" },
{ name: "Custom Title Bar", value: "custom-titlebar" },
{ name: "Auto Updater", value: "auto-updater" },
{ name: "Custom Installer", value: "installer" },
],
},
]);
advancedOptions = {
window: {
frameless: electronOptions.features.includes("frameless"),
"custom-titlebar":
electronOptions.features.includes("custom-titlebar"),
},
build: {
"auto-updater": electronOptions.features.includes("auto-updater"),
installer: electronOptions.features.includes("installer"),
},
};
} else if (template === "vue") {
const vueOptions = await inquirer.prompt([
{
type: "checkbox",
name: "features",
message: "Select Vue.js features:",
choices: [
{ name: "TypeScript", value: "typescript" },
{ name: "Vue Router", value: "router" },
{ name: "Pinia State Management", value: "pinia" },
{ name: "Unit Testing", value: "testing" },
],
},
]);
// Convert array of features to object format
advancedOptions = {
features: vueOptions.features.reduce((acc, feature) => {
acc[feature] = true;
return acc;
}, {}),
};
}
// Create project in current directory
const projectPath = path.join(process.cwd(), projectName);
try {
console.log();
const spinner = ora("Creating project structure...").start();
try {
switch (template) {
case "html":
spinner.text = "Creating HTML project structure...";
await createHtmlProject(projectPath, advancedOptions);
spinner.succeed("Project structure created");
spinner.start("Installing dependencies...");
spinner.succeed("Dependencies installed");
break;
case "react":
spinner.text = "Creating React project structure...";
await createReactProject(projectPath);
spinner.succeed("Project structure created");
spinner.start("Installing dependencies...");
await runCommand("npm install", projectPath);
spinner.succeed("Dependencies installed");
break;
case "electron":
spinner.text = "Creating Electron project structure...";
await createElectronProject(projectPath, advancedOptions);
spinner.succeed("Project structure created");
spinner.start("Installing dependencies...");
await runCommand("npm install", projectPath);
spinner.succeed("Dependencies installed");
break;
case "fivem":
spinner.text = "Creating FiveM resource structure...";
await createFiveMProject(projectPath);
spinner.succeed("FiveM resource created successfully!");
console.log(chalk.blue("\nResource created at:"));
console.log(chalk.white(` ${projectPath}`));
break;
case "redm":
spinner.text = "Creating RedM resource structure...";
await createRedMProject(projectPath);
spinner.succeed("RedM resource created successfully!");
console.log(chalk.blue("\nResource created at:"));
console.log(chalk.white(` ${projectPath}`));
break;
case "vue":
spinner.text = "Creating Vue.js project structure...";
await createVueProject(projectPath, advancedOptions);
spinner.succeed("Project structure created");
spinner.start("Installing dependencies...");
await runCommand("npm install", projectPath);
spinner.succeed("Dependencies installed");
break;
}
if (["html", "react", "electron", "vue"].includes(template)) {
console.log(chalk.blue("\nNext steps:"));
console.log(chalk.white(` 1. cd ${projectName}`));
if(template === "html") {
console.log(chalk.white(" 2. Right-click on the index.html file and select 'Open with Live Server'"));
}
if(template === "react") {
console.log(chalk.white(" 2. npm start"));
}
if(template === "electron") {
console.log(chalk.white(" 2. npm start"));
}
if(template === "vue") {
console.log(chalk.white(" 2. npm run dev"));
}
}
} catch (error) {
spinner.fail("Error creating project");
throw error;
}
} catch (error) {
console.error(chalk.red("\nError details:"), error);
process.exit(1);
}
});
program.parse(process.argv);