Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions bin/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ program
"--folders",
"Generate recommended folder structure"
)
.option(
"--tailwind",
"Set up Tailwind CSS"
)
.parse();

const projectName = program.args[0];
Expand Down
26 changes: 13 additions & 13 deletions src/cleaners/cra.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,10 @@ const {
writeFile,
} = require("../utils/files");

function cleanCRA(projectPath, typescript) {
function cleanCRA(projectPath, typescript, tailwind) {
const ext = typescript ? "tsx" : "js";

writeFile(
projectPath,
`src/App.${ext}`,
`function App() {
const appContent = `function App() {
return (
<div>
<h1>App</h1>
Expand All @@ -18,16 +15,19 @@ function cleanCRA(projectPath, typescript) {
}

export default App;
`
);
`;

writeFile(projectPath, `src/App.${ext}`, appContent);

const indexCSSImport = tailwind ? `import "./index.css";\n` : "";

writeFile(
projectPath,
`src/index.${ext}`,
typescript
? `import React from "react";
typescript
? `import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
${indexCSSImport}import App from "./App";

const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
Expand All @@ -39,9 +39,9 @@ root.render(
</React.StrictMode>
);
`
: `import React from "react";
: `import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
${indexCSSImport}import App from "./App";

const root = ReactDOM.createRoot(
document.getElementById("root")
Expand Down Expand Up @@ -69,4 +69,4 @@ root.render(
});
}

module.exports = cleanCRA;
module.exports = cleanCRA;
57 changes: 46 additions & 11 deletions src/creators/vite.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@ export default defineConfig({
})
`;

const VITE_CONFIG_JS_TAILWIND = `import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
plugins: [react(), tailwindcss()],
})
`;

const INDEX_HTML = (projectName) => `<!doctype html>
<html lang="en">
<head>
Expand Down Expand Up @@ -86,6 +95,30 @@ ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
)
`;

const MAIN_JSX_TAILWIND = `import React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import App from './App'

ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>
)
`;

const MAIN_TSX_TAILWIND = `import React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import App from './App'

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<App />
</React.StrictMode>
)
`;

const APP_JSX = `function App() {
return (
<div>
Expand All @@ -97,6 +130,7 @@ const APP_JSX = `function App() {
export default App
`;


const TSCONFIG = JSON.stringify({
compilerOptions: {
target: "ES2020",
Expand All @@ -118,7 +152,7 @@ const TSCONFIG = JSON.stringify({
include: ["src"],
}, null, 2);

function createVite(projectName, typescript) {
function createVite(projectName, typescript, tailwind) {
const projectPath = path.join(process.cwd(), projectName);
const ext = typescript ? "tsx" : "jsx";

Expand All @@ -135,7 +169,7 @@ function createVite(projectName, typescript) {
// vite.config
fs.writeFileSync(
path.join(projectPath, `vite.config.${typescript ? "ts" : "js"}`),
VITE_CONFIG_JS
tailwind ? VITE_CONFIG_JS_TAILWIND : VITE_CONFIG_JS
);

// index.html
Expand All @@ -144,16 +178,17 @@ function createVite(projectName, typescript) {
typescript ? INDEX_HTML_TS(projectName) : INDEX_HTML(projectName)
);

// src/main
fs.writeFileSync(
path.join(projectPath, `src/main.${ext}`),
typescript ? MAIN_TSX : MAIN_JSX
);
// src/main — include index.css import when tailwind is active
const mainContent = tailwind
? (typescript ? MAIN_TSX_TAILWIND : MAIN_JSX_TAILWIND)
: (typescript ? MAIN_TSX : MAIN_JSX);

fs.writeFileSync(path.join(projectPath, `src/main.${ext}`), mainContent);

// src/App
// src/App — minimal starter component
fs.writeFileSync(
path.join(projectPath, `src/App.${ext}`),
APP_JSX
path.join(projectPath, `src/App.${ext}`),
APP_JSX
);

// tsconfig if needed
Expand All @@ -172,4 +207,4 @@ function createVite(projectName, typescript) {
});
}

module.exports = createVite;
module.exports = createVite;
36 changes: 36 additions & 0 deletions src/generators/tailwind.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const { execSync } = require("child_process");
const { writeFile } = require("../utils/files");

// Tailwind v4: PostCSS plugin for CRA (webpack-based)
const POSTCSS_CONFIG_CRA = `module.exports = {
plugins: {
'@tailwindcss/postcss': {},
},
}
`;

// Tailwind v4: single CSS import replaces the three @tailwind directives
const INDEX_CSS = `@import "tailwindcss";
`;

function generateTailwind(projectPath, cra) {
if (cra) {
// CRA uses PostCSS — write the postcss config
writeFile(projectPath, "postcss.config.js", POSTCSS_CONFIG_CRA);
}
// Vite uses @tailwindcss/vite plugin (wired into vite.config) — no postcss config needed

writeFile(projectPath, "src/index.css", INDEX_CSS);

const deps = cra
? "tailwindcss @tailwindcss/postcss"
: "tailwindcss @tailwindcss/vite";

execSync(`npm install -D ${deps}`, {
cwd: projectPath,
stdio: "inherit",
shell: true,
});
}

module.exports = generateTailwind;
16 changes: 13 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const createVite = require("./creators/vite");
const cleanCRA = require("./cleaners/cra");

const generateFolders = require("./generators/folders");
const generateTailwind = require("./generators/tailwind");

const { success, info, error } = require("./utils/logger");

Expand All @@ -35,6 +36,7 @@ async function run(projectName, options) {

const useCRA = options.cra;
const typescript = options.ts;
const tailwind = options.tailwind;

info(`Using ${useCRA ? "Create React App" : "Vite"}`);

Expand All @@ -45,7 +47,7 @@ async function run(projectName, options) {
createCRA(projectName, typescript);
} else {
spinner.stop(); // npm install output needs to show
createVite(projectName, typescript);
createVite(projectName, typescript, tailwind);
}
} catch (e) {
spinner.stop();
Expand All @@ -63,11 +65,19 @@ async function run(projectName, options) {
// CRA needs cleanup — Vite is already scaffolded clean
if (useCRA) {
spinner.start("Cleaning starter files...");
cleanCRA(projectPath, typescript);
cleanCRA(projectPath, typescript, tailwind);
spinner.stop();
success("Starter boilerplate removed");
}

// Tailwind setup
if (tailwind) {
spinner.stop();
info("Setting up Tailwind CSS...");
generateTailwind(projectPath, useCRA);
success("Tailwind CSS configured");
}

// Generate folders
if (options.folders) {
generateFolders(projectPath);
Expand All @@ -85,4 +95,4 @@ async function run(projectName, options) {
}
}

module.exports = run;
module.exports = run;