create-react-fast-app
Version:
Instantly create a React + Vite + Tailwind + ShadCN app
223 lines (190 loc) • 5.79 kB
JavaScript
#!/usr/bin/env node
import enquirer from "enquirer";
import chalk from "chalk";
import { execa } from "execa";
import ora from "ora";
import degit from "degit";
import path from "path";
import fs from "fs";
import boxen from "boxen";
const { prompt } = enquirer;
const { Input, Select } = enquirer;
import open from "open";
import readline from "readline";
import { fileURLToPath } from "url";
const args = process.argv.slice(2);
// Default values
let projectNameArg = null;
let langArg = null;
// Parse CLI args
args.forEach((arg) => {
if (arg === "--js") langArg = "JavaScript";
else if (arg === "--ts") langArg = "TypeScript";
else if (!arg.startsWith("--")) projectNameArg = arg;
});
// Step 0: Give Credit
console.log();
console.log(
chalk.greenBright("✨ Thank You for using", chalk.italic("react-fast-app")));
console.log(
chalk.dim("This will creat a react app with Tailwind CSS and shadcn UI")
);
// Step 1: Ask for project name
console.log();
// const { projectName } = await prompt({
// type: "input",
// name: "projectName",
// message: chalk.cyan("Project name?\n", chalk.gray.italic(("use '.' for current folder:\n")),
// ),
// initial: "fast-react-app-by-satendra",
// });
const projectName = projectNameArg || (
await prompt({
type: "input",
name: "projectName",
message: chalk.cyan("Project name?\n", chalk.gray.italic("use '.' for current folder:\n")),
initial: "fast-react-app-by-satendra",
})
).projectName;
const targetDir =
projectName === "." ? "." : path.join(process.cwd(), projectName);
// Step 2: Ask for language preference
console.log();
// const { language } = await prompt({
// type: "select",
// name: "language",
// message: chalk.cyan("Choose your language:"),
// choices: [
// {
// name: "JavaScript",
// message: chalk.yellow("JavaScript"),
// value: "JavaScript",
// },
// {
// name: "TypeScript",
// message: chalk.blue("TypeScript"),
// value: "TypeScript",
// },
// ],
// });
const language = langArg || (
await prompt({
type: "select",
name: "language",
message: chalk.cyan("Choose your language:"),
choices: [
{
name: "JavaScript",
message: chalk.yellow("JavaScript"),
value: "JavaScript",
},
{
name: "TypeScript",
message: chalk.blue("TypeScript"),
value: "TypeScript",
},
],
})
).language;
// Step 3: Ask for UI library (currently only shadcn)
// console.log();
// const { uiLib } = await prompt({
// type: "select",
// name: "uiLib",
// message: chalk.cyan("Choose your UI library:"),
// choices: [
// {
// name: "shadcn",
// message: chalk.white.bgBlackBright('Shadcn UI'),
// value: "shadcn",
// },
// {
// name: "magic-ui",
// message: chalk.hex('#FEAACA')("Magic UI"),
// value: "magic-ui",
// },
// {
// name: "miui",
// message: chalk.hex('#0073E6')("MIUI"),
// value: "miui",
// },
// {
// name: "aceternity-ui",
// message: chalk.white.bgBlack("Aceternity UI"),
// value: "aceternity-ui",
// },
// ],
// });
const langSuffix = language === "JavaScript" ? "js" : "ts";
const repoFolder = `tailwind-shadcn-${langSuffix}`;
// Step 4: Clone the template
const repo = `satendra03/react-fast-app/${repoFolder}`;
const emitter = degit(repo, { force: true, verbose: true });
console.log();
const spinner = ora("Creating your Fast React app...").start();
try {
await emitter.clone(targetDir);
spinner.succeed(chalk.green("Project created successfully!"));
} catch (err) {
spinner.fail(chalk.red("Failed to create project."));
console.error(err);
process.exit(1);
}
// Step 5: Install dependencies
if (projectName !== ".") process.chdir(projectName);
console.log(chalk.cyan("\nInstalling dependencies...\n"));
const installSpinner = ora("Running npm install...").start();
try {
await execa("npm", ["install"], { stdio: "inherit" });
installSpinner.succeed("Dependencies installed.");
} catch (e) {
installSpinner.fail("Failed to install dependencies.");
process.exit(1);
}
const actualProjectName =
projectName === "." ? path.basename(process.cwd()) : projectName;
console.clear();
console.log(chalk.greenBright("\n✅ All done!\n"));
// console.log(chalk.blueBright(` cd ${actualProjectName}`));
// console.log(chalk.blueBright(` npm run dev\n`));
console.log(chalk.cyan("✨ Happy Development!\n"));
const credit =
chalk.greenBright("✨ Created with ❤️ by Satendra Parteti") +
"\n" +
chalk.cyan("🔗 GitHub: ") +
chalk.underline.blue("https://github.com/satendra03");
console.log();
console.log(
boxen(credit, {
padding: 1,
borderColor: "cyan",
borderStyle: "round",
align: "center",
})
);
console.log();
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const projectPath = process.cwd();
// Start dev server and listen to its stdout
const devProcess = execa("npm", ["run", "dev"], {
cwd: projectPath,
stdout: "pipe",
stderr: "inherit",
});
// Read stdout line by line
const rl = readline.createInterface({
input: devProcess.stdout,
});
let opened = false;
rl.on("line", (line) => {
console.log(line); // mirror the dev output to CLI
// Detect and open the local server URL
const match = line.match(/http:\/\/localhost:\d+/);
if (match && !opened) {
open(match[0]);
opened = true;
}
});
// Wait for the dev process to exit
await devProcess;