vite-custom-setup
Version:
A CLI tool to set up Vite with Tailwind CSS and Framer Motion for React projects.
225 lines (201 loc) • 6.16 kB
JavaScript
import fs from "fs";
import { execSync } from "child_process";
import inquirer from "inquirer";
// Function to execute shell commands with error handling
const runCommand = (command) => {
try {
execSync(command, { stdio: "inherit" });
return true;
} catch (error) {
console.error(`\x1b[31mFailed to execute command: ${command}\x1b[0m`);
return false;
}
};
// Create Tailwind CSS configuration
const setupTailwind = () => {
console.log("\x1b[34mSetting up Tailwind CSS...\x1b[0m");
if (
!runCommand("npm install -D tailwindcss postcss autoprefixer") ||
!runCommand("npx tailwindcss init -p")
) {
console.error("\x1b[31mFailed to set up Tailwind CSS.\x1b[0m");
process.exit(1);
}
// Update Tailwind configuration file
try {
const tailwindConfig = `
module.exports = {
content: ["./src/**/*.{js,jsx,ts,tsx}"],
theme: {
extend: {},
},
plugins: [],
};
`;
fs.writeFileSync("./tailwind.config.js", tailwindConfig);
// Add Tailwind directives to CSS
const cssContent = `
@tailwind base;
@tailwind components;
@tailwind utilities;
`;
fs.writeFileSync("./src/index.css", cssContent);
console.log("\x1b[32m✓ Tailwind CSS setup complete.\x1b[0m");
} catch (error) {
console.error("\x1b[31mFailed to configure Tailwind CSS.\x1b[0m", error);
process.exit(1);
}
};
// Create example component
const createExampleComponent = (
installTailwind,
installFramer,
installReactIcons,
installLucideReact,
installAxios
) => {
const exampleContent = `
import React from 'react'${
installFramer ? "\nimport { motion } from 'framer-motion'" : ""
}${installReactIcons ? "\nimport { FaReact } from 'react-icons/fa'" : ""}${
installLucideReact ? "\nimport { LucideIcon } from 'lucide-react'" : ""
}${installAxios ? "\nimport axios from 'axios'" : ""}
export default function Example() {
${
installAxios
? "\n React.useEffect(() => { axios.get('https://api.example.com').then(console.log); }, []);"
: ""
}
return (
${installFramer ? "<motion.div" : "<div"}
${
installFramer
? `
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}`
: ""
}
className="min-h-screen flex items-center justify-center bg-gray-100"
>
<div className="p-6 max-w-sm mx-auto bg-white rounded-xl shadow-lg flex items-center space-x-4">
${installReactIcons ? "<FaReact size={32} color='blue' />" : ""} ${
installLucideReact ? "<LucideIcon size={32} />" : ""
}
<div className="text-xl font-medium text-black">Your setup is complete!</div>
</div>
${installFramer ? "</motion.div>" : "</div>"}
)}`;
try {
fs.writeFileSync("./src/components/Example.jsx", exampleContent);
console.log("\x1b[32m✓ Example component created.\x1b[0m");
} catch (error) {
console.error("\x1b[31m✗ Failed to create example component.\x1b[0m");
}
};
// Update main App.jsx
const updateMainApp = () => {
const appContent = `
import Example from './components/Example'
function App() {
return (
<div>
<Example />
</div>
)
}
export default App`;
try {
fs.writeFileSync("./src/App.jsx", appContent);
console.log("\x1b[32m✓ App.jsx updated.\x1b[0m");
} catch (error) {
console.error("\x1b[31m✗ Failed to update App.jsx.\x1b[0m");
}
};
// Main setup function
const setupProject = async () => {
console.log(
"\x1b[34m\nVite Custom Setup - React + Tailwind + Framer Motion + Icons + Axios\x1b[0m\n"
);
const answers = await inquirer.prompt([
{
type: "input",
name: "projectName",
message: "Enter your project name:",
default: "my-vite-app",
validate: (input) => {
if (/^([A-Za-z\-_\d])+$/.test(input)) return true;
return "Project name may only include letters, numbers, underscores and hashes.";
},
},
{
type: "confirm",
name: "installTailwind",
message: "Would you like to install Tailwind CSS?",
default: true,
},
{
type: "confirm",
name: "installFramer",
message: "Would you like to install Framer Motion?",
default: true,
},
{
type: "confirm",
name: "installReactIcons",
message: "Would you like to install React Icons?",
default: true,
},
{
type: "confirm",
name: "installLucideReact",
message: "Would you like to install Lucide React?",
default: true,
},
{
type: "confirm",
name: "installAxios",
message: "Would you like to install Axios?",
default: true,
},
]);
const {
projectName,
installTailwind,
installFramer,
installReactIcons,
installLucideReact,
installAxios,
} = answers;
console.log("\n\x1b[34mCreating Vite project...\x1b[0m");
if (!runCommand(`npm create vite@latest ${projectName} -- --template react`))
process.exit(1);
process.chdir(projectName);
if (!runCommand("npm install")) process.exit(1);
if (installTailwind) setupTailwind();
if (installFramer) runCommand("npm install framer-motion");
if (installReactIcons) runCommand("npm install react-icons");
if (installLucideReact) runCommand("npm install lucide-react");
if (installAxios) runCommand("npm install axios");
createExampleComponent(
installTailwind,
installFramer,
installReactIcons,
installLucideReact,
installAxios
);
updateMainApp();
console.log("\n\x1b[32m✨ Setup complete!\x1b[0m");
console.log(
`\n\x1b[34mNext steps:\x1b[0m\n cd ${projectName}\n npm run dev\n`
);
};
process.on("uncaughtException", (err) => {
console.error("\x1b[31mAn error occurred:\x1b[0m", err);
process.exit(1);
});
setupProject().catch((error) => {
console.error("\x1b[31mSetup failed:\x1b[0m", error);
process.exit(1);
});