miaoda-expo-devkit 0.1.1-beta.98 → 0.1.1-dev.development-build.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +154 -110
  2. package/dist/babel/plugin-lucide-react-native.js +184 -184
  3. package/dist/cli/lint.js +317 -309
  4. package/dist/index.d.mts +1 -110
  5. package/dist/index.d.ts +1 -110
  6. package/dist/index.js +0 -159
  7. package/dist/index.mjs +0 -132
  8. package/dist/metro.d.mts +25 -5
  9. package/dist/metro.d.ts +25 -5
  10. package/dist/metro.js +49 -18
  11. package/dist/metro.mjs +50 -20
  12. package/dist/rules/no-expo-audio-compat.js +2041 -0
  13. package/dist/rules/no-expo-video-compat.js +90 -90
  14. package/dist/stubs/entry-inject.js +0 -2
  15. package/dist/stubs/expo-native-wechat-stub.js +116 -0
  16. package/dist/stubs/expo-router-entry-native-stub.js +4 -0
  17. package/dist/stubs/i18n/en.js +16 -1
  18. package/dist/stubs/i18n/zh.js +16 -1
  19. package/dist/stubs/logbox-control.js +9 -0
  20. package/dist/stubs/screenshot-control.js +1 -1
  21. package/dist/stubs/sentry-react-native-stub.js +15 -180
  22. package/oxlint-config.json +11 -4
  23. package/package.json +60 -5
  24. package/pnpm-patches/@expo__cli@55.0.16-55.0.22.patch +627 -0
  25. package/pnpm-patches/@expo__cli@55.0.16.patch +218 -0
  26. package/pnpm-patches/@expo__cli@55.0.17.patch +218 -0
  27. package/pnpm-patches/@expo__cli@55.0.18.patch +218 -0
  28. package/pnpm-patches/@expo__cli@55.0.19.patch +218 -0
  29. package/pnpm-patches/@expo__cli@55.0.20.patch +218 -0
  30. package/pnpm-patches/@expo__cli@55.0.21.patch +218 -0
  31. package/pnpm-patches/@expo__cli@55.0.22.patch +218 -0
  32. package/pnpm-patches/@shopify__react-native-skia@2.4.18.patch +14 -0
  33. package/pnpm-patches/expo-modules-core@55.0.15.patch +21 -0
  34. package/pnpm-patches/expo@55.0.6.patch +235 -0
  35. package/pnpm-patches/metro-file-map@0.83.3.patch +24 -0
  36. package/pnpm-patches/metro-runtime@0.83.3.patch +13 -0
  37. package/pnpm-patches/react-native-css-interop@0.2.6.patch +12 -0
  38. package/pnpm-patches/react-native-reanimated@4.2.1.patch +53 -0
  39. package/pnpm-patches/react-native-worklets@0.7.2.patch +91 -0
  40. package/pnpm-patches/react-native@0.83.2.patch +384 -0
  41. package/pnpmfile.cjs +168 -0
  42. package/dist/stubs/preview-control.js +0 -60
package/README.md CHANGED
@@ -2,11 +2,10 @@
2
2
 
3
3
  Expo / React Native 开发环境工具集,通过 Metro 构建层注入以下能力:
4
4
 
5
- - **Sentry DSN 替换** — 拦截 `@sentry/react-native`,将真实 DSN 替换为无害 stub,防止开发期错误事件发送到 Sentry 服务器
6
- - **错误 / 网络捕获** — 内置 `SentryCapture`,自动调用 Metro `/symbolicate` 还原源码位置并输出到 `console.warn`
5
+ - **Sentry 初始化守卫** — Native 透传真实初始化;独立 Expo Web 使用安全 fallback;sandbox Web 由前置 polyfill 唯一初始化
7
6
  - **Bundle 首部注入** — 在 expo-router 初始化之前执行自定义脚本
8
- - **HMR postMessage 控制** — 通过 `window.postMessage` 在运行时启动或停止 Fast Refresh
9
- - **LogBox 屏蔽** — web 平台禁用 Expo 全屏错误遮罩
7
+ - **HMR 控制** — Web postMessage 开关,以及复用 Metro WebSocket 的 Agent 批量编辑控制
8
+ - **LogBox 通知策略** — Development Build 的 iOS/Android 默认隐藏 `console.warn`、`console.error` 的 LogBox 通知,日志仍保留在 Metro/ADB;Web 继续使用 no-op 错误遮罩
10
9
  - **Metro transform 缓存持久化** — 将缓存目录固定到项目根目录(可通过 `METRO_CACHE_DIR` 指定),容器/CI 重启后不丢失
