main
1#!/bin/bash
2
3# Exit on error
4set -e
5
6# Detect Node version
7NODE_VERSION=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
8
9echo "๐ Detected Node.js version: $NODE_VERSION"
10
11if [ "$NODE_VERSION" -lt 18 ]; then
12 echo "โ Error: Node.js 18 or higher is required"
13 echo " Current version: $(node -v)"
14 exit 1
15fi
16
17# Set Vite version based on Node version
18if [ "$NODE_VERSION" -ge 20 ]; then
19 VITE_VERSION="latest"
20 echo "โ
Using Vite latest (Node 20+)"
21else
22 VITE_VERSION="5.4.11"
23 echo "โ
Using Vite $VITE_VERSION (Node 18 compatible)"
24fi
25
26# Detect OS and set sed syntax
27if [[ "$OSTYPE" == "darwin"* ]]; then
28 SED_INPLACE="sed -i ''"
29else
30 SED_INPLACE="sed -i"
31fi
32
33# Check if pnpm is installed
34if ! command -v pnpm &> /dev/null; then
35 echo "๐ฆ pnpm not found. Installing pnpm..."
36 npm install -g pnpm
37fi
38
39# Check if project name is provided
40if [ -z "$1" ]; then
41 echo "โ Usage: ./create-react-shadcn-complete.sh <project-name>"
42 exit 1
43fi
44
45PROJECT_NAME="$1"
46SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
47COMPONENTS_TARBALL="$SCRIPT_DIR/shadcn-components.tar.gz"
48
49# Check if components tarball exists
50if [ ! -f "$COMPONENTS_TARBALL" ]; then
51 echo "โ Error: shadcn-components.tar.gz not found in script directory"
52 echo " Expected location: $COMPONENTS_TARBALL"
53 exit 1
54fi
55
56echo "๐ Creating new React + Vite project: $PROJECT_NAME"
57
58# Create new Vite project (always use latest create-vite, pin vite version later)
59pnpm create vite "$PROJECT_NAME" --template react-ts
60
61# Navigate into project directory
62cd "$PROJECT_NAME"
63
64echo "๐งน Cleaning up Vite template..."
65$SED_INPLACE '/<link rel="icon".*vite\.svg/d' index.html
66$SED_INPLACE 's/<title>.*<\/title>/<title>'"$PROJECT_NAME"'<\/title>/' index.html
67
68echo "๐ฆ Installing base dependencies..."
69pnpm install
70
71# Pin Vite version for Node 18
72if [ "$NODE_VERSION" -lt 20 ]; then
73 echo "๐ Pinning Vite to $VITE_VERSION for Node 18 compatibility..."
74 pnpm add -D vite@$VITE_VERSION
75fi
76
77echo "๐ฆ Installing Tailwind CSS and dependencies..."
78pnpm install -D tailwindcss@3.4.1 postcss autoprefixer @types/node tailwindcss-animate
79pnpm install class-variance-authority clsx tailwind-merge lucide-react next-themes
80
81echo "โ๏ธ Creating Tailwind and PostCSS configuration..."
82cat > postcss.config.js << 'EOF'
83export default {
84 plugins: {
85 tailwindcss: {},
86 autoprefixer: {},
87 },
88}
89EOF
90
91echo "๐ Configuring Tailwind with shadcn theme..."
92cat > tailwind.config.js << 'EOF'
93/** @type {import('tailwindcss').Config} */
94module.exports = {
95 darkMode: ["class"],
96 content: [
97 "./index.html",
98 "./src/**/*.{js,ts,jsx,tsx}",
99 ],
100 theme: {
101 extend: {
102 colors: {
103 border: "hsl(var(--border))",
104 input: "hsl(var(--input))",
105 ring: "hsl(var(--ring))",
106 background: "hsl(var(--background))",
107 foreground: "hsl(var(--foreground))",
108 primary: {
109 DEFAULT: "hsl(var(--primary))",
110 foreground: "hsl(var(--primary-foreground))",
111 },
112 secondary: {
113 DEFAULT: "hsl(var(--secondary))",
114 foreground: "hsl(var(--secondary-foreground))",
115 },
116 destructive: {
117 DEFAULT: "hsl(var(--destructive))",
118 foreground: "hsl(var(--destructive-foreground))",
119 },
120 muted: {
121 DEFAULT: "hsl(var(--muted))",
122 foreground: "hsl(var(--muted-foreground))",
123 },
124 accent: {
125 DEFAULT: "hsl(var(--accent))",
126 foreground: "hsl(var(--accent-foreground))",
127 },
128 popover: {
129 DEFAULT: "hsl(var(--popover))",
130 foreground: "hsl(var(--popover-foreground))",
131 },
132 card: {
133 DEFAULT: "hsl(var(--card))",
134 foreground: "hsl(var(--card-foreground))",
135 },
136 },
137 borderRadius: {
138 lg: "var(--radius)",
139 md: "calc(var(--radius) - 2px)",
140 sm: "calc(var(--radius) - 4px)",
141 },
142 keyframes: {
143 "accordion-down": {
144 from: { height: "0" },
145 to: { height: "var(--radix-accordion-content-height)" },
146 },
147 "accordion-up": {
148 from: { height: "var(--radix-accordion-content-height)" },
149 to: { height: "0" },
150 },
151 },
152 animation: {
153 "accordion-down": "accordion-down 0.2s ease-out",
154 "accordion-up": "accordion-up 0.2s ease-out",
155 },
156 },
157 },
158 plugins: [require("tailwindcss-animate")],
159}
160EOF
161
162# Add Tailwind directives and CSS variables to index.css
163echo "๐จ Adding Tailwind directives and CSS variables..."
164cat > src/index.css << 'EOF'
165@tailwind base;
166@tailwind components;
167@tailwind utilities;
168
169@layer base {
170 :root {
171 --background: 0 0% 100%;
172 --foreground: 0 0% 3.9%;
173 --card: 0 0% 100%;
174 --card-foreground: 0 0% 3.9%;
175 --popover: 0 0% 100%;
176 --popover-foreground: 0 0% 3.9%;
177 --primary: 0 0% 9%;
178 --primary-foreground: 0 0% 98%;
179 --secondary: 0 0% 96.1%;
180 --secondary-foreground: 0 0% 9%;
181 --muted: 0 0% 96.1%;
182 --muted-foreground: 0 0% 45.1%;
183 --accent: 0 0% 96.1%;
184 --accent-foreground: 0 0% 9%;
185 --destructive: 0 84.2% 60.2%;
186 --destructive-foreground: 0 0% 98%;
187 --border: 0 0% 89.8%;
188 --input: 0 0% 89.8%;
189 --ring: 0 0% 3.9%;
190 --radius: 0.5rem;
191 }
192
193 .dark {
194 --background: 0 0% 3.9%;
195 --foreground: 0 0% 98%;
196 --card: 0 0% 3.9%;
197 --card-foreground: 0 0% 98%;
198 --popover: 0 0% 3.9%;
199 --popover-foreground: 0 0% 98%;
200 --primary: 0 0% 98%;
201 --primary-foreground: 0 0% 9%;
202 --secondary: 0 0% 14.9%;
203 --secondary-foreground: 0 0% 98%;
204 --muted: 0 0% 14.9%;
205 --muted-foreground: 0 0% 63.9%;
206 --accent: 0 0% 14.9%;
207 --accent-foreground: 0 0% 98%;
208 --destructive: 0 62.8% 30.6%;
209 --destructive-foreground: 0 0% 98%;
210 --border: 0 0% 14.9%;
211 --input: 0 0% 14.9%;
212 --ring: 0 0% 83.1%;
213 }
214}
215
216@layer base {
217 * {
218 @apply border-border;
219 }
220 body {
221 @apply bg-background text-foreground;
222 }
223}
224EOF
225
226# Add path aliases to tsconfig.json
227echo "๐ง Adding path aliases to tsconfig.json..."
228node -e "
229const fs = require('fs');
230const config = JSON.parse(fs.readFileSync('tsconfig.json', 'utf8'));
231config.compilerOptions = config.compilerOptions || {};
232config.compilerOptions.baseUrl = '.';
233config.compilerOptions.paths = { '@/*': ['./src/*'] };
234fs.writeFileSync('tsconfig.json', JSON.stringify(config, null, 2));
235"
236
237# Add path aliases to tsconfig.app.json
238echo "๐ง Adding path aliases to tsconfig.app.json..."
239node -e "
240const fs = require('fs');
241const path = 'tsconfig.app.json';
242const content = fs.readFileSync(path, 'utf8');
243// Remove comments manually
244const lines = content.split('\n').filter(line => !line.trim().startsWith('//'));
245const jsonContent = lines.join('\n');
246const config = JSON.parse(jsonContent.replace(/\/\*[\s\S]*?\*\//g, '').replace(/,(\s*[}\]])/g, '\$1'));
247config.compilerOptions = config.compilerOptions || {};
248config.compilerOptions.baseUrl = '.';
249config.compilerOptions.paths = { '@/*': ['./src/*'] };
250fs.writeFileSync(path, JSON.stringify(config, null, 2));
251"
252
253# Update vite.config.ts
254echo "โ๏ธ Updating Vite configuration..."
255cat > vite.config.ts << 'EOF'
256import path from "path";
257import react from "@vitejs/plugin-react";
258import { defineConfig } from "vite";
259
260export default defineConfig({
261 plugins: [react()],
262 resolve: {
263 alias: {
264 "@": path.resolve(__dirname, "./src"),
265 },
266 },
267});
268EOF
269
270# Install all shadcn/ui dependencies
271echo "๐ฆ Installing shadcn/ui dependencies..."
272pnpm install @radix-ui/react-accordion @radix-ui/react-aspect-ratio @radix-ui/react-avatar @radix-ui/react-checkbox @radix-ui/react-collapsible @radix-ui/react-context-menu @radix-ui/react-dialog @radix-ui/react-dropdown-menu @radix-ui/react-hover-card @radix-ui/react-label @radix-ui/react-menubar @radix-ui/react-navigation-menu @radix-ui/react-popover @radix-ui/react-progress @radix-ui/react-radio-group @radix-ui/react-scroll-area @radix-ui/react-select @radix-ui/react-separator @radix-ui/react-slider @radix-ui/react-slot @radix-ui/react-switch @radix-ui/react-tabs @radix-ui/react-toast @radix-ui/react-toggle @radix-ui/react-toggle-group @radix-ui/react-tooltip
273pnpm install sonner cmdk vaul embla-carousel-react react-day-picker react-resizable-panels date-fns react-hook-form @hookform/resolvers zod
274
275# Extract shadcn components from tarball
276echo "๐ฆ Extracting shadcn/ui components..."
277tar -xzf "$COMPONENTS_TARBALL" -C src/
278
279# Create components.json for reference
280echo "๐ Creating components.json config..."
281cat > components.json << 'EOF'
282{
283 "$schema": "https://ui.shadcn.com/schema.json",
284 "style": "default",
285 "rsc": false,
286 "tsx": true,
287 "tailwind": {
288 "config": "tailwind.config.js",
289 "css": "src/index.css",
290 "baseColor": "slate",
291 "cssVariables": true,
292 "prefix": ""
293 },
294 "aliases": {
295 "components": "@/components",
296 "utils": "@/lib/utils",
297 "ui": "@/components/ui",
298 "lib": "@/lib",
299 "hooks": "@/hooks"
300 }
301}
302EOF
303
304echo "โ
Setup complete! You can now use Tailwind CSS and shadcn/ui in your project."
305echo ""
306echo "๐ฆ Included components (40+ total):"
307echo " - accordion, alert, aspect-ratio, avatar, badge, breadcrumb"
308echo " - button, calendar, card, carousel, checkbox, collapsible"
309echo " - command, context-menu, dialog, drawer, dropdown-menu"
310echo " - form, hover-card, input, label, menubar, navigation-menu"
311echo " - popover, progress, radio-group, resizable, scroll-area"
312echo " - select, separator, sheet, skeleton, slider, sonner"
313echo " - switch, table, tabs, textarea, toast, toggle, toggle-group, tooltip"
314echo ""
315echo "To start developing:"
316echo " cd $PROJECT_NAME"
317echo " pnpm dev"
318echo ""
319echo "๐ Import components like:"
320echo " import { Button } from '@/components/ui/button'"
321echo " import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'"
322echo " import { Dialog, DialogContent, DialogTrigger } from '@/components/ui/dialog'"