mcpField
L
limrun-expo-development

limrun-expo-development

github.com/limrun-inc开发243.2k 次安装1 天前更新
代码仓库

关于 limrun-expo-development

面向 Expo / React Native 开发的 Limrun 技能,指导在远程 iOS 模拟器和 Android 模拟器上构建、启动及验证应用。内容涵盖开发客户端、Metro 连接和原生变更后的重建流程。

主要功能

  1. 01Expo 项目就绪检查
  2. 02远程构建并安装 Debug 应用
  3. 03通过 Metro 迭代 JS / TS
  4. 04连接 iOS 与 Android 模拟器
  5. 05验证界面并检查应用日志

适用场景

  1. 01在远程模拟器上调试 Expo 应用
  2. 02原生依赖变更后重建开发客户端
  3. 03通过 Metro 预览 JS / TS 修改

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/limrun-inc/skills --skill limrun-expo-development

安装后在 Claude Code 中输入/limrun-expo-development确认已加载。

详细介绍

在 Limrun 上开发 Expo 应用

将此技能用于 Expo / React Native 专属设置和开发客户端迭代,适用于 iOS 模拟器和 Android 模拟器。有关命令详情、设备交互、截图、录屏和清理,请使用 limrun-ios-simulator 和 limrun-android-emulator;有关构建标志详情和非 Expo 工作流,请使用构建技能(limrun-xcode、limrun-gradle)。

所有构建和设备操作都必须在 Limrun 上运行。不要使用本地 Xcode、本地模拟器、本地 Android SDK 或本地模拟器;本地 adb 仅用于通过 CLI 的隧道与远程模拟器通信。

Expo 就绪检查

在更改 Expo 依赖项或应用配置之前,请检查应用的 Expo SDK 版本,并使用对应版本的 Expo 文档。

验证这是一个 Expo 应用:

npx expo config --type introspect --json

推导以下值:

  • 从 ios.bundleIdentifier(iOS)获取 BUNDLE_ID,从 android.package(Android)获取 PACKAGE。当缺少 android.package 时,introspect 会报告占位值(例如 com.placeholder.appid),但构建会生成不同的真实 applicationId;请在构建前于 app.json 中设置 android.package,确保 $PACKAGE 与已安装的应用一致。
  • 从 slug 获取 SLUG
  • 从 scheme 获取 SCHEME,若未设置,则使用 exp+${SLUG}
  • 从 git branch --show-current 获取 BRANCH,若未设置,则使用 main
  • iOS 上的 ASSET_NAME="${BUNDLE_ID}/${BRANCH}-debug.zip";Android 上的 ASSET_NAME="${PACKAGE}/${BRANCH}-debug.apk"

确保安装开发客户端

Expo 开发构建需要 expo-dev-client。如果 package.json 中缺少该依赖,请添加:

npx expo install expo-dev-client

安装 expo-dev-client、添加/移除/更新原生依赖项,或更改原生应用配置,都会导致已上传的 Debug 资源过期。启动开发循环前,请构建新的 Debug 应用。原生依赖项发生更改会使已上传的 Debug 资源过期,因此必须先重新构建,再开始开发循环。

Debug 构建资源

首先检查是否已有可复用的 Debug 开发客户端资源:

lim asset list --name-prefix "$BUNDLE_ID/"   # iOS
lim asset list --name-prefix "$PACKAGE/"     # Android

仅在以下条件均满足时,才复用完全匹配的 $ASSET_NAME:

  • 该资源存在;且
  • 本次会话中未更改原生依赖项或原生配置。

如果当前任务更改了原生依赖项或原生配置,即使 $ASSET_NAME 存在,也请跳过资源复用。

复用资源时,请创建或复用设备并安装资源:

lim ios create \
  --reuse-if-exists \
  --install-asset "$ASSET_NAME" \
  --label repo=<repo> \
  --label agent=<agent>

lim android create \
  --reuse-if-exists \
  --install-asset "$ASSET_NAME" \
  --no-open \
  --label repo=<repo> \
  --label agent=<agent>

