Skip to content

Commit 2e41f54

Browse files
committed
chore(build): add Vercel preview builds for framework test apps
1 parent 308aef5 commit 2e41f54

File tree

4 files changed

+344
-1
lines changed

4 files changed

+344
-1
lines changed

core/scripts/vercel-build.sh

Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
#!/bin/bash
2+
#
3+
# Vercel preview build script
4+
#
5+
# Builds core component tests (same as before) plus framework test apps
6+
# (Angular, React, Vue) so they're all accessible from a single preview URL.
7+
#
8+
# Core tests: /src/components/{name}/test/{scenario}
9+
# Angular test app: /angular/
10+
# React test app: /react/
11+
# Vue test app: /vue/
12+
# React Router app: /react-router/ (only Vite-based apps, i.e. RR6+)
13+
#
14+
set -e
15+
16+
# Vercel places core/ at /vercel/path1 (bind mount). The full repo clone
17+
# lives at /vercel/path0. We can't rely on `..` to reach it, so we search.
18+
CORE_DIR=$(pwd)
19+
OUTPUT_DIR="${CORE_DIR}/../_vercel_output"
20+
21+
# Find the actual repo root (the directory containing packages/)
22+
REPO_ROOT=""
23+
for candidate in /vercel/path0 /vercel/path1 "${CORE_DIR}/.."; do
24+
if [ -d "${candidate}/packages" ]; then
25+
REPO_ROOT="${candidate}"
26+
break
27+
fi
28+
done
29+
30+
echo "=== Ionic Framework Preview Build ==="
31+
echo "Core dir: ${CORE_DIR}"
32+
echo "Repo root: ${REPO_ROOT:-NOT FOUND}"
33+
34+
rm -rf "${OUTPUT_DIR}"
35+
mkdir -p "${OUTPUT_DIR}"
36+
37+
# ---------------------------------------------------------------------------
38+
# Step 1 - Build Core (dependencies already installed by Vercel installCommand)
39+
# ---------------------------------------------------------------------------
40+
echo ""
41+
echo "--- Step 1: Building Core ---"
42+
npm run build
43+
44+
# Copy core files to output. The test HTML files use relative paths like
45+
# ../../../../../dist/ionic/ionic.esm.js so the directory structure must
46+
# be preserved exactly.
47+
echo "Copying core output..."
48+
cp -r "${CORE_DIR}/src" "${OUTPUT_DIR}/src"
49+
cp -r "${CORE_DIR}/dist" "${OUTPUT_DIR}/dist"
50+
cp -r "${CORE_DIR}/css" "${OUTPUT_DIR}/css"
51+
mkdir -p "${OUTPUT_DIR}/scripts"
52+
cp -r "${CORE_DIR}/scripts/testing" "${OUTPUT_DIR}/scripts/testing"
53+
54+
# Vercel mounts core/ at a separate path (path1) from the repo clone (path0).
55+
# Framework packages reference core via relative paths (../../core/css etc.),
56+
# which resolve to path0/core/ -- not path1/ where we just built.
57+
# Symlink path0/core -> path1 so those references find the build outputs.
58+
if [ -n "${REPO_ROOT}" ] && [ "${CORE_DIR}" != "${REPO_ROOT}/core" ] && [ -d "${REPO_ROOT}/core" ]; then
59+
echo "Linking ${REPO_ROOT}/core -> ${CORE_DIR} (so framework builds find core outputs)"
60+
rm -rf "${REPO_ROOT}/core"
61+
ln -s "${CORE_DIR}" "${REPO_ROOT}/core"
62+
fi
63+
64+
# ---------------------------------------------------------------------------
65+
# Check if the full repo is available
66+
# ---------------------------------------------------------------------------
67+
if [ -z "${REPO_ROOT}" ]; then
68+
echo ""
69+
echo "WARNING: Could not find repo root (no directory with packages/ found)"
70+
echo "Only core tests will be deployed (framework test apps require the full repo)."
71+
72+
# Generate landing page and exit -- core tests are still useful
73+
cat > "${OUTPUT_DIR}/index.html" << 'LANDING_EOF'
74+
<!DOCTYPE html>
75+
<html lang="en"><head><meta charset="UTF-8"><title>Ionic Framework - Preview</title></head>
76+
<body><h1>Ionic Framework Preview</h1><p>Core tests only. Browse to /src/components/{name}/test/{scenario}/</p></body>
77+
</html>
78+
LANDING_EOF
79+
80+
echo "=== Preview build complete (core only) ==="
81+
exit 0
82+
fi
83+
84+
# ---------------------------------------------------------------------------
85+
# Step 2 - Build Framework Packages (parallel)
86+
# ---------------------------------------------------------------------------
87+
echo ""
88+
echo "--- Step 2: Building Framework Packages ---"
89+
90+
build_angular_pkgs() {
91+
cd "${REPO_ROOT}/packages/angular" && npm install && npm run build
92+
cd "${REPO_ROOT}/packages/angular-server" && npm install && npm run build
93+
}
94+
95+
build_react_pkgs() {
96+
cd "${REPO_ROOT}/packages/react" && npm install && npm run build
97+
cd "${REPO_ROOT}/packages/react-router" && npm install && npm run build
98+
}
99+
100+
build_vue_pkgs() {
101+
cd "${REPO_ROOT}/packages/vue" && npm install && npm run build
102+
cd "${REPO_ROOT}/packages/vue-router" && npm install && npm run build
103+
}
104+
105+
build_angular_pkgs > /tmp/vercel-angular-pkg.log 2>&1 &
106+
PID_ANG=$!
107+
build_react_pkgs > /tmp/vercel-react-pkg.log 2>&1 &
108+
PID_REACT=$!
109+
build_vue_pkgs > /tmp/vercel-vue-pkg.log 2>&1 &
110+
PID_VUE=$!
111+
112+
FAILED=""
113+
wait $PID_ANG || { echo "Angular packages failed:"; tail -30 /tmp/vercel-angular-pkg.log; FAILED="${FAILED} angular-pkg"; }
114+
wait $PID_REACT || { echo "React packages failed:"; tail -30 /tmp/vercel-react-pkg.log; FAILED="${FAILED} react-pkg"; }
115+
wait $PID_VUE || { echo "Vue packages failed:"; tail -30 /tmp/vercel-vue-pkg.log; FAILED="${FAILED} vue-pkg"; }
116+
117+
if [ -n "${FAILED}" ]; then
118+
echo "ERROR: Framework package builds failed:${FAILED}"
119+
echo "Core tests will still be deployed."
120+
else
121+
echo "All framework packages built."
122+
fi
123+
124+
# ---------------------------------------------------------------------------
125+
# Step 3 - Build Framework Test Apps (parallel)
126+
# ---------------------------------------------------------------------------
127+
echo ""
128+
echo "--- Step 3: Building Framework Test Apps ---"
129+
130+
# Find the best available app version for a given package
131+
pick_app() {
132+
local test_dir="$1"
133+
shift
134+
for v in "$@"; do
135+
if [ -d "${test_dir}/apps/${v}" ]; then
136+
echo "${v}"
137+
return 0
138+
fi
139+
done
140+
return 1
141+
}
142+
143+
build_angular_test() {
144+
local APP
145+
APP=$(pick_app "${REPO_ROOT}/packages/angular/test" ng20 ng19 ng18) || {
146+
echo "[angular] No test app found, skipping."
147+
return 0
148+
}
149+
echo "[angular] Building ${APP}..."
150+
151+
cd "${REPO_ROOT}/packages/angular/test"
152+
./build.sh "${APP}"
153+
cd "build/${APP}"
154+
npm install
155+
npm run sync
156+
# --base-href sets <base href="/angular/"> so Angular Router works under the sub-path
157+
npm run build -- --base-href /angular/
158+
159+
mkdir -p "${OUTPUT_DIR}/angular"
160+
cp -r dist/test-app/browser/* "${OUTPUT_DIR}/angular/"
161+
echo "[angular] Done."
162+
}
163+
164+
build_react_test() {
165+
local APP
166+
APP=$(pick_app "${REPO_ROOT}/packages/react/test" react19 react18) || {
167+
echo "[react] No test app found, skipping."
168+
return 0
169+
}
170+
echo "[react] Building ${APP}..."
171+
172+
cd "${REPO_ROOT}/packages/react/test"
173+
./build.sh "${APP}"
174+
cd "build/${APP}"
175+
npm install
176+
npm run sync
177+
# --base sets Vite's base URL; import.meta.env.BASE_URL is read by IonReactRouter basename
178+
npx vite build --base /react/
179+
180+
mkdir -p "${OUTPUT_DIR}/react"
181+
cp -r dist/* "${OUTPUT_DIR}/react/"
182+
echo "[react] Done."
183+
}
184+
185+
build_vue_test() {
186+
echo "[vue] Building vue3..."
187+
188+
cd "${REPO_ROOT}/packages/vue/test"
189+
./build.sh vue3
190+
cd build/vue3
191+
npm install
192+
npm run sync
193+
# Vue Router already reads import.meta.env.BASE_URL which Vite sets from --base
194+
npx vite build --base /vue/
195+
196+
mkdir -p "${OUTPUT_DIR}/vue"
197+
cp -r dist/* "${OUTPUT_DIR}/vue/"
198+
echo "[vue] Done."
199+
}
200+
201+
build_react_router_test() {
202+
# Only build Vite-based (RR6+) apps. CRA-based reactrouter5 can't handle
203+
# sub-path routing without code changes, and it's being replaced by RR6.
204+
local APP
205+
APP=$(pick_app "${REPO_ROOT}/packages/react-router/test" reactrouter6-react19 reactrouter6-react18 reactrouter6) || {
206+
echo "[react-router] No Vite-based test app found, skipping."
207+
return 0
208+
}
209+
echo "[react-router] Building ${APP}..."
210+
211+
cd "${REPO_ROOT}/packages/react-router/test"
212+
./build.sh "${APP}"
213+
cd "build/${APP}"
214+
npm install --legacy-peer-deps
215+
npm run sync
216+
npx vite build --base /react-router/
217+
218+
mkdir -p "${OUTPUT_DIR}/react-router"
219+
cp -r dist/* "${OUTPUT_DIR}/react-router/"
220+
echo "[react-router] Done."
221+
}
222+
223+
build_angular_test > /tmp/vercel-angular-test.log 2>&1 &
224+
PID_ANG_TEST=$!
225+
build_react_test > /tmp/vercel-react-test.log 2>&1 &
226+
PID_REACT_TEST=$!
227+
build_vue_test > /tmp/vercel-vue-test.log 2>&1 &
228+
PID_VUE_TEST=$!
229+
build_react_router_test > /tmp/vercel-rr-test.log 2>&1 &
230+
PID_RR_TEST=$!
231+
232+
TEST_FAILED=""
233+
wait $PID_ANG_TEST || { echo "Angular test app failed:"; tail -30 /tmp/vercel-angular-test.log; TEST_FAILED="${TEST_FAILED} angular"; }
234+
wait $PID_REACT_TEST || { echo "React test app failed:"; tail -30 /tmp/vercel-react-test.log; TEST_FAILED="${TEST_FAILED} react"; }
235+
wait $PID_VUE_TEST || { echo "Vue test app failed:"; tail -30 /tmp/vercel-vue-test.log; TEST_FAILED="${TEST_FAILED} vue"; }
236+
wait $PID_RR_TEST || { echo "React Router test app failed:"; tail -30 /tmp/vercel-rr-test.log; TEST_FAILED="${TEST_FAILED} react-router"; }
237+
238+
if [ -n "${TEST_FAILED}" ]; then
239+
echo ""
240+
echo "WARNING: Some test app builds failed:${TEST_FAILED}"
241+
echo "Core tests and successful framework apps will still be deployed."
242+
fi
243+
244+
# ---------------------------------------------------------------------------
245+
# Step 4 - Landing Page
246+
# ---------------------------------------------------------------------------
247+
echo ""
248+
echo "--- Step 4: Generating landing page ---"
249+
250+
cat > "${OUTPUT_DIR}/index.html" << 'LANDING_EOF'
251+
<!DOCTYPE html>
252+
<html lang="en">
253+
<head>
254+
<meta charset="UTF-8">
255+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
256+
<title>Ionic Framework - Preview</title>
257+
<style>
258+
* { margin: 0; padding: 0; box-sizing: border-box; }
259+
body {
260+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
261+
background: #f5f5f5; padding: 2rem; color: #333;
262+
}
263+
.container { max-width: 680px; margin: 0 auto; }
264+
h1 { font-size: 1.4rem; margin-bottom: 0.25rem; }
265+
.subtitle { color: #666; margin-bottom: 1.5rem; font-size: 0.875rem; }
266+
.cards { display: grid; gap: 0.75rem; }
267+
a.card {
268+
background: #fff; border-radius: 8px; padding: 1rem 1.25rem;
269+
text-decoration: none; color: inherit;
270+
border: 1px solid #e0e0e0; transition: border-color 0.15s;
271+
}
272+
a.card:hover { border-color: #4f8ef7; }
273+
.card h2 { font-size: 1rem; margin-bottom: 0.2rem; display: flex; align-items: center; gap: 0.5rem; }
274+
.card p { color: #666; font-size: 0.8rem; }
275+
.badge {
276+
display: inline-block; font-size: 0.65rem; padding: 1px 6px;
277+
border-radius: 3px; font-weight: 600;
278+
}
279+
.badge-green { background: #e8f5e9; color: #2e7d32; }
280+
.badge-blue { background: #e3f2fd; color: #1565c0; }
281+
.badge-red { background: #fce4ec; color: #c62828; }
282+
.badge-purple{ background: #f3e5f5; color: #6a1b9a; }
283+
hr { border: none; border-top: 1px solid #e0e0e0; margin: 1rem 0; }
284+
.tip { font-size: 0.75rem; color: #999; margin-top: 1rem; }
285+
</style>
286+
</head>
287+
<body>
288+
<div class="container">
289+
<h1>Ionic Framework Preview</h1>
290+
<p class="subtitle">Component test apps for manual validation</p>
291+
<div class="cards">
292+
<a class="card" href="/src/components/">
293+
<h2>Core Components <span class="badge badge-blue">Stencil</span></h2>
294+
<p>Browse to /src/components/{name}/test/{scenario}/</p>
295+
</a>
296+
<hr>
297+
<a class="card" href="/angular/">
298+
<h2>Angular <span class="badge badge-red">ng20</span></h2>
299+
<p>@ionic/angular standalone + lazy-loaded component tests</p>
300+
</a>
301+
<a class="card" href="/react/">
302+
<h2>React <span class="badge badge-blue">react19</span></h2>
303+
<p>@ionic/react overlays, hooks, tabs, form controls</p>
304+
</a>
305+
<a class="card" href="/vue/">
306+
<h2>Vue <span class="badge badge-green">vue3</span></h2>
307+
<p>@ionic/vue overlays, router, tabs, lifecycle</p>
308+
</a>
309+
<a class="card" href="/react-router/">
310+
<h2>React Router <span class="badge badge-purple">rr6</span></h2>
311+
<p>@ionic/react-router navigation, tabs, transitions</p>
312+
</a>
313+
</div>
314+
<p class="tip">Links to framework apps that were not built will 404.</p>
315+
</div>
316+
</body>
317+
</html>
318+
LANDING_EOF
319+
320+
echo ""
321+
echo "=== Preview build complete ==="
322+
ls -la "${OUTPUT_DIR}"

core/vercel.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"$schema": "https://openapi.vercel.sh/vercel.json",
3+
"framework": null,
4+
"installCommand": "npm install",
5+
"buildCommand": "bash scripts/vercel-build.sh",
6+
"outputDirectory": "../_vercel_output",
7+
"rewrites": [
8+
{ "source": "/angular/:path*", "destination": "/angular/index.html" },
9+
{ "source": "/react/:path*", "destination": "/react/index.html" },
10+
{ "source": "/vue/:path*", "destination": "/vue/index.html" },
11+
{ "source": "/react-router/:path*", "destination": "/react-router/index.html" }
12+
]
13+
}

packages/react/test/base/src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ setupIonicReact();
4444

4545
const App: React.FC = () => (
4646
<IonApp>
47-
<IonReactRouter>
47+
<IonReactRouter basename={import.meta.env?.BASE_URL?.replace(/\/$/, '')}>
4848
<IonRouterOutlet>
4949
<Route exact path="/" component={Main} />
5050
<Route path="/overlay-hooks" component={OverlayHooks} />
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,9 @@
11
/// <reference types="react-scripts" />
2+
3+
interface ImportMetaEnv {
4+
readonly BASE_URL?: string;
5+
}
6+
7+
interface ImportMeta {
8+
readonly env?: ImportMetaEnv;
9+
}

0 commit comments

Comments
 (0)