11
10
  - **构建耗时日志** — 将 bundle 总耗时和每个 cache miss 文件的 transform 耗时写入 JSONL 文件(通过 `METRO_TRANSFORM_LOG` 启用)
12
11
  - **esbuild minifier** — 生产构建时将 Metro minifier 切换为 esbuild(比 terser 快数十倍)
@@ -60,7 +59,7 @@ module.exports = withExpoCalendarStub(withExpoMediaLibraryStub(withEntryInjectio
60
59
 
61
60
  ### Sentry 初始化
62
61
 
63
- 照常调用 `Sentry.init`,stub 自动注入内置捕获器,无需额外配置:
62
+ 应用可照常调用 `Sentry.init`:
64
63
 
65
64
  ```ts
66
65
  // app/_layout.tsx
@@ -68,34 +67,30 @@ import * as Sentry from '@sentry/react-native';
68
67
 
69
68
  Sentry.init({
70
69
  dsn: 'https://your-key@sentry.io/your-project',
71
- // stub 自动拦截 beforeSend / beforeBreadcrumb,错误摘要输出到 console.warn
72
70
  });
73
71
  ```
74
72
 
75
- #### 自定义回调(可选)
73
+ stub 的行为按平台区分:
76
74
 
77
- 传入自己的 `beforeSend` / `beforeBreadcrumb`,会在内置捕获器之后串联执行:
75
+ - iOS/Android:原样调用真实 `@sentry/react-native` init,应用参数不变;
76
+ - 独立 Expo Web:用安全 fallback DSN 初始化,避免开发事件进入真实项目;
77
+ - vite-sandbox Expo Web:sandbox 生成的 Metro polyfill 已经把应用实际依赖的 `@sentry/browser` 与统一观测运行时打包,并先于应用完成唯一一次 init;stub 检测版本字符串在场标记后吞掉应用 init;
78
+ - 版本不一致:只输出 `console.error`,不向编辑器宿主发送消息。
78
79
 
79
- ```ts
80
- import * as Sentry from '@sentry/react-native';
81
- import { SentryCapture } from 'miaoda-expo-devkit';
82
- import type { ErrorCaptureInfo, NetworkCaptureInfo } from 'miaoda-expo-devkit';
83
-
84
- const capture = new SentryCapture({
85
- onError(info: ErrorCaptureInfo) {
86
- console.log('Error at:', info.symbolicatedFrames?.[0]);
87
- },
88
- onNetwork(info: NetworkCaptureInfo) {
89
- console.log('Network:', info.method, info.url, info.statusCode);
90
- },
91
- });
80
+ fetch/XHR 取数、网络与 console 事件、原始数据长度限制、Supabase 白名单、`GLOBAL_ERROR` 和 Metro `/symbolicate` 均由 vite-sandbox 的同一份运行时维护。devkit 不复制这些规则,也不再包含 capture 或 runtime bridge。
92
81
 
93
- Sentry.init({
94
- dsn: 'https://your-key@sentry.io/your-project',
95
- beforeSend: capture.beforeSend,
96
- beforeBreadcrumb: capture.beforeBreadcrumb,
97
- });
98
- ```
82
+ ### Development Build 的 LogBox 通知
83
+
84
+ `withDevkit` 在开发模式下会通过公共入口脚本调用 `LogBox.ignoreAllLogs(true)`。该设置作用于所有使用
85
+ 此 DevKit Metro 配置的 iOS/Android 应用:
86
+
87
+ - `console.warn`、`console.error` 不再弹出 LogBox 底部通知;
88
+ - 原始日志仍会输出到 Metro、ADB 或 Xcode 控制台;
89
+ - 未捕获的 JavaScript 异常仍由 React Native 和自定义 Development Build 的错误处理链路处理;
90
+ - 如需临时恢复 LogBox,可在应用调试代码中调用 `LogBox.ignoreAllLogs(false)`。
91
+
92
+ 该策略属于 JavaScript Bundle 的公共启动注入,不修改业务页面,也不需要在每个应用中重复配置。未使用
93
+ `withDevkit` 的应用不会自动获得此策略。
99
94
 
100
95
  ### HMR postMessage 控制
101
96
 
@@ -138,7 +133,10 @@ window.__DEVKIT_HMR_ENABLED__ // true | false
138
133
 
139
134
  从 `miaoda-expo-devkit/metro` 引入。
140
135
 
141
- 在 expo-router 启动前注入脚本,注入内容(`dist/stubs/entry-inject.js`):
136
+ 在 expo-router 启动前注入平台对应的公共入口脚本。Web 使用 `dist/stubs/entry-inject.js`;iOS/Android
137
+ 使用 `dist/stubs/expo-router-entry-native-stub.js`,在加载原始入口前安装 LogBox 通知策略。
138
+
139
+ Web 注入内容包括:
142
140
 
143
141
  1. 设置 `globalThis.__DEVKIT_INJECTED__ = true`
144
142
  2. 安装 HMR postMessage 控制器(`window.addEventListener('message', ...)`)
@@ -151,91 +149,72 @@ window.__DEVKIT_HMR_ENABLED__ // true | false
151
149
 
152
150
  ---
153
151
 
154
- ### `new SentryCapture(options)`
155
-
156
- 从 `miaoda-expo-devkit` 引入。
157
-
158
- 用于自定义捕获行为。stub 内置了一个默认实例(输出到 `console.warn`),此类用于叠加自定义处理逻辑。
159
-
160
- | 选项 | 类型 | 说明 |
161
- |---|---|---|
162
- | `onError` | `(info: ErrorCaptureInfo) => void` | 捕获到错误时触发,携带符号化后的调用栈 |
163
- | `onNetwork` | `(info: NetworkCaptureInfo) => void` | 捕获到 HTTP 请求时触发 |
164
- | `symbolicator` | `MetroSymbolicator` | 可选,单测时注入 mock 以隔离网络依赖 |
165
-
166
- 构造完成后,将绑定方法传给 `Sentry.init`:
167
-
168
- ```ts
169
- Sentry.init({
170
- beforeSend: capture.beforeSend,
171
- beforeBreadcrumb: capture.beforeBreadcrumb,
172
- });
173
- ```
152
+ ## 全局标记
174
153
 
175
- #### `ErrorCaptureInfo`
154
+ 注入脚本在 bundle 执行时(早于任何 React 代码)写入以下全局变量,可用于 E2E 测试断言:
176
155
 
177
- | 字段 | 类型 | 说明 |
156
+ | 变量 | 类型 | 说明 |
178
157
  |---|---|---|
179
- | `message` | `string` | 错误信息 |
180
- | `symbolicatedFrames` | `MetroFrame[] \| null` | 经 Metro 符号化后还原到源码位置的调用栈帧 |
181
- | `componentStack` | `string \| null` | React 组件调用栈(ErrorBoundary 捕获的错误才有值) |
182
- | `mechanismType` | `string \| null` | Sentry 捕获方式:`'onerror'`、`'generic'`、`'auto.function.react.error_boundary'` 等 |
158
+ | `window.__DEVKIT_INJECTED__` | `true` | 注入脚本已执行(由 `withEntryInjection` 保证) |
159
+ | `window.__DEVKIT_HMR_ENABLED__` | `boolean` | 当前 HMR 状态,随 postMessage 消息实时更新 |
183
160
 
184
- #### `NetworkCaptureInfo`
161
+ ## 环境变量
185
162
 
186
- | 字段 | 类型 | 说明 |
163
+ | 变量 | 默认值 | 说明 |
187
164
  |---|---|---|
188
- | `method` | `string \| null` | HTTP 方法 |
189
- | `url` | `string \| null` | 请求 URL |
190
- | `statusCode` | `number \| null` | HTTP 状态码(连接级失败为 `null`) |
191
-
192
- ---
165
+ | `INJECT_SENTRY_DSN` | 安全 stub DSN | vite-sandbox 服务端已有变量;sandbox 会自动编入生成 runtime,应用开发者无需配置 |
166
+ | `METRO_CACHE_DIR` | `projectRoot/.metro-cache` | Metro transform 缓存目录绝对路径,优先级最高,适合容器/CI 挂载外部持久目录 |
167
+ | `METRO_TRANSFORM_LOG` | _(未设置时不记录)_ | 构建日志输出文件的绝对路径(JSONL 格式),仅开发模式(`__DEV__`)下生效 |
168
+ | `EXPO_AI_ERROR_COLLECTOR` | _(未设置时关闭)_ | 设为 `1` 后由 DevKit 管理的 Expo CLI patch 采集当前 Metro 编译状态 |
169
+ | `EXPO_AI_ERROR_FILE` | `<projectRoot>/node_modules/.miaoda-expo/ai-error.json` | AI 当前错误状态文件;支持绝对路径或相对项目根目录的路径 |
170
+ | `EXPO_MIAODA_HMR_CONTROL` | _(未设置时关闭)_ | `1` 启用 Metro `/hot` 上的 Agent 暂停、恢复和完整重载协议 |
171
+ | `EXPO_MIAODA_HMR_STATE_FILE` | _(未设置时仅保留进程内状态)_ | 暂停状态持久化文件,使用源码与 Metro watch folders 之外的绝对路径 |
193
172
 
194
- ### `new MetroSymbolicator(options?)`
173
+ ### Development Build 批量编辑控制
195
174
 
196
- 从 `miaoda-expo-devkit` 引入。
175
+ 启用 `EXPO_MIAODA_HMR_CONTROL=1` 后,Agent 可通过 POST
176
+ `/innerapi/v1/sourcecode/__hmr_off` 暂停更新,完成文件写入后调用 `__hmr_on` 恢复;
177
+ 客户端每 2 秒通过 GET `__hmr_status` 校验状态,补偿 `/hot` WebSocket 断连。
178
+ 服务端等待客户端暂停确认,并在恢复前同步文件监听队列、编译最终增量;WebSocket 保持
179
+ 连接,用户 Fast Refresh 偏好独立保留。断线期间漏掉控制消息时,客户端会完整加载最终
180
+ Bundle,避免使用失效的增量 revision。最终编译失败会返回 `422` 并继续保持暂停。
197
181
 
198
- 通过 Metro `/symbolicate` 接口将 bundle 行列号还原为源码位置。`SentryCapture` 内部已默认使用,主要用途是单测时注入 mock。
182
+ 普通恢复执行 Fast Refresh;`__hmr_reload` 执行完整 Bundle 重载,若当前暂停则延迟到
183
+ 恢复时执行。协议、返回值、版本要求和加载边界见 [Metro HMR 批量编辑控制](../../docs/metro-hmr-control.md)。
199
184
 
200
- | 选项 | 类型 | 说明 |
201
- |---|---|---|
202
- | `baseUrl` | `string` | Metro server 地址。不传则从 `NativeModules.SourceCode.scriptURL` 自动推断,fallback 到 `localhost:8081`。 |
185
+ ### Metro 编译错误采集
203
186
 
204
- **单测示例:**
187
+ DevKit 会通过 pnpm patch 接入 Expo CLI 自己的 Metro reporter wrapper,不修改
188
+ LogBox 或 Metro 核心。启用后,首次 bundle 失败也能在前端启动前写入状态文件:
205
189
 
206
- ```ts
207
- const mockSym = {
208
- parseErrorStack: jest.fn(() => []),
209
- symbolicate: jest.fn(async () => null),
210
- };
211
- const onError = jest.fn();
212
- const capture = new SentryCapture({ onError, symbolicator: mockSym });
213
-
214
- await capture.beforeSend(fakeEvent, { originalException: new Error('oops') });
215
-
216
- expect(onError).toHaveBeenCalledWith(
217
- expect.objectContaining({ message: 'oops' }),
218
- );
190
+ ```sh
191
+ EXPO_AI_ERROR_COLLECTOR=1 npx expo start
219
192
  ```
220
193
 
221
- ---
194
+ 默认状态文件位于 `node_modules/.miaoda-expo/ai-error.json`,目录会自动创建。依赖重装
195
+ 可能清理这个目录;需要持久化时请设置 `EXPO_AI_ERROR_FILE` 指向项目外部路径。
222
196
 
223
- ## 全局标记
197
+ 状态文件只表示“当前编译”:`bundle_build_started` 会立即清除上一次错误,
198
+ `bundling_error` 写入结构化错误,下一次完整编译成功或失败都会替换旧状态。
199
+ Expo CLI patch 也会为 HMR 增量更新写入 `hmr_update_started`、
200
+ `hmr_update_done` 状态,确保源码修复后通过 Fast Refresh 恢复时旧错误同步清除。
201
+ AI 进程直接读取这个文件即可,不需要额外的 HTTP 服务或 WebSocket 客户端。
224
202
 
225
- 注入脚本在 bundle 执行时(早于任何 React 代码)写入以下全局变量,可用于 E2E 测试断言:
203
+ 返回对象包含 `status`(`compiling`、`failed`、`succeeded`)和 `error`。失败时
204
+ `error` 至少包含 `type`、`message`、`filename`、`lineNumber`、`column`、
205
+ `errors` 和 `stack`(存在时)。
226
206
 
227
- | 变量 | 类型 | 说明 |
228
- |---|---|---|
229
- | `window.__DEVKIT_INJECTED__` | `true` | 注入脚本已执行(由 `withEntryInjection` 保证) |
230
- | `window.__DEVKIT_HMR_ENABLED__` | `boolean` | 当前 HMR 状态,随 postMessage 消息实时更新 |
207
+ 当前模板为 Expo CLI `55.0.16` 至 `55.0.22` 提供一个范围补丁(`>=55.0.16 <55.0.23`)。
208
+ 该范围以外的 CLI 版本不会静默套用补丁;升级时需要先检查发布产物,确认兼容后再调整
209
+ `pnpmfile.cjs` 中的范围和对应测试。
231
210
 
232
- ## 环境变量
211
+ 以下变量只服务于 sandbox 专项 E2E,不是应用或线上部署配置:
233
212
 
234
- | 变量 | 默认值 | 说明 |
213
+ | 变量 | 默认值 | 设置方与用途 |
235
214
  |---|---|---|
236
- | `SENTRY_OVERRIDE_DSN` | `https://stubPublicKey@o0.ingest.sentry.io/0` | 覆盖 Sentry DSN,可指向本地 relay 等自定义端点 |
237
- | `METRO_CACHE_DIR` | `projectRoot/.metro-cache` | Metro transform 缓存目录绝对路径,优先级最高,适合容器/CI 挂载外部持久目录 |
238
- | `METRO_TRANSFORM_LOG` | _(未设置时不记录)_ | 构建日志输出文件的绝对路径(JSONL 格式),仅开发模式(`__DEV__`)下生效 |
215
+ | `EXPO_OVERRIDE_METRO_CONFIG` | 无 | 线上由 vite-sandbox 自动设置;专项 E2E 由准备流程指向生成的 `node_modules/.cache/vite-sandbox/metro.config.cjs` |
216
+ | `EXPO_E2E_PORT` | `3001` | 仅在 Playwright 端口冲突时由本地或 CI 覆盖 |
217
+ | `EXPO_E2E_SANDBOX_PROJECT_ID` | `0` | 仅用于断言 Sentry envelope 使用 sandbox DSN 的 project id |
239
218
 
240
219
  ## 工作原理
241
220
 
@@ -316,12 +295,13 @@ metro.config.js
316
295
  │ └─ getUserMedia 不可用:降级透传 expo 原实现(不崩溃)
317
296
  │
318
297
  └─ withEntryInjection → resolver.resolveRequest(仅 __DEV__)
319
- └─ expo-router/entry-classic → dist/stubs/expo-router-entry-stub.js
320
- ├─ require('./entry-inject') ← 注入脚本(bundle 首部执行)
321
- │ ├─ globalThis.__DEVKIT_INJECTED__ = true
322
- │ ├─ setupHMRMessageControl() ← 安装 postMessage 监听器
323
- │ └─ console.log('[DevKit] Hello World')
324
- └─ require('expo-router/entry-classic') ← 原 expo-router 入口
298
+ └─ expo-router/entry-classic
299
+ ├─ Web:dist/stubs/expo-router-entry-stub.js
300
+ │ ├─ require('./entry-inject') ← Web 注入脚本
301
+ │ └─ require('expo-router/entry-classic') ← 原入口
302
+ └─ iOS/Android:dist/stubs/expo-router-entry-native-stub.js
303
+ ├─ require('./logbox-control') ← LogBox 通知屏蔽
304
+ └─ require('expo-router/entry-classic') ← 原入口
325
305
 
326
306
  setupHMRMessageControl()
327
307
  └─ window.addEventListener('message', handler)
@@ -334,18 +314,16 @@ setupHMRMessageControl()
334
314
 
335
315
  sentry-react-native-stub.js
336
316
  └─ init(options)
337
- └─ RealSentry.init({ ...options, dsn: OVERRIDE_DSN, beforeSend, beforeBreadcrumb })
338
- ├─ _capture.beforeSend(event, hint) ← 内置 SentryCapture(自动注入)
339
- │ ├─ MetroSymbolicator.symbolicate() → POST /symbolicate
340
- │ └─ onError({ message, frames, ... }) → console.warn(默认)
341
- └─ options.beforeSend?(event, hint) ← 调用方自定义(可选,串联执行)
317
+ ├─ Native: RealSentry.init(options)
318
+ ├─ 独立 Web: RealSentry.init({...options, dsn: FALLBACK_DSN})
319
+ └─ sandbox Web: 校验版本标记后返回,不重复初始化
342
320
  ```
343
321
 
344
322
  ## Package Exports
345
323
 
346
324
  | 子路径 | 文件 | 内容 |
347
325
  |---|---|---|
348
- | `.` | `dist/index.js` | `SentryCapture`、`MetroSymbolicator`、全部类型 |
326
+ | `.` | `dist/index.js` | 主入口(当前不导出运行时 API) |
349
327
  | `./metro` | `dist/metro.js` | `withDevkit`、`withDevStubs`、`withEntryInjection`、`withPersistentCache`、`withTransformLogger`、`withExpoNotificationsStub`、`withExpoMediaLibraryStub`、`withExpoCalendarStub` 等全部 Metro wrapper |
350
328
  | `./babel-plugin-jsx-source` | `dist/babel/plugin-jsx-source.js` | Babel 插件:为 JSX 注入 source 信息 |
351
329
  | `./babel-preset` | `dist/babel/preset.js` | Babel Preset:集成 jsx-source 和 lucide 插件 |
@@ -446,6 +424,16 @@ import rawSchema from '../schemas/app-config-schema-v56.json';
446
424
  - 安装 CSS 注入控制器
447
425
  - 安装路由消息控制器
448
426
 
427
+ ### logbox-control.js
428
+
429
+ 由 iOS/Android 的原生入口 stub 在业务代码前加载,调用 `LogBox.ignoreAllLogs(true)` 隐藏普通错误和警告
430
+ 通知。它不删除日志,也不阻止未捕获异常进入自定义错误处理链路。
431
+
432
+ ### expo-router-entry-native-stub.js
433
+
434
+ 仅用于 iOS/Android 的开发 Bundle。先加载 `logbox-control.js`,再加载原始
435
+ `expo-router/entry-classic`,确保所有接入 `withDevkit` 的应用共享同一套 LogBox 通知策略。
436
+
449
437
  ---
450
438
 
451
439
  ### expo-router-entry-stub.js
@@ -534,13 +522,13 @@ window.postMessage({ type: 'editor-refresh' }, '*') //
534
522
  `@sentry/react-native` 模块替换 stub,由 `withDevStubs()` 在 Metro 层自动注入。
535
523
 
536
524
  **功能:**
537
- - 将用户传入的 DSN 替换为无害的覆盖值(默认 `https://stubPublicKey@o0.ingest.sentry.io/0`)
538
- - 自动注入内置 SentryCapture,监听并符号化 Sentry 错误事件
539
- - 串联调用方传入的 `beforeSend` / `beforeBreadcrumb`(内置捕获器先执行)
540
- - 向父窗口发送 `GLOBAL_ERROR` 事件,携带错误信息
525
+ - Native 原样透传应用的 Sentry init
526
+ - 独立 Web 使用安全 fallback DSN
527
+ - sandbox Web 校验运行时版本标记并阻止重复 init
528
+ - 版本不一致时只向 console 输出诊断信息,不通知编辑器宿主
541
529
 
542
530
  **环境变量:**
543
- - `SENTRY_OVERRIDE_DSN`:自定义覆盖 DSN(如指向本地 relay)
531
+ - 应用开发者不需要新增环境变量;`INJECT_SENTRY_DSN` 由 vite-sandbox 启动链路自动处理
544
532
 
545
533
  ---
546
534
 
@@ -853,3 +841,59 @@ export { Star, BookOpen as ReadIcon } from "lucide-react-native";
853
841
  ### 版本兼容性
854
842
 
855
843
  插件在运行时探测 `lucide-react-native` 的实际目录结构,自动适配新旧版本的路径差异(`>= 1.9` 新增 `icons/` 子目录;旧版 `1.8.x` 图标直接位于 `dist/cjs/`),lucide 升级时无需修改配置。
844
+
845
+ ---
846
+
847
+ ## pnpm 配置依赖与兼容补丁
848
+
849
+ DevKit 可以作为 pnpm `configDependency`,在普通依赖解析前集中注入经过验证的 Expo/React Native 兼容补丁。要求 pnpm `>=10.32.0`。
850
+
851
+ 发布新版 DevKit 后,在应用工作区根目录执行:
852
+
853
+ ```bash
854
+ pnpm add --config --registry=https://registry.npmjs.org/ miaoda-expo-devkit@<version>
855
+ ```
856
+
857
+ 由于现有包名不属于 pnpm 的自动插件命名空间,还需要在 `pnpm-workspace.yaml` 中指定 hook:
858
+
859
+ ```yaml
860
+ pnpmfile:
861
+ - node_modules/.pnpm-config/miaoda-expo-devkit/pnpmfile.cjs
862
+ ```
863
+
864
+ 本仓库的 `scripts/build.sh` 会自动把生成 Template 的 `pnpmfile` 改为上述 config dependency 路径,并写入精确版本和完整性校验;无需在生成产物中维护额外的 `.pnpmfile.cjs`。
865
+
866
+ DevKit 当前管理以下补丁:
867
+
868
+ - `@shopify/react-native-skia@2.4.18`
869
+ - `expo@55.0.6`(固定 `expo-audio@55.0.6` 和 `expo-video@55.0.5` 的 Expo 兼容版本表)
870
+ - `expo-modules-core@55.0.15`
871
+ - `react-native-css-interop@0.2.6`
872
+ - `react-native-reanimated@4.2.1`
873
+
874
+ 迁移历史项目时,hook 会计算同名本地 patch 的 SHA-256:已知模板 patch 会自动切换到 DevKit 内置文件;内容不同的项目定制 patch 会继续优先,并输出警告。项目中其他 `patchedDependencies` 不受影响。迁移后必须重新生成并提交 `pnpm-lock.yaml`。
875
+
876
+ 发布前可用一条命令运行轻量集成测试:
877
+
878
+ ```bash
879
+ pnpm --dir packages/devkit test:patches
880
+ ```
881
+
882
+ 该命令会自动构建并打包 DevKit,在临时项目中模拟 pnpm 安装 Config Dependency 后的目录布局,安装受支持版本范围内的目标依赖,检查 10 个托管补丁及 lockfile,然后执行 frozen-lockfile 复装,并运行真实 Metro 文件监听、HTTP 控制接口和 `/hot` WebSocket 回归,最后清理临时目录。它不会运行 Expo Prebuild、Gradle 或 Android 编译。
883
+
884
+ ### 本地编辑 pnpm 补丁
885
+
886
+ 不要直接修改 `pnpm-patches/*.patch`。需要调整受管依赖时,使用被 Git 忽略的本地工作区:
887
+
888
+ ```bash
889
+ pnpm --dir packages/devkit patch:workspace init expo@55.0.6
890
+ # 编辑 packages/devkit/.patch-work/.../source 中的正常源码
891
+ pnpm --dir packages/devkit patch:workspace generate expo@55.0.6
892
+ ```
893
+
894
+ `init` 会从 npm 下载未修改的对应版本,应用当前 DevKit 补丁,并保留未打补丁基线;
895
+ `generate` 根据两棵源码树生成新的 `pnpm-patches/*.patch`,同时更新补丁哈希测试和
896
+ 历史哈希迁移记录。`.patch-work/` 只存在于本地,不纳入 Git,也不会进入发布包。
897
+
898
+ 修改完成后仍需运行 `pnpm --dir packages/devkit test`,提交前再运行
899
+ `pnpm --dir packages/devkit test:patches` 验证补丁能在真实依赖安装中应用。