Android 注意事项:保留 create 默认打开的隧道(此处不要传入 --no-connect,这与普通设备操作会话不同);下方的 Metro 反向 隧道将在该隧道上运行。请记下输出中的实例 ID,并在后续每次 lim android 调用中传入 --id:Metro 和 Expo 从应用 目录运行,且实例解析按 git 工作树区分,因此从其他目录运行的命令 无法自行找到该实例。

在 Android 上进行全新构建

远程构建 Debug APK 并将其上传为资源(Expo prebuild、 --expo-app-dir 和其他构建标志请参阅 limrun-gradle;默认的 assembleDebug 任务适用于开发客户端构建):

lim gradle build . --upload "$ASSET_NAME"
lim android create --reuse-if-exists --install-asset "$ASSET_NAME" --no-open --label repo=<repo> --label agent=<agent>

如果要在运行中的模拟器上稍后重新构建原生应用,请使用 --upload 进行重新构建, 并通过构建输出的 Download URL 安装新的 APK(实例会在服务端获取该 APK):

lim gradle build . --upload "$ASSET_NAME"
lim android install-app "<Download URL from the build output>" --id <android-instance-id>
在 iOS 上全新构建

全新构建时,先创建或重用独立的 Xcode 沙盒并进行构建,再创建模拟器,避免模拟器在漫长的构建期间闲置(并触发闲置超时):

lim xcode create --reuse-if-exists --label repo=<repo> --label agent=<agent>

lim xcode build . \
  --configuration Debug \
  --upload "$ASSET_NAME"

如果项目需要指定 Xcode,请在仓库中运行一次 lim xcode version set <major|major.minor>(Xcode 27 正式版使用 27,测试版使用 27.1);规则请参阅 limrun-xcode。

如果项目布局需要,请使用 --expo-app-dir、--scheme 或 --workspace。

然后创建连接到该 Xcode 目标的模拟器;连接后会立即安装并启动构建产物:

lim ios create --attach \
  --reuse-if-exists \
  --label repo=<repo> \
  --label agent=<agent>

如果没有浏览器可向用户展示,请在任何 create 命令中添加 --no-open;这会跳过打开流 URL,并在输出中保留该 URL,供分享使用。

如果已有模拟器因重用资源而在运行,之后又需要进行原生重建,请连接到同一模拟器,而不是创建第二个模拟器:

lim xcode attach-simulator <ios-instance-id> --id <xcode-instance-id>

连接后,每次 lim xcode build 成功,都会在已连接的模拟器上安装并启动应用。

在 iOS 上通过 Limrun 启动 Metro

此流程适用于 iOS。Android 使用 adb reverse;请跳转到在 Android 上启动 Metro,不要运行 lim ios 命令。

Debug 应用安装后,启动一个目标隧道。Metro 可以继续使用其常规本地端口;Expo 会公布 localhost:

METRO_PORT=8081
lim ios tunnel \
  --selector "localhost:${METRO_PORT}" \
  --detach \
  --id <ios-instance-id>
TUNNEL_URL="http://localhost:${METRO_PORT}"
echo "TUNNEL_URL=$TUNNEL_URL"

EXPO_PACKAGER_PROXY_URL="$TUNNEL_URL" \
  npx expo start --dev-client --port "$METRO_PORT"

EXPO_PACKAGER_PROXY_URL 会在清单、bundle URL 和深层链接中保留 localhost 和声明的端口。请将其内联设置,以使其优先于项目 dotenv 值。用户迭代期间,请让 Metro 和分离运行的隧道保持运行。将 Metro 作为受管理的后台进程运行,或者先把输出的 TUNNEL_URL 复制到另一个终端,再启动应用。

如果端口 8081 已被占用,请选择另一个明确的端口,并在隧道选择器、TUNNEL_URL 和 Expo 的 --port 中使用相同的值。选择器集合不可变:端口发生变化时,请停止并使用完整的选择器列表重新创建隧道。

