#!/bin/bash # Exit on error set -e # Detect Node version NODE_VERSION=$(node -v | cut -d'v' -f2 | cut -d'.' -f1) echo "๐Ÿ” Detected Node.js version: $NODE_VERSION" if [ "$NODE_VERSION" -lt 18 ]; then echo "โŒ Error: Node.js 18 or higher is required" echo " Current version: $(node -v)" exit 1 fi # Set Vite version based on Node version if [ "$NODE_VERSION" -ge 20 ]; then VITE_VERSION="latest" echo "โœ… Using Vite latest (Node 20+)" else VITE_VERSION="5.4.11" echo "โœ… Using Vite $VITE_VERSION (Node 18 compatible)" fi # Detect OS and set sed syntax if [[ "$OSTYPE" == "darwin"* ]]; then SED_INPLACE="sed -i ''" else SED_INPLACE="sed -i" fi # Check if pnpm is installed if ! command -v pnpm &> /dev/null; then echo "๐Ÿ“ฆ pnpm not found. Installing pnpm..." npm install -g pnpm fi # Check if project name is provided if [ -z "$1" ]; then echo "โŒ Usage: ./create-react-shadcn-complete.sh " exit 1 fi PROJECT_NAME="$1" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" COMPONENTS_TARBALL="$SCRIPT_DIR/shadcn-components.tar.gz" # Check if components tarball exists if [ ! -f "$COMPONENTS_TARBALL" ]; then echo "โŒ Error: shadcn-components.tar.gz not found in script directory" echo " Expected location: $COMPONENTS_TARBALL" exit 1 fi echo "๐Ÿš€ Creating new React + Vite project: $PROJECT_NAME" # Create new Vite project (always use latest create-vite, pin vite version later) pnpm create vite "$PROJECT_NAME" --template react-ts # Navigate into project directory cd "$PROJECT_NAME" echo "๐Ÿงน Cleaning up Vite template..." $SED_INPLACE '/.*<\/title>/'"$PROJECT_NAME"'<\/title>/' index.html echo "๐Ÿ“ฆ Installing base dependencies..." pnpm install # Pin Vite version for Node 18 if [ "$NODE_VERSION" -lt 20 ]; then echo "๐Ÿ“Œ Pinning Vite to $VITE_VERSION for Node 18 compatibility..." pnpm add -D vite@$VITE_VERSION fi echo "๐Ÿ“ฆ Installing Tailwind CSS and dependencies..." pnpm install -D tailwindcss@3.4.1 postcss autoprefixer @types/node tailwindcss-animate pnpm install class-variance-authority clsx tailwind-merge lucide-react next-themes echo "โš™๏ธ Creating Tailwind and PostCSS configuration..." cat > postcss.config.js << 'EOF' export default { plugins: { tailwindcss: {}, autoprefixer: {}, }, } EOF echo "๐Ÿ“ Configuring Tailwind with shadcn theme..." cat > tailwind.config.js << 'EOF' /** @type {import('tailwindcss').Config} */ module.exports = { darkMode: ["class"], content: [ "./index.html", "./src/**/*.{js,ts,jsx,tsx}", ], theme: { extend: { colors: { border: "hsl(var(--border))", input: "hsl(var(--input))", ring: "hsl(var(--ring))", background: "hsl(var(--background))", foreground: "hsl(var(--foreground))", primary: { DEFAULT: "hsl(var(--primary))", foreground: "hsl(var(--primary-foreground))", }, secondary: { DEFAULT: "hsl(var(--secondary))", foreground: "hsl(var(--secondary-foreground))", }, destructive: { DEFAULT: "hsl(var(--destructive))", foreground: "hsl(var(--destructive-foreground))", }, muted: { DEFAULT: "hsl(var(--muted))", foreground: "hsl(var(--muted-foreground))", }, accent: { DEFAULT: "hsl(var(--accent))", foreground: "hsl(var(--accent-foreground))", }, popover: { DEFAULT: "hsl(var(--popover))", foreground: "hsl(var(--popover-foreground))", }, card: { DEFAULT: "hsl(var(--card))", foreground: "hsl(var(--card-foreground))", }, }, borderRadius: { lg: "var(--radius)", md: "calc(var(--radius) - 2px)", sm: "calc(var(--radius) - 4px)", }, keyframes: { "accordion-down": { from: { height: "0" }, to: { height: "var(--radix-accordion-content-height)" }, }, "accordion-up": { from: { height: "var(--radix-accordion-content-height)" }, to: { height: "0" }, }, }, animation: { "accordion-down": "accordion-down 0.2s ease-out", "accordion-up": "accordion-up 0.2s ease-out", }, }, }, plugins: [require("tailwindcss-animate")], } EOF # Add Tailwind directives and CSS variables to index.css echo "๐ŸŽจ Adding Tailwind directives and CSS variables..." cat > src/index.css << 'EOF' @tailwind base; @tailwind components; @tailwind utilities; @layer base { :root { --background: 0 0% 100%; --foreground: 0 0% 3.9%; --card: 0 0% 100%; --card-foreground: 0 0% 3.9%; --popover: 0 0% 100%; --popover-foreground: 0 0% 3.9%; --primary: 0 0% 9%; --primary-foreground: 0 0% 98%; --secondary: 0 0% 96.1%; --secondary-foreground: 0 0% 9%; --muted: 0 0% 96.1%; --muted-foreground: 0 0% 45.1%; --accent: 0 0% 96.1%; --accent-foreground: 0 0% 9%; --destructive: 0 84.2% 60.2%; --destructive-foreground: 0 0% 98%; --border: 0 0% 89.8%; --input: 0 0% 89.8%; --ring: 0 0% 3.9%; --radius: 0.5rem; } .dark { --background: 0 0% 3.9%; --foreground: 0 0% 98%; --card: 0 0% 3.9%; --card-foreground: 0 0% 98%; --popover: 0 0% 3.9%; --popover-foreground: 0 0% 98%; --primary: 0 0% 98%; --primary-foreground: 0 0% 9%; --secondary: 0 0% 14.9%; --secondary-foreground: 0 0% 98%; --muted: 0 0% 14.9%; --muted-foreground: 0 0% 63.9%; --accent: 0 0% 14.9%; --accent-foreground: 0 0% 98%; --destructive: 0 62.8% 30.6%; --destructive-foreground: 0 0% 98%; --border: 0 0% 14.9%; --input: 0 0% 14.9%; --ring: 0 0% 83.1%; } } @layer base { * { @apply border-border; } body { @apply bg-background text-foreground; } } EOF # Add path aliases to tsconfig.json echo "๐Ÿ”ง Adding path aliases to tsconfig.json..." node -e " const fs = require('fs'); const config = JSON.parse(fs.readFileSync('tsconfig.json', 'utf8')); config.compilerOptions = config.compilerOptions || {}; config.compilerOptions.baseUrl = '.'; config.compilerOptions.paths = { '@/*': ['./src/*'] }; fs.writeFileSync('tsconfig.json', JSON.stringify(config, null, 2)); " # Add path aliases to tsconfig.app.json echo "๐Ÿ”ง Adding path aliases to tsconfig.app.json..." node -e " const fs = require('fs'); const path = 'tsconfig.app.json'; const content = fs.readFileSync(path, 'utf8'); // Remove comments manually const lines = content.split('\n').filter(line => !line.trim().startsWith('//')); const jsonContent = lines.join('\n'); const config = JSON.parse(jsonContent.replace(/\/\*[\s\S]*?\*\//g, '').replace(/,(\s*[}\]])/g, '\$1')); config.compilerOptions = config.compilerOptions || {}; config.compilerOptions.baseUrl = '.'; config.compilerOptions.paths = { '@/*': ['./src/*'] }; fs.writeFileSync(path, JSON.stringify(config, null, 2)); " # Update vite.config.ts echo "โš™๏ธ Updating Vite configuration..." cat > vite.config.ts << 'EOF' import path from "path"; import react from "@vitejs/plugin-react"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [react()], resolve: { alias: { "@": path.resolve(__dirname, "./src"), }, }, }); EOF # Install all shadcn/ui dependencies echo "๐Ÿ“ฆ Installing shadcn/ui dependencies..." pnpm 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 pnpm install sonner cmdk vaul embla-carousel-react react-day-picker react-resizable-panels date-fns react-hook-form @hookform/resolvers zod # Extract shadcn components from tarball echo "๐Ÿ“ฆ Extracting shadcn/ui components..." tar -xzf "$COMPONENTS_TARBALL" -C src/ # Create components.json for reference echo "๐Ÿ“ Creating components.json config..." cat > components.json << 'EOF' { "$schema": "https://ui.shadcn.com/schema.json", "style": "default", "rsc": false, "tsx": true, "tailwind": { "config": "tailwind.config.js", "css": "src/index.css", "baseColor": "slate", "cssVariables": true, "prefix": "" }, "aliases": { "components": "@/components", "utils": "@/lib/utils", "ui": "@/components/ui", "lib": "@/lib", "hooks": "@/hooks" } } EOF echo "โœ… Setup complete! You can now use Tailwind CSS and shadcn/ui in your project." echo "" echo "๐Ÿ“ฆ Included components (40+ total):" echo " - accordion, alert, aspect-ratio, avatar, badge, breadcrumb" echo " - button, calendar, card, carousel, checkbox, collapsible" echo " - command, context-menu, dialog, drawer, dropdown-menu" echo " - form, hover-card, input, label, menubar, navigation-menu" echo " - popover, progress, radio-group, resizable, scroll-area" echo " - select, separator, sheet, skeleton, slider, sonner" echo " - switch, table, tabs, textarea, toast, toggle, toggle-group, tooltip" echo "" echo "To start developing:" echo " cd $PROJECT_NAME" echo " pnpm dev" echo "" echo "๐Ÿ“š Import components like:" echo " import { Button } from '@/components/ui/button'" echo " import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'" echo " import { Dialog, DialogContent, DialogTrigger } from '@/components/ui/dialog'"