1 #!/bin/bash
2 # Discover the shape of a fullstack_js project before migration.
3 # Usage: bash fullstack_detect.sh
4 #
5 # Outputs key=value pairs describing the project structure.
6 # The agent reads this output to decide what to pass to the copy scripts.
7
8 set -euo pipefail
9
10 BACKUP=".migration-backup"
11
12 if [ ! -d "$BACKUP" ]; then
13 echo "ERROR: $BACKUP directory not found"
14 exit 1
15 fi
16
17 echo "==== PROJECT DETECTION ===="
18
19 # --- Tailwind version ---
20 # v3 uses @tailwind directives; v4 uses @import "tailwindcss"
21 TW_VERSION="v4"
22 for css in "$BACKUP"/client/src/index.css "$BACKUP"/src/index.css "$BACKUP"/index.css; do
23 if [ -f "$css" ] && grep -q '@tailwind base' "$css" 2>/dev/null; then
24 TW_VERSION="v3"
25 break
26 fi
27 done
28 echo "TAILWIND_VERSION=$TW_VERSION"
29
30 # --- Theme system ---
31 HAS_THEME_JSON="false"
32 if [ -f "$BACKUP/theme.json" ]; then
33 HAS_THEME_JSON="true"
34 fi
35 echo "HAS_THEME_JSON=$HAS_THEME_JSON"
36
37 # --- Client directory ---
38 # Most fullstack_js projects use client/, but some use src/ or root
39 CLIENT_DIR=""
40 if [ -d "$BACKUP/client/src" ]; then
41 CLIENT_DIR="client"
42 elif [ -d "$BACKUP/src/components" ] || [ -d "$BACKUP/src/pages" ] || [ -f "$BACKUP/src/App.tsx" ] || [ -f "$BACKUP/src/main.tsx" ]; then
43 CLIENT_DIR="src"
44 fi
45 echo "CLIENT_DIR=$CLIENT_DIR"
46
47 # --- Route layout ---
48 ROUTE_LAYOUT="none"
49 if [ -d "$BACKUP/server/routes" ]; then
50 ROUTE_LAYOUT="directory"
51 elif [ -f "$BACKUP/server/routes.ts" ]; then
52 ROUTE_LAYOUT="single"
53 fi
54 echo "ROUTE_LAYOUT=$ROUTE_LAYOUT"
55
56 # --- Schema discovery ---
57 # Try common locations in order of likelihood
58 SCHEMA_PATH=""
59 for candidate in \
60 "$BACKUP/shared/schema.ts" \
61 "$BACKUP/shared/schema/index.ts" \
62 "$BACKUP/db/schema.ts" \
63 "$BACKUP/server/db/schema.ts" \
64 "$BACKUP/src/shared/schema.ts"; do
65 if [ -f "$candidate" ]; then
66 SCHEMA_PATH="${candidate#"$BACKUP/"}"
67 break
68 fi
69 done
70 if [ -z "$SCHEMA_PATH" ]; then
71 # Broader search: any file named schema.ts outside node_modules
72 FOUND=$(find "$BACKUP" -name "schema.ts" -not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null | head -1)
73 if [ -n "$FOUND" ]; then
74 SCHEMA_PATH="${FOUND#"$BACKUP/"}"
75 fi
76 fi
77 echo "SCHEMA_PATH=$SCHEMA_PATH"
78
79 # --- Storage file ---
80 HAS_STORAGE="false"
81 if [ -f "$BACKUP/server/storage.ts" ]; then
82 HAS_STORAGE="true"
83 fi
84 echo "HAS_STORAGE=$HAS_STORAGE"
85
86 # --- Attached assets ---
87 HAS_ATTACHED_ASSETS="false"
88 if [ -d "$BACKUP/attached_assets" ]; then
89 HAS_ATTACHED_ASSETS="true"
90 fi
91 echo "HAS_ATTACHED_ASSETS=$HAS_ATTACHED_ASSETS"
92
93 # --- Custom queryClient ---
94 HAS_CUSTOM_QUERYCLIENT="false"
95 for candidate in \
96 "$BACKUP/client/src/lib/queryClient.ts" \
97 "$BACKUP/client/src/lib/queryClient.tsx" \
98 "$BACKUP/client/src/queryClient.ts" \
99 "$BACKUP/src/lib/queryClient.ts" \
100 "$BACKUP/src/queryClient.ts"; do
101 if [ -f "$candidate" ]; then
102 HAS_CUSTOM_QUERYCLIENT="true"
103 break
104 fi
105 done
106 echo "HAS_CUSTOM_QUERYCLIENT=$HAS_CUSTOM_QUERYCLIENT"
107
108 # --- Server files ---
109 echo ""
110 echo "SERVER_FILES:"
111 if [ -d "$BACKUP/server" ]; then
112 find "$BACKUP/server" -maxdepth 2 -name "*.ts" -not -path "*/node_modules/*" 2>/dev/null | sort | while read -r f; do
113 echo " ${f#"$BACKUP/"}"
114 done
115 fi
116
117 # --- Client directories ---
118 echo ""
119 echo "CLIENT_DIRS:"
120 if [ -n "$CLIENT_DIR" ] && [ -d "$BACKUP/$CLIENT_DIR/src" ]; then
121 for entry in "$BACKUP/$CLIENT_DIR/src/"*; do
122 [ -e "$entry" ] || continue
123 echo " $CLIENT_DIR/src/$(basename "$entry")"
124 done
125 fi
126
127 # --- Tailwind config location ---
128 TW_CONFIG=""
129 for candidate in \
130 "$BACKUP/tailwind.config.ts" \
131 "$BACKUP/tailwind.config.js" \
132 "$BACKUP/tailwind.config.cjs" \
133 "$BACKUP/client/tailwind.config.ts" \
134 "$BACKUP/client/tailwind.config.js"; do
135 if [ -f "$candidate" ]; then
136 TW_CONFIG="${candidate#"$BACKUP/"}"
137 break
138 fi
139 done
140 echo ""
141 echo "TAILWIND_CONFIG=$TW_CONFIG"
142
143 # --- Vite plugins in use ---
144 # Known scaffold plugins that don't need manual porting
145 SCAFFOLD_PLUGINS="@vitejs/plugin-react @tailwindcss/vite @replit/vite-plugin-runtime-error-modal @replit/vite-plugin-cartographer @replit/vite-plugin-dev-banner"
146
147 echo ""
148 echo "VITE_PLUGINS:"
149 VITE_CONFIG=""
150 for candidate in "$BACKUP/vite.config.ts" "$BACKUP/client/vite.config.ts" "$BACKUP/vite.config.js" "$BACKUP/client/vite.config.js"; do
151 if [ -f "$candidate" ]; then
152 VITE_CONFIG="$candidate"
153 break
154 fi
155 done
156 ALL_PLUGINS=""
157 if [ -n "$VITE_CONFIG" ]; then
158 ALL_PLUGINS=$(sed -n 's/.*from ["\x27]\([^"\x27]*\)["\x27].*/\1/p' "$VITE_CONFIG" 2>/dev/null | grep -iv "^vite$\|^path$\|^express\|^http\|^url" || true)
159 echo "$ALL_PLUGINS" | while read -r p; do
160 [ -z "$p" ] && continue
161 echo " $p"
162 done
163 fi
164
165 # --- Custom Vite plugins (not in scaffold) ---
166 echo ""
167 echo "CUSTOM_VITE_PLUGINS:"
168 if [ -n "$ALL_PLUGINS" ]; then
169 echo "$ALL_PLUGINS" | while read -r p; do
170 [ -z "$p" ] && continue
171 IS_SCAFFOLD="false"
172 for sp in $SCAFFOLD_PLUGINS; do
173 if [ "$p" = "$sp" ]; then
174 IS_SCAFFOLD="true"
175 break
176 fi
177 done
178 if [ "$IS_SCAFFOLD" = "false" ]; then
179 echo " $p"
180 fi
181 done
182 fi
183 # Also detect local/relative plugin imports
184 if [ -n "$VITE_CONFIG" ]; then
185 LOCAL_PLUGINS=$(grep -oE 'from ["\x27]\./[^"\x27]+["\x27]' "$VITE_CONFIG" 2>/dev/null | sed "s/from [\"']//;s/[\"']//" || true)
186 if [ -n "$LOCAL_PLUGINS" ]; then
187 echo "$LOCAL_PLUGINS" | while read -r p; do
188 [ -z "$p" ] && continue
189 echo " $p (local file)"
190 done
191 fi
192 fi
193
194 # --- Potential conflicts ---
195 echo ""
196 echo "CONFLICTS:"
197 HAS_CONFLICTS="false"
198
199 # PostCSS config + Tailwind v4 = build failure
200 if [ "$TW_VERSION" = "v4" ]; then
201 for candidate in "$BACKUP/postcss.config.js" "$BACKUP/postcss.config.cjs" "$BACKUP/postcss.config.mjs"; do
202 if [ -f "$candidate" ]; then
203 echo " WARNING: $(basename "$candidate") exists but Tailwind v4 detected — will conflict with @tailwindcss/vite"
204 HAS_CONFLICTS="true"
205 break
206 fi
207 done
208 fi
209
210 # Old vite plugins that won't exist in the scaffold
211 if [ -n "$VITE_CONFIG" ]; then
212 if grep -q "vite-plugin-shadcn-theme-json" "$VITE_CONFIG" 2>/dev/null && [ "$HAS_THEME_JSON" = "false" ]; then
213 echo " WARNING: vite.config uses shadcn-theme-json plugin but no theme.json found"
214 HAS_CONFLICTS="true"
215 fi
216 fi
217
218 # Custom Vite plugins that will be silently dropped
219 # Note: avoid piped while loops — they run in subshells and can't update HAS_CONFLICTS
220 if [ -n "$ALL_PLUGINS" ]; then
221 while read -r p; do
222 [ -z "$p" ] && continue
223 IS_SCAFFOLD="false"
224 for sp in $SCAFFOLD_PLUGINS; do
225 if [ "$p" = "$sp" ]; then IS_SCAFFOLD="true"; break; fi
226 done
227 if [ "$IS_SCAFFOLD" = "false" ]; then
228 echo " WARNING: custom Vite plugin '$p' will be DROPPED unless manually ported"
229 HAS_CONFLICTS="true"
230 fi
231 done <<< "$ALL_PLUGINS"
232 fi
233 if [ -n "${LOCAL_PLUGINS:-}" ]; then
234 while read -r p; do
235 [ -z "$p" ] && continue
236 echo " WARNING: local Vite plugin '$p' will be DROPPED unless manually ported"
237 HAS_CONFLICTS="true"
238 done <<< "$LOCAL_PLUGINS"
239 fi
240
241 [ "$HAS_CONFLICTS" = "false" ] && echo " none"
242
243 echo ""
244 echo "==== END DETECTION ===="