只有在依赖项已安装且环境确实与网络隔离时,才添加 --offline。离线模式会禁用网络检查和依赖项验证,因此不要用它来绕过常规的 Expo 身份验证。

启动 iOS 开发客户端

通过开发客户端 URL 打开 Debug 应用:

ENCODED_URL="$(node -e 'console.log(encodeURIComponent(process.argv[1]))' "$TUNNEL_URL")"
DEV_CLIENT_URL="${SCHEME}://expo-development-client/?url=${ENCODED_URL}"
lim ios open-url --id <ios-instance-id> "$DEV_CLIENT_URL"

如果打开失败,且主 scheme 来自 scheme,请使用 exp+${SLUG} 重试一次。在全新实例中,iOS 开发菜单的引导页面可能会拦截第一个深层链接;请点按完成引导,然后再次打开 URL。

使用 Expo Go 时,将 --dev-client 替换为 --go,然后打开:

lim ios open-url \
  --id <ios-instance-id> \
  "exp://${TUNNEL_URL#http://}"

隧道生命周期:

lim ios tunnel status --id <ios-instance-id> --json
lim ios tunnel stop --id <ios-instance-id>

一个实例只能接受一个活动目标隧道。开始另一组路由之前,请先停止当前隧道。迭代结束后,使用 Ctrl+C 停止 Metro,并使用上述命令停止分离运行的隧道。

如果模拟器在 Metro 停止时尝试访问某条路由,隧道仍会保持活动状态,状态中会记录相关联的 connection_refused。使用相同的代理 URL 重启 Metro,并重新打开开发客户端 URL;不要重新创建模拟器或隧道。

在 Android 上启动 Metro

Android 使用 CLI 的 ADB 隧道通过 adb reverse 连接。Metro 保持默认端口 8081,无需覆盖打包器主机名,模拟器通过 http://127.0.0.1:8081 访问 Metro:

lim android connect --id <android-instance-id>   # background shell; prints "Tunnel started on 127.0.0.1:<port>."
adb -s 127.0.0.1:<port> reverse tcp:8081 tcp:8081

npx expo start --dev-client --port 8081

DEV_CLIENT_URL="${SCHEME}://expo-development-client/?url=http%3A%2F%2F127.0.0.1%3A8081"
lim android open-url "$DEV_CLIENT_URL" --id <android-instance-id>

ADB 隧道会随启动它的 shell 一同结束,且每次重新连接时端口都会变化;每次重新连接后,都要使用新的序列号重新运行 adb reverse。隧道详情请参阅 limrun-android-emulator。

首次启动时,请点按完成开发菜单的引导页面(lim android tap-element --text Continue),然后关闭开发菜单。bundle 会在原生页面后方加载。

回退方案:Expo 隧道

如果无法使用 Limrun 端点,请启动 Expo 的公共隧道:

npm install --save-dev '@expo/ngrok@^4.1.0'
npx expo start --dev-client --tunnel

请使用 Expo 输出的完整开发客户端 URI:

DEV_CLIENT_URL="<complete URI printed by Expo>"
lim ios open-url --id <ios-instance-id> "$DEV_CLIENT_URL"
lim android open-url "$DEV_CLIENT_URL" --id <android-instance-id>

旧版 iOS 固定端口反向隧道

对于已经使用预留端口范围 57090–57099 的工作流,仍可使用 lim ios reverse。 Expo dev-client 可以推导或公布多个打包器 URL,因此像 57090:8081 这样的映射不匹配,可能会导致某些 URL 指向本地 Metro 端口,而不是面向模拟器的反向端点。

在 REACT_NATIVE_PACKAGER_HOSTNAME 和编码后的 dev-client URL 中,都使用 lim ios reverse 打印的面向模拟器的主机。运行 Metro 时,请在单独的终端或后台终端中持续运行反向命令:

lim ios reverse 57090:57090 --id <ios-instance-id>

REACT_NATIVE_PACKAGER_HOSTNAME=<reverse-host> \
  npx expo start --dev-client --host lan --port 57090

