@twotwoba/vv-cli
Version:
CLI tool for creating Vue3 or React19 template projects by vite
118 lines (103 loc) • 4.3 kB
JavaScript
import { program } from "commander";
import inquirer from "inquirer";
import fse from "fs-extra";
import path from "path";
import chalk from "chalk";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const templatePathVue = path.resolve(__dirname, "../template-vue");
const templatePathReact = path.resolve(__dirname, "../template-react");
// Read package.json to get version
const packageJson = JSON.parse(
fse.readFileSync(path.resolve(__dirname, "../package.json"), "utf8")
);
program
.name("vv-cli")
.description("CLI tool for creating Vue3/React + Vite projects")
.version(packageJson.version);
program
.argument("[project-name]", "Name of the project")
.action(async (projectName) => {
try {
// If project name is not provided, ask for it
if (!projectName) {
const answer = await inquirer.prompt([
{
type: "input",
name: "projectName",
message: "Please enter your project name:",
validate: (input) => {
if (!input) return "Project name is required";
if (fse.existsSync(input))
return "Directory already exists";
return true;
},
},
]);
projectName = answer.projectName;
}
// Check if directory already exists
if (fse.existsSync(projectName)) {
console.error(
chalk.red(`Error: Directory ${projectName} already exists`)
);
process.exit(1);
}
// Ask for template type
const templateAnswer = await inquirer.prompt([
{
type: "list",
name: "template",
message: "Select a template:",
choices: [
{
name: "Vue3 + Vite + Naive UI + Unocss",
value: "vue",
},
{
name: "React19 + Vite + shadcn/ui + tailwindcss",
value: "react",
},
],
},
]);
// Set template path based on user choice
const templatePath =
templateAnswer.template === "vue"
? templatePathVue
: templatePathReact;
// Create project directory
const targetPath = path.resolve(process.cwd(), projectName);
// Copy template files
console.log(chalk.blue("Creating project directory..."));
await fse.copy(templatePath, targetPath, {
filter: (src) => {
const basename = path.basename(src);
return basename !== "node_modules" && basename !== ".git";
},
});
// Rename gitignore to .gitignore
// .gitignore will not update to npm registry
const gitignorePath = path.join(targetPath, "gitignore");
const dotGitignorePath = path.join(targetPath, ".gitignore");
if (await fse.pathExists(gitignorePath)) {
await fse.rename(gitignorePath, dotGitignorePath);
}
// Update package.json
const pkgPath = path.join(targetPath, "package.json");
const pkg = await fse.readJson(pkgPath);
pkg.name = projectName;
await fse.writeJson(pkgPath, pkg, { spaces: 2 });
console.log(chalk.green("\n✨ Project created successfully!"));
console.log("\nNext steps:");
console.log(chalk.cyan(` cd ${projectName}`));
console.log(chalk.cyan(" pnpm install"));
console.log(chalk.cyan(" pnpm dev\n"));
} catch (error) {
console.error(chalk.red("Error creating project:"), error);
process.exit(1);
}
});
program.parse();