create-nodex
Version:
CLI tool to create modern Node.js projects with TypeScript, AI capabilities, and more
130 lines (129 loc) • 4.46 kB
JavaScript
import fsExtra from "fs-extra";
import { join, dirname, resolve } from "path";
import { chdir } from "process";
import { execSync } from "child_process";
import { readFileSync, writeFileSync } from "fs";
import { fileURLToPath } from "url";
import chalk from "chalk";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
export const questions = [
{
type: "input",
name: "projectName",
message: "Project name:",
validate: (input) => /^[a-z\-]+$/.test(input),
},
{
type: "confirm",
name: "useTypeScript",
message: "Use TypeScript?",
default: true,
},
{
type: "confirm",
name: "hotReload",
message: "Enable hot reload?",
default: true,
},
{
type: "confirm",
name: "addTesting",
message: "Add Jest testing?",
default: true,
},
];
export async function init(options) {
// Add signal handlers for graceful shutdown
process.on("SIGINT", () => {
console.log("\nExiting gracefully...");
process.exit(0);
});
process.on("SIGTERM", () => {
console.log("\nExiting gracefully...");
process.exit(0);
});
// Get current working directory before changing
const currentDir = process.cwd();
// Create project directory with absolute path
const targetDir = resolve(currentDir, options.projectName);
// Get template dir with absolute path from package location
// Assuming 'templates' is in the parent directory of __dirname (i.e., 'src/../templates')
const templateDir = join(__dirname, "..", // Go up one level from 'src/commands' to 'src'
"templates", options.useTypeScript ? "typescript" : "javascript");
// Ensure target directory exists
fsExtra.ensureDirSync(targetDir);
// Copy template files with filtering
fsExtra.copySync(templateDir, targetDir, {
filter: (src) => {
// Don't copy index.js to TypeScript projects
if (options.useTypeScript && src.endsWith("index.js")) {
return false;
}
// Don't copy index.ts to JavaScript projects
if (!options.useTypeScript && src.endsWith("index.ts")) {
return false;
}
return true;
},
});
// Update package.json
chdir(targetDir);
const pkgPath = join(targetDir, "package.json");
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
pkg.name = options.projectName;
pkg.scripts = {
...(options.useTypeScript
? {
start: "node dist/index.js",
build: "tsc",
dev: "nodemon",
}
: {
start: "node src/index.js",
dev: "nodemon",
}),
...(options.addTesting && { test: "jest" }),
};
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
// Install dependencies
const deps = [];
if (options.useTypeScript) {
deps.push("typescript", "@types/node", "tsx");
}
if (options.hotReload) {
deps.push("nodemon");
}
if (options.addTesting) {
deps.push("jest");
if (options.useTypeScript) {
deps.push("@types/jest", "ts-jest");
}
}
if (deps.length > 0) {
console.log("Installing dependencies...");
execSync(`npm install --save-dev ${deps.join(" ")}`, { stdio: "inherit" });
}
// Create nodemon.json if hot reload is enabled
if (options.hotReload) {
const nodemonConfig = options.useTypeScript
? {
watch: ["src"],
ext: "ts",
ignore: ["dist"],
exec: "npx tsx src/index.ts",
}
: {
watch: ["src"],
ext: "js",
ignore: ["node_modules"],
exec: "node src/index.js",
};
writeFileSync(join(targetDir, "nodemon.json"), JSON.stringify(nodemonConfig, null, 2));
}
console.log(`\n${chalk.bgGreen.black(" SUCCESS ")} ${chalk.green.bold(`Project ${options.projectName} created successfully`)} ${chalk.green("🚀🚀🚀")}`);
console.log(`\n${chalk.cyan.bold("To get started:")}`);
console.log(` ${chalk.yellow("cd")} ${chalk.yellow.bold(options.projectName)}`);
console.log(` ${chalk.yellow("npm run dev")}`);
}