ENCODED_URL="$(node -e 'console.log(encodeURIComponent(process.argv[1]))' "http://<reverse-host>:57090")"
DEV_CLIENT_URL="${SCHEME}://expo-development-client/?url=${ENCODED_URL}"
lim ios open-url --id <ios-instance-id> "$DEV_CLIENT_URL"

验证

如需快速进行静态验证,优先使用:

npx tsc --noEmit

只有在仓库已配置 ESLint 时,才运行 npm run lint 或 npx expo lint。对于尚未配置 lint 的项目,Expo lint 可能会创建 ESLint 配置并修改依赖项。

在 iOS 上,先查看元素树:

lim ios element-tree

成功表示应用 UI 可见,或 Expo 开发菜单显示已连接到隧道。在全新实例上,首次启动 dev-client 时可能会进入覆盖启动器的开发菜单引导页:点按继续(lim ios tap-element --ax-label Continue),然后重新打开 dev-client URL,因为首次深层链接会被该页面消耗。如果元素树无法确认连接,请检查应用日志:

lim ios app-log "$BUNDLE_ID" --tail 100

在 Android 上,请通过屏幕截图而非元素树进行验证:Expo 应用通常不会在元素树中提供无障碍节点,因此屏幕已渲染但元素树为空是正常的(参见 limrun-android-emulator):

lim android screenshot check.png --id <android-instance-id>

如需了解应用为何退出(崩溃、ANR),请在监控模式下重新启动应用;应用运行期间命令会一直阻塞(请在后台 shell 中运行),应用退出时会打印退出原因、堆栈跟踪以及近期应用日志的末尾内容:

lim android launch-app "$PACKAGE" --mode RelaunchIfRunning --id <android-instance-id>

迭代

连接后,JS/TS 编辑应通过 Metro 更新,无需再次进行原生构建。如果任务更改了原生依赖项、原生配置或构建设置,请在重新启动开发循环前重新构建 Debug。

告知用户:

  • 设备串流的 Markdown 短链接,例如 [打开模拟器串流](<signedStreamUrl>) 或 [打开模拟器串流](<signedStreamUrl>)
  • 已上传的 Debug 资源名称
  • 现在可以通过 Metro 迭代 JS/TS 更改
  • 原生更改需要新的 Debug 构建

最终预览

如需最终的可分享预览或 PR 演示,请使用 Release 构建,这样用户无需运行 Metro:

ASSET_NAME="<bundle-id>/<pr-or-session>.zip"
lim xcode build . --configuration Release --upload "$ASSET_NAME"

ASSET_NAME="<package>/<pr-or-session>.apk"
lim gradle build . --task assembleRelease --upload "$ASSET_NAME"

预览 URL(APK 资源需使用 platform=android):

https://console.limrun.com/preview?asset=${ASSET_NAME}&platform=ios

注意事项

  • npx expo start --dev-client 需要 expo-dev-client;没有它,Expo 无法确定开发构建的 scheme。
  • No script URL provided 通常表示该应用并非 dev-client 构建,或启动时未提供 dev-client URL。
  • 全新原生重建/安装后,类似 Cannot find native module 的过期 Metro/运行时错误可能来自旧应用进程。重新打开 dev-client URL,并在认定重建失败前通过 element-tree 验证。
  • 如果添加原生依赖项后,Debug 构建仍表现得像旧的原生依赖图,这是异常的 Limrun 行为。请重试构建;创建新的构建/设备目标仅作为故障排查的备用方案。
  • Expo 隧道启动可能不稳定。更改工作流前请先重试。
  • 原生依赖项或原生配置更改后,请勿重复使用已上传的 Debug 资源。
  • 在此循环中,每次调用 lim android 都要传入 --id <android-instance-id>:实例解析按 git 工作树分别进行,而循环中的命令会从不同目录运行。
  • 对 Expo 应用而言,截图显示应用正常但 Android 元素树为空是正常的;请通过截图验证。

评论

支持 Markdown