init-artifact.sh 9.7 KB

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