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'"