Conversation
…tination-html feat(app): adopt yellow/black branding, map destination selection, and HTML-structured screens
Introduce a GitHub repository link in the main landing screen header to provide quick access to the project repo. This change doesn't alter navigation or app behavior. - Displays a "GitHub Repository" link under the YellowCab header - Uses React Native Linking to open https://github.com/amoschanda/uber - Includes accessibility labeling for screen readers
feat(welcome): display GitHub repository link on onboarding screen
…tory - Create apk-repo with builds/docs and CI/CD workflow - Add app.json and eas.json configurations for Android - Add build-apk.sh to automate EAS builds and artifact handling - Add README, build-metadata.json, and checksum scaffolding - Prepare for artifact distribution and future releases
…al-android-apk-from-the-uber-taxi-app feat(apk): initialize Expo EAS Android APK build workflow and repository
📝 WalkthroughWalkthroughThis PR rebrands the Expo Uber app to YellowCab with a yellow/gold color scheme, adds web deployment infrastructure via GitHub Actions and Vercel, implements map-based destination selection with reverse geocoding, adds comprehensive deployment documentation, and includes build automation scripts for Android APK generation. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 16
🤖 Fix all issues with AI agents
In @.github/workflows/deploy-web.yml:
- Around line 11-18: Update the GitHub Actions steps titled "Checkout" and
"Setup Node.js" to use the v4 releases: change uses: actions/checkout@v3 to
actions/checkout@v4 and uses: actions/setup-node@v3 to actions/setup-node@v4;
also update the node-version value from "18" to "20" (or add a matrix if you
need multiple versions) to take advantage of Node.js 20 support and v4
improvements in actions/setup-node.
- Around line 28-34: Update the Deploy to Vercel step that currently uses
"amondnet/vercel-action@v25" (the step named "Deploy to Vercel") to reference
the latest stable release tag (e.g., v41.1.4) and optionally replace the mutable
tag with a specific commit SHA for pinning; modify the "uses:
amondnet/vercel-action@v25" value to the new tag or SHA and keep existing inputs
(vercel-token, vercel-org-id, vercel-project-id, vercel-args) unchanged.
In `@app.json`:
- Around line 26-30: Remove the unused background location permission by
deleting "ACCESS_BACKGROUND_LOCATION" from the permissions array in app.json;
the app only uses foreground APIs (Location.requestForegroundPermissionsAsync
and Location.getCurrentPositionAsync in home.tsx) so keep only foreground
location permissions ("ACCESS_FINE_LOCATION" and "ACCESS_COARSE_LOCATION") to
avoid stricter app store review and privacy disclosures.
In `@app/`(auth)/welcome.tsx:
- Line 3: Wrap calls to Linking.openURL in an async handler that first calls
Linking.canOpenURL(url) and only calls Linking.openURL(url) if canOpenURL
returns true; surround both calls with try/catch to handle runtime errors and
log or show a user-friendly error (e.g., via Alert or processLogger). Update the
touchable element (TouchableOpacity or the Text that acts as a link) to include
accessibilityRole="link" and an appropriate accessibilityLabel to improve
screen-reader semantics. Locate usages of Linking.openURL and the
TouchableOpacity in welcome.tsx and update those handlers and props accordingly.
In `@build-apk.sh`:
- Around line 154-165: The buildStatus block is checking hard-coded "debug" and
"release" APK filenames which don't match the actual build profiles
(development|preview|production); update the logic that sets buildStatus to
inspect the correct profile folders (using the buildProfile variable or a list
of supported profiles) under BUILD_DIR and detect any .apk in each profile
folder, then populate keys matching the actual profile names (e.g., development,
preview, production) with "completed" or "not-built"; modify the buildStatus
structure and the file-existence checks that reference BUILD_DIR so they
dynamically derive paths from buildProfile or iterate the supported profiles
rather than using the debug/release filenames.
- Around line 115-136: The update_checksums function currently only scans
${BUILD_DIR} for *.apk so it misses APKs placed in per-profile subdirectories
(${BUILD_DIR}/${profile}); modify update_checksums to recursively find APKs
under ${BUILD_DIR} (or iterate over profile subdirs) and append their checksums
to checksums.md5 and checksums.sha256; update the logic in update_checksums
(referencing update_checksums, BUILD_DIR and profile) to use find or a loop over
"${BUILD_DIR}"/* to locate all *.apk files, handle paths with spaces, and write
results into the existing checksums files before returning to REPO_DIR.
In `@components/Map.tsx`:
- Around line 2-4: The handleMapPress handler can be invoked by rapid taps or
marker presses and its async reverse-geocode results can arrive out of order;
add a useRef-based requestId counter (e.g., mapPressRequestIdRef) incremented on
each handleMapPress call and captured inside the async task to ignore stale
responses, and early-return when the event is a marker press (inspect
MapPressEvent.nativeEvent?.action or a pressed flag) to avoid handling
marker-initiated presses; update the reverse-geocode success/error paths to
check the current requestId before setting state or showing Alert so only the
latest request can modify UI.
In `@DEPLOYMENT.md`:
- Around line 59-62: Update the command in DEPLOYMENT.md to match package.json
by replacing the incorrect script name `npm run web:export` with the actual
script `npm run build:web`; edit the bash code block so it reads `npm install`
followed by `npm run build:web` (ensure the code fence and spacing remain
valid).
- Around line 25-27: Update the outdated Expo command `expo export:web` to the
modern syntax used by our build workflow—replace it with `npx expo export -p
web` (or `expo export -p web`) so docs match the `npm run build:web` behavior
which runs `expo export -p web --no-minify`; ensure the documentation command
and any examples reflect `-p web` platform syntax.
- Around line 66-70: Replace the deprecated command string "expo export:web"
with the modern invocation "npx expo export -p web" and update the subsequent
references to the output folder: change "dist/web" to "dist" in steps that
describe the created folder and the upload instruction so the doc reflects the
new output location.
In `@eas.json`:
- Around line 19-22: Update the "production" profile's Android configuration by
changing the "buildType" value from "apk" to "app-bundle" so production releases
produce an Android App Bundle for Play Store submission; locate the "production"
-> "android" -> "buildType" entry in eas.json and replace "apk" with
"app-bundle".
In `@lib/geocoding.ts`:
- Around line 8-17: Before calling response.json() in the geocoding fetch logic,
check the HTTP status using response.ok and throw a descriptive error including
response.status and response.statusText when the request failed; update the
fetch block that constructs the URL with googlePlacesApiKey (the response
variable and the error throw "No address found for the selected location") to
first do if (!response.ok) throw new Error(`Geocoding API error
${response.status}: ${response.statusText}`) so you only call response.json()
for successful responses and preserve the existing "No address found..." error
path when data.status !== "OK" or results are empty.
In `@metro.config.js`:
- Around line 11-17: The current unconditional aliasing in
config.resolver.extraNodeModules (mapping "react-native" => "react-native-web"
and "react-native-maps" => "./web-polyfills/react-native-maps-web.js") breaks
native builds; update the logic to only set these aliases when
EXPO_USE_WEB_PLATFORM is enabled (mirror the existing conditional used for
sourceExts). Specifically, wrap or gate the assignment to
config.resolver.extraNodeModules behind a check of
process.env.EXPO_USE_WEB_PLATFORM (or the same variable/name used by
sourceExts), leaving resolver behavior untouched for native builds when the flag
is not set.
In `@vercel.json`:
- Around line 1-15: Update the vercel.json settings to follow Expo web
deployment guidance: change the buildCommand value from "npm run build:web" to
"expo export -p web", set devCommand to "expo" instead of "expo start --web",
set framework to null (not "other"), replace the legacy "routes" array with a
modern "rewrites" entry using { "source": "/:path*", "destination": "/" }, and
add cleanUrls: true; adjust the outputDirectory key only if needed but leave
"dist" as-is if correct.
In `@WEB_DEPLOYMENT_GUIDE.md`:
- Around line 84-89: Replace the typo "Turing device sensors, camera, etc." in
the native modules list with a clearer phrase such as "Other/Additional device
sensors, camera, etc."; update the list item that currently reads `Turing device
sensors, camera, etc.` so it reads `Other/Additional device sensors, camera,
etc.` (or similar wording) to fix the wording in the native modules section.
In `@web-polyfills/index.js`:
- Around line 4-27: Replace usage of the legacy global alias and the truthy env
check: change the conditional to check typeof window !== "undefined" &&
process.env.EXPO_USE_WEB_PLATFORM === "true", and assign mocks to globalThis
(e.g. globalThis.expo_location, globalThis.stripe_payment_sheet,
globalThis.expo_auth) instead of global.*; keep the same mock shapes for
getCurrentPositionAsync, requestForegroundPermissionsAsync, initPaymentSheet,
presentPaymentSheet, authenticateAsync and isEnrolledAsync.
🧹 Nitpick comments (2)
lib/geocoding.ts (1)
1-1: Add API key validation.If
EXPO_PUBLIC_PLACES_API_KEYis not set, the API call will fail with a cryptic error. Consider adding a guard or at least logging a warning.♻️ Suggested improvement
const googlePlacesApiKey = process.env.EXPO_PUBLIC_PLACES_API_KEY; + +if (!googlePlacesApiKey) { + console.warn("EXPO_PUBLIC_PLACES_API_KEY is not set. Reverse geocoding will not work."); +}app.json (1)
31-34: Avoid hardcoding the Google Maps API key in config.Consider moving the key to an env-backed
app.config.tsto prevent accidental commits and simplify CI injection.
| - name: Checkout | ||
| uses: actions/checkout@v3 | ||
|
|
||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v3 | ||
| with: | ||
| node-version: "18" | ||
| cache: "npm" |
There was a problem hiding this comment.
Update GitHub Actions to latest versions.
The actions/checkout@v3 and actions/setup-node@v3 are outdated. Update to v4 for better performance, security patches, and Node.js 20 support.
📝 Suggested fix
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Setup Node.js
- uses: actions/setup-node@v3
+ uses: actions/setup-node@v4
with:
node-version: "18"
cache: "npm"🧰 Tools
🪛 actionlint (1.7.10)
12-12: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
15-15: the runner of "actions/setup-node@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
🤖 Prompt for AI Agents
In @.github/workflows/deploy-web.yml around lines 11 - 18, Update the GitHub
Actions steps titled "Checkout" and "Setup Node.js" to use the v4 releases:
change uses: actions/checkout@v3 to actions/checkout@v4 and uses:
actions/setup-node@v3 to actions/setup-node@v4; also update the node-version
value from "18" to "20" (or add a matrix if you need multiple versions) to take
advantage of Node.js 20 support and v4 improvements in actions/setup-node.
| - name: Deploy to Vercel | ||
| uses: amondnet/vercel-action@v25 | ||
| with: | ||
| vercel-token: ${{ secrets.VERCEL_TOKEN }} | ||
| vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} | ||
| vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} | ||
| vercel-args: "--prod" |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
latest version amondnet/vercel-action GitHub Action 2025
💡 Result:
The latest amondnet/vercel-action release in 2025 (and the newest shown on the repo releases page) is v41.1.4 (released July 15, 2025). [1][2]
Use it like:
- uses: amondnet/vercel-action@v41.1.4Update the Vercel action to the latest stable version and consider pinning to SHA for security.
The action is using v25, but the latest stable version is v41.1.4 (released July 15, 2025). Update to the latest version to benefit from security patches and improvements. Additionally, consider pinning to a specific commit SHA instead of a mutable tag for better reproducibility and security.
- uses: amondnet/vercel-action@v41.1.4🤖 Prompt for AI Agents
In @.github/workflows/deploy-web.yml around lines 28 - 34, Update the Deploy to
Vercel step that currently uses "amondnet/vercel-action@v25" (the step named
"Deploy to Vercel") to reference the latest stable release tag (e.g., v41.1.4)
and optionally replace the mutable tag with a specific commit SHA for pinning;
modify the "uses: amondnet/vercel-action@v25" value to the new tag or SHA and
keep existing inputs (vercel-token, vercel-org-id, vercel-project-id,
vercel-args) unchanged.
| "permissions": [ | ||
| "ACCESS_FINE_LOCATION", | ||
| "ACCESS_COARSE_LOCATION", | ||
| "ACCESS_BACKGROUND_LOCATION" | ||
| ], |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify whether background location is actually used in the codebase.
rg -n -C3 'requestBackgroundPermissionsAsync|startLocationUpdatesAsync|stopLocationUpdatesAsync|TaskManager|allowsBackgroundLocationUpdates' --type=ts --type=tsxRepository: adrianhajdin/uber
Length of output: 88
🏁 Script executed:
#!/bin/bash
# Search for background location usage with correct file type
rg -n -C3 'requestBackgroundPermissionsAsync|startLocationUpdatesAsync|stopLocationUpdatesAsync|TaskManager|allowsBackgroundLocationUpdates' --type=tsRepository: adrianhajdin/uber
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Find and examine app.json
find . -name "app.json" -type f | head -5
# Search more broadly for location-related code
rg -n 'location|Location|LOCATION' --type=js | head -20Repository: adrianhajdin/uber
Length of output: 172
🏁 Script executed:
#!/bin/bash
# Check the app.json permissions section
cat app.json | grep -A 10 '"permissions"'
# Search for expo-location usage
rg -n 'expo-location|useLocation|getCurrentPositionAsync|watchPositionAsync' --type=js
# Search for location-related imports
rg -n "from.*location|import.*location" --type=jsRepository: adrianhajdin/uber
Length of output: 427
🏁 Script executed:
#!/bin/bash
# Search for actual location usage in the codebase
rg -n 'getCurrentPositionAsync|getLastKnownPositionAsync|watchPositionAsync|location\.' --type=js -A 2
# Also check for any background tasks or location tracking
rg -n 'BackgroundLocation|background.*location|location.*background' -i --type=js
# List main source files to understand structure
find . -name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx" | grep -E '(src/|app/|screens/|components/)' | head -20Repository: adrianhajdin/uber
Length of output: 776
🏁 Script executed:
#!/bin/bash
# Check key screens for location usage
for file in "./app/(root)/(tabs)/home.tsx" "./app/(root)/find-ride.tsx" "./app/(root)/book-ride.tsx"; do
if [ -f "$file" ]; then
echo "=== $file ==="
rg -n 'location|Location|coordinates|latitude|longitude|position' "$file" | head -10
fi
doneRepository: adrianhajdin/uber
Length of output: 1216
Remove ACCESS_BACKGROUND_LOCATION permission from app.json.
The codebase only uses foreground location APIs (Location.requestForegroundPermissionsAsync() and Location.getCurrentPositionAsync() in home.tsx). The ACCESS_BACKGROUND_LOCATION permission is not used anywhere and unnecessarily triggers stricter app store review requirements and enhanced privacy disclosures.
🤖 Prompt for AI Agents
In `@app.json` around lines 26 - 30, Remove the unused background location
permission by deleting "ACCESS_BACKGROUND_LOCATION" from the permissions array
in app.json; the app only uses foreground APIs
(Location.requestForegroundPermissionsAsync and Location.getCurrentPositionAsync
in home.tsx) so keep only foreground location permissions
("ACCESS_FINE_LOCATION" and "ACCESS_COARSE_LOCATION") to avoid stricter app
store review and privacy disclosures.
| import { router } from "expo-router"; | ||
| import { useRef, useState } from "react"; | ||
| import { Image, Text, TouchableOpacity, View } from "react-native"; | ||
| import { Image, Linking, Text, TouchableOpacity, View } from "react-native"; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cd /repo && git ls-files | grep -E "(welcome|auth)" | head -20Repository: adrianhajdin/uber
Length of output: 116
🏁 Script executed:
cat -n app/\(auth\)/welcome.tsx | head -50Repository: adrianhajdin/uber
Length of output: 2143
🏁 Script executed:
cat -n app/\(auth\)/welcome.tsx | sed -n '15,35p'Repository: adrianhajdin/uber
Length of output: 925
Add error handling for external link and improve link accessibility.
Linking.openURL can fail due to network issues or unsupported schemes. Add a robustness check with canOpenURL() and handle errors gracefully. Also add accessibilityRole="link" for better semantic accessibility.
🔧 Suggested fix
-import { Image, Linking, Text, TouchableOpacity, View } from "react-native";
+import { Alert, Image, Linking, Text, TouchableOpacity, View } from "react-native";- <TouchableOpacity
- onPress={() => Linking.openURL("https://github.com/amoschanda/uber")}
+ <TouchableOpacity
+ onPress={async () => {
+ const url = "https://github.com/amoschanda/uber";
+ try {
+ const supported = await Linking.canOpenURL(url);
+ if (!supported) {
+ Alert.alert("Error", "Unable to open the repository link.");
+ return;
+ }
+ await Linking.openURL(url);
+ } catch {
+ Alert.alert("Error", "Unable to open the repository link.");
+ }
+ }}
className="mt-2"
accessible={true}
accessibilityLabel="GitHub Repository"
+ accessibilityRole="link"
>🤖 Prompt for AI Agents
In `@app/`(auth)/welcome.tsx at line 3, Wrap calls to Linking.openURL in an async
handler that first calls Linking.canOpenURL(url) and only calls
Linking.openURL(url) if canOpenURL returns true; surround both calls with
try/catch to handle runtime errors and log or show a user-friendly error (e.g.,
via Alert or processLogger). Update the touchable element (TouchableOpacity or
the Text that acts as a link) to include accessibilityRole="link" and an
appropriate accessibilityLabel to improve screen-reader semantics. Locate usages
of Linking.openURL and the TouchableOpacity in welcome.tsx and update those
handlers and props accordingly.
| # Function to update checksums | ||
| update_checksums() { | ||
| echo "" | ||
| echo -e "${YELLOW}Generating checksums...${NC}" | ||
|
|
||
| cd "${BUILD_DIR}" | ||
|
|
||
| # Create checksums file | ||
| > checksums.md5 | ||
| > checksums.sha256 | ||
|
|
||
| for apk in *.apk; do | ||
| if [ -f "$apk" ]; then | ||
| echo "Generating checksum for $apk..." | ||
| md5sum "$apk" >> checksums.md5 | ||
| sha256sum "$apk" >> checksums.sha256 | ||
| fi | ||
| done | ||
|
|
||
| cd "${REPO_DIR}" | ||
| echo -e "${GREEN}✓ Checksums generated${NC}" | ||
| } |
There was a problem hiding this comment.
Checksums miss APKs stored in per‑profile directories.
update_checksums only scans ${BUILD_DIR} for *.apk, but builds are output to ${BUILD_DIR}/${profile}. This will produce empty checksum files.
🔧 Suggested fix
- cd "${BUILD_DIR}"
-
- # Create checksums file
- > checksums.md5
- > checksums.sha256
-
- for apk in *.apk; do
- if [ -f "$apk" ]; then
- echo "Generating checksum for $apk..."
- md5sum "$apk" >> checksums.md5
- sha256sum "$apk" >> checksums.sha256
- fi
- done
+ cd "${BUILD_DIR}"
+
+ : > checksums.md5
+ : > checksums.sha256
+
+ find . -name "*.apk" -print0 | while IFS= read -r -d '' apk; do
+ echo "Generating checksum for $apk..."
+ md5sum "$apk" >> checksums.md5
+ sha256sum "$apk" >> checksums.sha256
+ done🧰 Tools
🪛 Shellcheck (0.11.0)
[warning] 123-123: This redirection doesn't have a command. Move to its command (or use 'true' as no-op).
(SC2188)
[warning] 124-124: This redirection doesn't have a command. Move to its command (or use 'true' as no-op).
(SC2188)
🤖 Prompt for AI Agents
In `@build-apk.sh` around lines 115 - 136, The update_checksums function currently
only scans ${BUILD_DIR} for *.apk so it misses APKs placed in per-profile
subdirectories (${BUILD_DIR}/${profile}); modify update_checksums to recursively
find APKs under ${BUILD_DIR} (or iterate over profile subdirs) and append their
checksums to checksums.md5 and checksums.sha256; update the logic in
update_checksums (referencing update_checksums, BUILD_DIR and profile) to use
find or a loop over "${BUILD_DIR}"/* to locate all *.apk files, handle paths
with spaces, and write results into the existing checksums files before
returning to REPO_DIR.
| const response = await fetch( | ||
| `https://maps.googleapis.com/maps/api/geocode/json?latlng=${latitude},${longitude}&key=${googlePlacesApiKey}` | ||
| ); | ||
|
|
||
| const data = await response.json(); | ||
|
|
||
| if (data.status === "OK" && data.results.length > 0) { | ||
| return data.results[0].formatted_address; | ||
| } else { | ||
| throw new Error("No address found for the selected location"); |
There was a problem hiding this comment.
Add HTTP response status check before parsing JSON.
If the API returns an HTTP error (4xx/5xx), response.json() may fail or return unexpected data. Check response.ok first to provide clearer error messages.
🐛 Suggested fix
const response = await fetch(
`https://maps.googleapis.com/maps/api/geocode/json?latlng=${latitude},${longitude}&key=${googlePlacesApiKey}`
);
+ if (!response.ok) {
+ throw new Error(`Geocoding API request failed with status ${response.status}`);
+ }
+
const data = await response.json();
if (data.status === "OK" && data.results.length > 0) {🤖 Prompt for AI Agents
In `@lib/geocoding.ts` around lines 8 - 17, Before calling response.json() in the
geocoding fetch logic, check the HTTP status using response.ok and throw a
descriptive error including response.status and response.statusText when the
request failed; update the fetch block that constructs the URL with
googlePlacesApiKey (the response variable and the error throw "No address found
for the selected location") to first do if (!response.ok) throw new
Error(`Geocoding API error ${response.status}: ${response.statusText}`) so you
only call response.json() for successful responses and preserve the existing "No
address found..." error path when data.status !== "OK" or results are empty.
| // Resolve react-native imports to react-native-web | ||
| config.resolver.extraNodeModules = { | ||
| "react-native": require.resolve("react-native-web"), | ||
| "react-native-maps": require.resolve( | ||
| "./web-polyfills/react-native-maps-web.js", | ||
| ), | ||
| }; |
There was a problem hiding this comment.
Critical: Unconditional aliasing breaks native builds.
The extraNodeModules configuration unconditionally aliases react-native to react-native-web, which will break iOS and Android builds. This should be conditional on EXPO_USE_WEB_PLATFORM, similar to the sourceExts configuration.
🐛 Proposed fix
-// Resolve react-native imports to react-native-web
-config.resolver.extraNodeModules = {
- "react-native": require.resolve("react-native-web"),
- "react-native-maps": require.resolve(
- "./web-polyfills/react-native-maps-web.js",
- ),
-};
+// Web-specific module resolution
+if (process.env.EXPO_USE_WEB_PLATFORM) {
+ config.resolver.extraNodeModules = {
+ ...config.resolver.extraNodeModules,
+ "react-native": require.resolve("react-native-web"),
+ "react-native-maps": require.resolve(
+ "./web-polyfills/react-native-maps-web.js",
+ ),
+ };
+}🤖 Prompt for AI Agents
In `@metro.config.js` around lines 11 - 17, The current unconditional aliasing in
config.resolver.extraNodeModules (mapping "react-native" => "react-native-web"
and "react-native-maps" => "./web-polyfills/react-native-maps-web.js") breaks
native builds; update the logic to only set these aliases when
EXPO_USE_WEB_PLATFORM is enabled (mirror the existing conditional used for
sourceExts). Specifically, wrap or gate the assignment to
config.resolver.extraNodeModules behind a check of
process.env.EXPO_USE_WEB_PLATFORM (or the same variable/name used by
sourceExts), leaving resolver behavior untouched for native builds when the flag
is not set.
| { | ||
| "buildCommand": "npm run build:web", | ||
| "outputDirectory": "dist", | ||
| "devCommand": "expo start --web", | ||
| "framework": "other", | ||
| "builds": [ | ||
| { | ||
| "src": "package.json", | ||
| "use": "vercel/static-build", | ||
| "config": { | ||
| "outputDirectory": "dist" | ||
| } | ||
| } | ||
| ], | ||
| "routes": [{ "handle": "filesystem" }, { "src": "/.*", "dest": "/" }] |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Vercel static-build configuration Expo web expo export SPA routing
💡 Result:
For an Expo Web SPA (single index.html) deployed as a static site on Vercel, use Expo’s web export to dist/ and add a catch‑all rewrite so refresh/direct hits on /any/route return your SPA entry file.
1) Ensure you’re exporting an SPA
In app.json / app.config.js, set Expo Router’s web output to SPA mode (single is the SPA mode; static generates per-route HTML). [1]
2) Build output (local + Vercel)
Export for web (outputs to dist/): [2][1]
npx expo export -p web3) vercel.json for Expo static export + SPA routing
Create vercel.json in the repo root (Expo’s docs example): [1]
{
"buildCommand": "expo export -p web",
"outputDirectory": "dist",
"devCommand": "expo",
"cleanUrls": true,
"framework": null,
"rewrites": [
{ "source": "/:path*", "destination": "/" }
]
}That rewrite is the key fix for Vercel 404s on refresh/deep links in an SPA. [1][3]
🌐 Web query:
Vercel routes fallback SPA single page application configuration
💡 Result:
To make a SPA (client-side router) fallback on Vercel, add a vercel.json that rewrites “unknown” paths to your SPA entry (usually index.html).
Basic SPA fallback (static-only SPA)
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
}Vercel’s rewrites already prioritize real files in your output (the “filesystem check”), so existing assets like /assets/app.js won’t be rewritten. [1][2]
SPA fallback but don’t catch /api/*
Use a negative lookahead so /api/... is excluded from the fallback:
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"rewrites": [
{ "source": "/((?!api/).*)", "destination": "/index.html" }
]
}This pattern is commonly used to prevent SPA fallbacks from hijacking API routes. [3]
Legacy alternative (routes + filesystem handle)
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"routes": [
{ "handle": "filesystem" },
{ "src": "/(.*)", "dest": "/index.html" }
]
}This is the older equivalent approach. [1]
Sources: [1] Vercel project configuration (Legacy SPA Fallback examples) [2] Vercel rewrites docs [3] Vercel discussion showing /((?!api/).*) SPA fallback exclusion pattern
🌐 Web query:
Expo web build output directory dist vercel.json
💡 Result:
Expo’s current web export command outputs to dist/ (not web-build/). Use:
- Build:
npx expo export -p web - Output directory:
dist(docs.expo.dev)
A working vercel.json (for SPA-style routing) is:
{
"buildCommand": "expo export -p web",
"outputDirectory": "dist",
"devCommand": "expo",
"cleanUrls": true,
"framework": null,
"rewrites": [{ "source": "/:path*", "destination": "/" }]
}
``` ([docs.expo.dev](https://docs.expo.dev/guides/publishing-websites/?utm_source=openai))
Citations:
- 1: https://docs.expo.dev/guides/publishing-websites/?utm_source=openai
- 2: https://docs.expo.dev/guides/publishing-websites/?utm_source=openai
---
</details>
**Update vercel.json to match Expo web deployment guidance—use `expo export -p web`, correct `devCommand`, and switch to modern `rewrites` format.**
The configuration has several deviations from [Expo's official docs](https://docs.expo.dev/guides/publishing-websites/):
- `buildCommand`: Use `"expo export -p web"` instead of `"npm run build:web"`
- `devCommand`: Use `"expo"` instead of `"expo start --web"`
- `framework`: Use `null` instead of `"other"`
- Routing: Use the modern `rewrites` format instead of legacy `routes`; change to `"rewrites": [{ "source": "/:path*", "destination": "/" }]`
- Add `"cleanUrls": true` for cleaner URLs
<details>
<summary>Corrected vercel.json</summary>
```json
{
"buildCommand": "expo export -p web",
"outputDirectory": "dist",
"devCommand": "expo",
"cleanUrls": true,
"framework": null,
"rewrites": [{ "source": "/:path*", "destination": "/" }]
}🤖 Prompt for AI Agents
In `@vercel.json` around lines 1 - 15, Update the vercel.json settings to follow
Expo web deployment guidance: change the buildCommand value from "npm run
build:web" to "expo export -p web", set devCommand to "expo" instead of "expo
start --web", set framework to null (not "other"), replace the legacy "routes"
array with a modern "rewrites" entry using { "source": "/:path*", "destination":
"/" }, and add cleanUrls: true; adjust the outputDirectory key only if needed
but leave "dist" as-is if correct.
| - `@stripe/stripe-react-native` → Needs Stripe.js web version | ||
| - `expo-location` → Needs browser geolocation API | ||
| - `react-native-maps` → Needs Google Maps JavaScript API | ||
| - `expo-local-authentication` → WebAuthn API | ||
| - Turing device sensors, camera, etc. | ||
|
|
There was a problem hiding this comment.
Fix typo in native modules list.
“Turing device sensors” reads like a typo and should be “Other/Additional device sensors.”
✏️ Suggested edit
-- Turing device sensors, camera, etc.
+- Other device sensors, camera, etc.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - `@stripe/stripe-react-native` → Needs Stripe.js web version | |
| - `expo-location` → Needs browser geolocation API | |
| - `react-native-maps` → Needs Google Maps JavaScript API | |
| - `expo-local-authentication` → WebAuthn API | |
| - Turing device sensors, camera, etc. | |
| - `@stripe/stripe-react-native` → Needs Stripe.js web version | |
| - `expo-location` → Needs browser geolocation API | |
| - `react-native-maps` → Needs Google Maps JavaScript API | |
| - `expo-local-authentication` → WebAuthn API | |
| - Other device sensors, camera, etc. |
🤖 Prompt for AI Agents
In `@WEB_DEPLOYMENT_GUIDE.md` around lines 84 - 89, Replace the typo "Turing
device sensors, camera, etc." in the native modules list with a clearer phrase
such as "Other/Additional device sensors, camera, etc."; update the list item
that currently reads `Turing device sensors, camera, etc.` so it reads
`Other/Additional device sensors, camera, etc.` (or similar wording) to fix the
wording in the native modules section.
| if (typeof window !== "undefined" && process.env.EXPO_USE_WEB_PLATFORM) { | ||
| // Mock expo-location | ||
| global.expo_location = { | ||
| getCurrentPositionAsync: async () => ({ | ||
| coords: { | ||
| latitude: 37.7749, | ||
| longitude: -122.4194, | ||
| accuracy: 1, | ||
| }, | ||
| }), | ||
| requestForegroundPermissionsAsync: async () => ({ status: "granted" }), | ||
| }; | ||
|
|
||
| // Mock stripe payment sheet | ||
| global.stripe_payment_sheet = { | ||
| initPaymentSheet: async () => ({ paymentOption: null }), | ||
| presentPaymentSheet: async () => ({ error: null }), | ||
| }; | ||
|
|
||
| // Mock expo-local-authentication | ||
| global.expo_auth = { | ||
| authenticateAsync: async () => ({ success: true }), | ||
| isEnrolledAsync: async () => true, | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's confirm the file exists and review the full context
find . -name "index.js" -path "*web-polyfills*" -type fRepository: adrianhajdin/uber
Length of output: 85
🏁 Script executed:
# Check if there's any documentation or tests that might clarify env var usage
find . -type f \( -name "*.md" -o -name "*.config.js" -o -name "*.env*" \) | grep -E "(README|config|env)" | head -20Repository: adrianhajdin/uber
Length of output: 129
🌐 Web query:
Expo/React Native web global object polyfill best practices globalThis vs global
💡 Result:
What to use: globalThis vs global (Expo / React Native Web)
- Prefer
globalThisfor cross-platform code. React Native explicitly documents that:globalis a legacy alias forglobalThisand recommendsglobalThisinstead. [1]window/selfare also aliases forglobalThisand RN recommendsglobalThisover them too. [2]
- On the web,
globalThisis the standard, spec-defined way to access the global object across environments. [3]
Best-practice polyfill/alias patterns (when you must support a library)
1) If a dependency expects Node-style global on web
Some packages written for Node/RN may reference global. In a browser build, you can safely alias it early:
// polyfills.ts (import first!)
if (typeof globalThis.global === 'undefined') {
// `@ts-expect-error` - define legacy alias for deps
globalThis.global = globalThis;
}This aligns with RN’s “global is a legacy alias for globalThis” model. [1]
2) If you need to support environments missing globalThis
This is uncommon for modern Expo web targets, but if you truly need it, use a spec-compliant shim (rather than hand-rolled Function("return this")() tricks):
- The
globalthispackage provides a spec-compliant shim API. [4]
3) Put polyfills before anything else
Ensure your polyfill file is imported before other imports (so dependencies see it at module evaluation time):
import './polyfills';
import { registerRootComponent } from 'expo';
import App from './App';
registerRootComponent(App);Bundler guidance (Webpack / Expo web)
- If you’re tempted to inject globals via bundler config (e.g., shimming), do it only when necessary—Webpack’s own guidance is that relying on globals is generally discouraged and shimming is a last resort. [5]
Rule of thumb
- Write new code against
globalThis. - Only add
global = globalThis(web) to satisfy third-party dependencies. - Prefer targeted shims/polyfills over broad “make the web look like Node” polyfills.
Sources: React Native global docs [1], RN window alias docs [2], MDN globalThis reference [3], globalthis shim package docs [4], Webpack shimming guidance [5].
🌐 Web query:
JavaScript environment variables process.env string comparison "true" "false"
💡 Result:
In Node.js, values in process.env are strings (or undefined)—even if you assign non-strings, Node will implicitly convert them to strings (e.g., null becomes 'null'). [1]
So for boolean-like env vars, compare to the string values you expect:
const enabled = process.env.FEATURE_ENABLED === 'true'; // only true when exactly "true"If you want to accept more inputs ("1", "TRUE", etc.), normalize first:
const enabled = (process.env.FEATURE_ENABLED ?? '').toLowerCase() === 'true';This “compare to 'true'” pattern is the common approach. [2]
Sources: [1] Node.js process.env docs, [2] Stack Overflow discussion on boolean env vars
🌐 Web query:
globalThis browser compatibility availability web environment
💡 Result:
globalThis is the standardized way to access the global object across JavaScript environments (Window, Web Workers, Node.js, etc.), avoiding environment-specific globals like window, self, or global. [1]
Browser compatibility (web)
Supported in all modern browsers; key minimum versions: Chrome 71+, Firefox 65+, Safari 12.1+, Edge 79+ (Chromium-based). [2][3]
Not supported in Internet Explorer (all versions, including IE 11) and legacy Edge (EdgeHTML 12–18). [2][4]
Overall global support is ~95%+ of users (per Can I use). [2]
Non-browser / other JS environments
- Node.js: supported since Node 12 (and
globalis considered legacy—useglobalThis). [3][5] - Web Workers:
globalThisworks in worker contexts as well (unlikewindow). [1]
If you need to support older browsers
Use a globalThis polyfill (commonly via core-js). [1]
🏁 Script executed:
# Let's examine the actual file content and context around the issue
cat -n ./web-polyfills/index.jsRepository: adrianhajdin/uber
Length of output: 1128
🏁 Script executed:
# Check if there are any related config files that might set these env vars
grep -r "EXPO_USE_WEB_PLATFORM" . --include="*.js" --include="*.ts" --include="*.json" --include="*.config.*" 2>/dev/null | head -20Repository: adrianhajdin/uber
Length of output: 240
🏁 Script executed:
# Check the entry point and how this polyfill file is imported
find . -name "index.js" -o -name "App.js" -o -name "app.js" | xargs grep -l "web-polyfills" 2>/dev/nullRepository: adrianhajdin/uber
Length of output: 45
🏁 Script executed:
# Check how the metro.config.js uses this env var to understand the pattern
cat -n ./metro.config.js | head -30Repository: adrianhajdin/uber
Length of output: 831
🏁 Script executed:
# Look for any documentation or setup files that explain how EXPO_USE_WEB_PLATFORM is intended to be used
grep -r "EXPO_USE_WEB_PLATFORM" . --include="*.md" 2>/dev/nullRepository: adrianhajdin/uber
Length of output: 114
Use globalThis and explicitly check env var against the string "true".
global is a legacy alias that isn't guaranteed to exist in browser environments; React Native recommends globalThis instead. Additionally, the current truthy check will incorrectly treat the string "false" as enabled, since non-empty strings are truthy in JavaScript. The env var should be explicitly compared to "true".
🛠️ Proposed fix
-if (typeof window !== "undefined" && process.env.EXPO_USE_WEB_PLATFORM) {
+const useWebPolyfills =
+ typeof window !== "undefined" &&
+ process.env.EXPO_USE_WEB_PLATFORM === "true";
+
+if (useWebPolyfills) {
+ const g = globalThis;
// Mock expo-location
- global.expo_location = {
+ g.expo_location = {
getCurrentPositionAsync: async () => ({
coords: {
latitude: 37.7749,
longitude: -122.4194,
accuracy: 1,
},
}),
requestForegroundPermissionsAsync: async () => ({ status: "granted" }),
};
// Mock stripe payment sheet
- global.stripe_payment_sheet = {
+ g.stripe_payment_sheet = {
initPaymentSheet: async () => ({ paymentOption: null }),
presentPaymentSheet: async () => ({ error: null }),
};
// Mock expo-local-authentication
- global.expo_auth = {
+ g.expo_auth = {
authenticateAsync: async () => ({ success: true }),
isEnrolledAsync: async () => true,
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (typeof window !== "undefined" && process.env.EXPO_USE_WEB_PLATFORM) { | |
| // Mock expo-location | |
| global.expo_location = { | |
| getCurrentPositionAsync: async () => ({ | |
| coords: { | |
| latitude: 37.7749, | |
| longitude: -122.4194, | |
| accuracy: 1, | |
| }, | |
| }), | |
| requestForegroundPermissionsAsync: async () => ({ status: "granted" }), | |
| }; | |
| // Mock stripe payment sheet | |
| global.stripe_payment_sheet = { | |
| initPaymentSheet: async () => ({ paymentOption: null }), | |
| presentPaymentSheet: async () => ({ error: null }), | |
| }; | |
| // Mock expo-local-authentication | |
| global.expo_auth = { | |
| authenticateAsync: async () => ({ success: true }), | |
| isEnrolledAsync: async () => true, | |
| }; | |
| const useWebPolyfills = | |
| typeof window !== "undefined" && | |
| process.env.EXPO_USE_WEB_PLATFORM === "true"; | |
| if (useWebPolyfills) { | |
| const g = globalThis; | |
| // Mock expo-location | |
| g.expo_location = { | |
| getCurrentPositionAsync: async () => ({ | |
| coords: { | |
| latitude: 37.7749, | |
| longitude: -122.4194, | |
| accuracy: 1, | |
| }, | |
| }), | |
| requestForegroundPermissionsAsync: async () => ({ status: "granted" }), | |
| }; | |
| // Mock stripe payment sheet | |
| g.stripe_payment_sheet = { | |
| initPaymentSheet: async () => ({ paymentOption: null }), | |
| presentPaymentSheet: async () => ({ error: null }), | |
| }; | |
| // Mock expo-local-authentication | |
| g.expo_auth = { | |
| authenticateAsync: async () => ({ success: true }), | |
| isEnrolledAsync: async () => true, | |
| }; | |
| } |
🤖 Prompt for AI Agents
In `@web-polyfills/index.js` around lines 4 - 27, Replace usage of the legacy
global alias and the truthy env check: change the conditional to check typeof
window !== "undefined" && process.env.EXPO_USE_WEB_PLATFORM === "true", and
assign mocks to globalThis (e.g. globalThis.expo_location,
globalThis.stripe_payment_sheet, globalThis.expo_auth) instead of global.*; keep
the same mock shapes for getCurrentPositionAsync,
requestForegroundPermissionsAsync, initPaymentSheet, presentPaymentSheet,
authenticateAsync and isEnrolledAsync.
Summary by CodeRabbit
Release Notes
New Features
Style
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.