boiler-code-create
Version:
A blazing-fast CLI tool to scaffold a full-featured MERN Stack project with React + Vite + TailwindCSS v4 and JWT Auth backend
344 lines (274 loc) ⢠10.5 kB
JavaScript
#!/usr/bin/env node
const { execSync } = require("child_process");
const fs = require("fs");
const path = require("path");
const folderName = process.argv[2] || "my-mern-app";
const folderPath = path.join(process.cwd(),folderName);
const mainfolder= fs.mkdirSync(folderPath);
console.log(`\nš Creating React project: ${folderName}...`);
// Step 1: Create Vite + React app
execSync(`npm create vite@latest frontend -- --template react`, {
stdio: "inherit",
cwd:folderPath
});
const appPath=path.join(folderPath,"frontend")
// Step 2: Install frontend dependencies
execSync(`npm install`, { cwd: appPath, stdio: "inherit" });
// Step 3: Install TailwindCSS v4
console.log(`\nšØ Installing TailwindCSS v4...`);
execSync(`npm install tailwindcss @tailwindcss/vite`, {
cwd: appPath,
stdio: "inherit",
shell: true,
});
// Step 4: Write vite.config.js manually
const viteConfig = `
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
plugins: [
react(),
tailwindcss()
],
});
`;
fs.writeFileSync(`${appPath}/vite.config.js`,viteConfig);
// Step 5: Create index.css and app.jsx
const indexCss = `
@import "tailwindcss"
`;
fs.writeFileSync(`${appPath}/src/index.css`, indexCss);
const appdata=`import { useState } from "react";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./index.css";
const features = [
{
title: "āļø Instant Fullstack Setup",
description: "Spin up a complete MERN project in seconds with one CLI command.",
},
{
title: "š JWT Auth + Bcrypt",
description: "Authentication is preconfigured with JWT and bcrypt hashing.",
},
{
title: "š Clean Project Structure",
description: "Organized folder structure for models, routes, controllers, and more.",
},
{
title: "šØ TailwindCSS v4 Integrated",
description: "Beautiful styling powered by the latest Tailwind v4 setup.",
},
];
export default function App() {
const [count, setCount] = useState(0);
return (
<div className="min-h-screen bg-gradient-to-tr from-white via-indigo-50 to-purple-100 text-gray-800 flex flex-col">
{/* Header */}
<header className="text-center py-10">
<div className="flex justify-center items-center space-x-8 mb-6">
<a href="https://vitejs.dev" target="_blank" rel="noreferrer">
<img src={viteLogo} className="h-16 hover:scale-110 transition-transform" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank" rel="noreferrer">
<img src={reactLogo} className="h-16 hover:scale-110 transition-transform" alt="React logo" />
</a>
</div>
<h1 className="text-4xl sm:text-5xl font-extrabold text-indigo-600 drop-shadow-lg">boiler-code-create</h1>
<p className="mt-4 text-lg sm:text-xl max-w-2xl mx-auto text-gray-600">
Launch your fullstack MERN app in seconds. Backend + Frontend ready to build.
</p>
</header>
{/* Feature Section */}
<section className="py-12 px-6 sm:px-12 grid grid-cols-1 sm:grid-cols-2 gap-6 max-w-5xl mx-auto">
{features.map((feature, idx) => (
<div key={idx} className="bg-white p-6 rounded-xl shadow hover:shadow-xl transition-all border border-gray-100">
<h3 className="text-xl font-semibold text-indigo-700 mb-2">{feature.title}</h3>
<p className="text-gray-600 text-sm">{feature.description}</p>
</div>
))}
</section>
{/* Interactive Counter */}
<section className="text-center my-16">
<h2 className="text-2xl font-semibold mb-4 text-indigo-700">ā” Try Hot Reloading</h2>
<button
onClick={() => setCount(count + 1)}
className="px-6 py-2 text-white bg-indigo-600 rounded-lg hover:bg-indigo-700 shadow transition-all"
>
Count is {count}
</button>
<p className="text-sm mt-3 text-gray-500">Edit <code>App.jsx</code> and save to test live updates!</p>
</section>
{/* Footer */}
<footer className="mt-auto bg-white border-t py-4 text-center text-sm text-gray-500">
Ā© {new Date().getFullYear()} boiler-code-create. Crafted with ā¤ļø for developers.
</footer>
</div>
);
}
`
fs.writeFileSync(`${appPath}/src/App.jsx`,appdata);
// Step 6: Install React Router
console.log(`\nš Installing React Router...`);
execSync(`npm install react-router-dom`, {
cwd: appPath,
stdio: "inherit",
});
console.log(`\nā
Your React + Tailwind v4 MERN app is ready!`);
// Step 7: Create backend folder
console.log(`\nš Creating backend folder...`);
const backendFolderName = "backend";
const backendPath=path.join(folderPath,backendFolderName);
fs.mkdirSync(backendPath);
execSync(`npm init -y `,{
cwd:backendPath,
stdio:"inherit"
})
console.log(`\n Installing express mongoose dotenv cors jsonwebtoken bcrypt ...`);
execSync(`npm i express mongoose dotenv cors jsonwebtoken nodemon`,{
cwd:backendPath,
stdio:"inherit"
})
const indexdata=`
const express = require("express");
const cors = require("cors");
require("dotenv").config();
const connectDB = require("./config/db");
const userRoutes = require("./routes/userRoutes");
const { notFound, errorHandler } = require("./middleware/errorMiddleware");
const app = express();
connectDB();
app.use(cors());
app.use(express.json());
app.use("/api/users", userRoutes);
app.get("/", (req, res) => {
res.send("API is running");
});
app.use(notFound);
app.use(errorHandler);
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(\`Server is running on port \${PORT}\`);
});
`
console.log("creating index.js...")
fs.writeFileSync(path.join(backendPath,"index.js"),indexdata)
const envData=`
PORT=5000
MONGO_URI=mongodb://localhost:27017/mydatabase
JWT_SECRET=yourStrongJWTSecret
`
console.log("creating .env...");
fs.writeFileSync(path.join(backendPath,".env"),envData)
const mongodata=`const mongoose = require('mongoose');
const connectDB = async () => {
try {
await mongoose.connect(process.env.MONGO_URI);
console.log('ā
MongoDB connected successfully');
} catch (err) {
console.error('ā MongoDB connection error:', err);
process.exit(1); // Stop the server on failure
}
};
module.exports = connectDB;
`
fs.mkdirSync(path.join(backendPath,"config"));
console.log("creating db.js...");
fs.writeFileSync(path.join(backendPath,"config","db.js"),mongodata)
const middlewarePath=path.join(backendPath,"middleware")
const routesPath=path.join(backendPath,"routes")
const modelsPath=path.join(backendPath,"models")
const controllerspath=path.join(backendPath,"controllers");
const userModelData = `
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema(
{
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
},
{ timestamps: true }
);
module.exports = mongoose.model("User", userSchema);
`;
fs.mkdirSync(modelsPath);
fs.writeFileSync(path.join(modelsPath, "userModel.js"), userModelData);
const controllerData = `
const User = require("../models/userModel");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const generateToken = (id) => {
return jwt.sign({ id }, process.env.JWT_SECRET, { expiresIn: "30d" });
};
exports.registerUser = async (req, res) => {
try {
const { name, email, password } = req.body;
const userExists = await User.findOne({ email });
if (userExists) return res.status(400).json({ message: "User already exists" });
const salt = await bcrypt.genSalt(10);
const hashed = await bcrypt.hash(password, salt);
const user = await User.create({ name, email, password: hashed });
res.status(201).json({
_id: user._id,
name: user.name,
email: user.email,
token: generateToken(user._id),
});
} catch (err) {
res.status(500).json({ message: err.message });
}
};
exports.loginUser = async (req, res) => {
try {
const { email, password } = req.body;
const user = await User.findOne({ email });
const isMatch = user && await bcrypt.compare(password, user.password);
if (!isMatch) return res.status(401).json({ message: "Invalid credentials" });
res.json({
_id: user._id,
name: user.name,
email: user.email,
token: generateToken(user._id),
});
} catch (err) {
res.status(500).json({ message: err.message });
}
};
`;
fs.mkdirSync(controllerspath);
fs.writeFileSync(path.join(controllerspath, "userController.js"), controllerData);
const routesData = `
const express = require("express");
const router = express.Router();
const { registerUser, loginUser } = require("../controllers/userController");
router.post("/register", registerUser);
router.post("/login", loginUser);
module.exports = router;
`;
fs.mkdirSync(routesPath);
fs.writeFileSync(path.join(routesPath, "userRoutes.js"), routesData);
const middlewareData = `
const notFound = (req, res, next) => {
const error = new Error(\`Not Found - \${req.originalUrl}\`);
res.status(404);
next(error);
};
const errorHandler = (err, req, res, next) => {
const statusCode = res.statusCode === 200 ? 500 : res.statusCode;
res.status(statusCode).json({
message: err.message,
stack: process.env.NODE_ENV === "production" ? null : err.stack,
});
};
module.exports = { notFound, errorHandler };
`;
fs.mkdirSync(middlewarePath);
fs.writeFileSync(path.join(middlewarePath, "errorMiddleware.js"), middlewareData);
console.log("\nā
All backend files created successfully!");
console.log("\nš To run backend:");
console.log(`š cd ${folderName}/backend`);
console.log(`š node index.js`);
console.log("\n\n for running frontend")
console.log(`š cd ${folderName}`);
console.log(`š cd frontend && npm run dev `);