miaoda-expo-devkit 0.1.1-dev.development-build.4 → 0.1.1-dev.development-build.5
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.
- package/README.md +3 -1
- package/package.json +1 -1
- package/pnpm-patches/expo@55.0.6.patch +164 -14
- package/pnpmfile.cjs +1 -0
package/README.md
CHANGED
|
@@ -180,7 +180,9 @@ Web 注入内容包括:
|
|
|
180
180
|
Bundle,避免使用失效的增量 revision。最终编译失败会返回 `422` 并继续保持暂停。
|
|
181
181
|
|
|
182
182
|
普通恢复执行 Fast Refresh;`/_hmr/reload` 执行完整 Bundle 重载,若当前暂停则延迟到
|
|
183
|
-
|
|
183
|
+
恢复时执行。暂停期间 iOS/Android 客户端展示全屏「应用正在更新中」遮罩,阻断用户
|
|
184
|
+
对旧界面的交互,恢复后自动消失(Web 端不展示)。过渡期内旧路径
|
|
185
|
+
`/innerapi/v1/sourcecode/__hmr_*` 作为别名同样可用,
|
|
184
186
|
供平台网关 sourcecode 透传通道不改路由接入。协议、返回值、版本要求和加载边界见
|
|
185
187
|
[Metro HMR 批量编辑控制](../../docs/metro-hmr-control.md)。
|
|
186
188
|
|
package/package.json
CHANGED
|
@@ -21,7 +21,7 @@ index e2b8941..355deae 100644
|
|
|
21
21
|
"jest-expo": "~55.0.9",
|
|
22
22
|
"lottie-react-native": "~7.3.4",
|
|
23
23
|
diff --git a/src/async-require/hmr.ts b/src/async-require/hmr.ts
|
|
24
|
-
index c0ea71e..
|
|
24
|
+
index c0ea71e..9250e76 100644
|
|
25
25
|
--- a/src/async-require/hmr.ts
|
|
26
26
|
+++ b/src/async-require/hmr.ts
|
|
27
27
|
@@ -17,6 +17,7 @@ import {
|
|
@@ -32,7 +32,7 @@ index c0ea71e..be9cb40 100644
|
|
|
32
32
|
showLoading,
|
|
33
33
|
} from './hmrUtils';
|
|
34
34
|
|
|
35
|
-
@@ -29,6 +30,
|
|
35
|
+
@@ -29,6 +30,66 @@ let hmrClient: MetroHMRClient | null = null;
|
|
36
36
|
let hmrUnavailableReason: string | null = null;
|
|
37
37
|
const buildErrorQueue = new Set<unknown>();
|
|
38
38
|
let didConnect: boolean = false;
|
|
@@ -40,6 +40,34 @@ index c0ea71e..be9cb40 100644
|
|
|
40
40
|
+// miaodaPaused 是 Agent 的编辑闸门,与用户自己的 Fast Refresh 开关
|
|
41
41
|
+// (fastRefreshRequested)相互独立:放行后只有用户开着 Fast Refresh 才恢复更新。
|
|
42
42
|
+let miaodaPaused = false;
|
|
43
|
+
+// 暂停状态订阅者:launch/MiaodaHmrOverlay 的全屏遮罩据此在 Agent 批量编辑
|
|
44
|
+
+// 期间盖住应用、阻断交互。遮罩挂在 dev 模式的根组件包装里,纯 JS 实现。
|
|
45
|
+
+const miaodaPausedListeners = new Set<(paused: boolean) => void>();
|
|
46
|
+
+
|
|
47
|
+
+// miaodaPaused 的唯一写入入口:值真正变化时才广播,订阅者异常不能
|
|
48
|
+
+// 反过来打断 HMR 控制链路(ack、reload 等时序敏感操作)。
|
|
49
|
+
+function setMiaodaPaused(paused: boolean) {
|
|
50
|
+
+ if (miaodaPaused === paused) return;
|
|
51
|
+
+ miaodaPaused = paused;
|
|
52
|
+
+ for (const listener of miaodaPausedListeners) {
|
|
53
|
+
+ try {
|
|
54
|
+
+ listener(paused);
|
|
55
|
+
+ } catch {}
|
|
56
|
+
+ }
|
|
57
|
+
+}
|
|
58
|
+
+
|
|
59
|
+
+// 订阅暂停状态变化,返回取消订阅函数(供 React useEffect 使用)。
|
|
60
|
+
+export function subscribeMiaodaPaused(listener: (paused: boolean) => void): () => void {
|
|
61
|
+
+ miaodaPausedListeners.add(listener);
|
|
62
|
+
+ return () => {
|
|
63
|
+
+ miaodaPausedListeners.delete(listener);
|
|
64
|
+
+ };
|
|
65
|
+
+}
|
|
66
|
+
+
|
|
67
|
+
+// 遮罩挂载时读取当前值,避免错过订阅注册之前发生的迁移。
|
|
68
|
+
+export function isMiaodaPaused(): boolean {
|
|
69
|
+
+ return miaodaPaused;
|
|
70
|
+
+}
|
|
43
71
|
+let fastRefreshRequested = false;
|
|
44
72
|
+// 断线兜底轮询间隔。控制消息走 /hot WebSocket,轮询只用于 socket 断开期间的
|
|
45
73
|
+// 状态同步,不需要很频繁。
|
|
@@ -71,7 +99,7 @@ index c0ea71e..be9cb40 100644
|
|
|
71
99
|
const pendingLogs: [LogLevel, any[]][] = [];
|
|
72
100
|
|
|
73
101
|
type LogLevel =
|
|
74
|
-
@@ -42,6 +
|
|
102
|
+
@@ -42,6 +103,102 @@ type LogLevel =
|
|
75
103
|
| 'groupEnd'
|
|
76
104
|
| 'debug';
|
|
77
105
|
|
|
@@ -140,7 +168,7 @@ index c0ea71e..be9cb40 100644
|
|
|
140
168
|
+ if (miaodaSessionBaseVersion === null && version !== null) miaodaSessionBaseVersion = version;
|
|
141
169
|
+ if (state.paused) {
|
|
142
170
|
+ miaodaObservedPaused = true;
|
|
143
|
-
+
|
|
171
|
+
+ setMiaodaPaused(true);
|
|
144
172
|
+ hmrClient?.disable();
|
|
145
173
|
+ // 把观察到的这次暂停作为本地基线版本。之后若出现没有对应 WebSocket
|
|
146
174
|
+ // 控制消息的新版本,说明错过了状态迁移,必须完整 Bundle reload。
|
|
@@ -155,7 +183,7 @@ index c0ea71e..be9cb40 100644
|
|
|
155
183
|
+ const missedReload = version !== null && miaodaSessionBaseVersion !== null && version > miaodaSessionBaseVersion && state.lastMode === 'reload';
|
|
156
184
|
+ const shouldReload = missedReload || (missedControl && (miaodaObservedPaused || miaodaWebSocketDisconnected));
|
|
157
185
|
+ miaodaObservedPaused = false;
|
|
158
|
-
+
|
|
186
|
+
+ setMiaodaPaused(false);
|
|
159
187
|
+ miaodaLastControlVersion = version;
|
|
160
188
|
+ if (shouldReload) {
|
|
161
189
|
+ reload();
|
|
@@ -174,7 +202,7 @@ index c0ea71e..be9cb40 100644
|
|
|
174
202
|
function assert(foo: any, msg: string): asserts foo {
|
|
175
203
|
if (!foo) throw new Error(msg);
|
|
176
204
|
}
|
|
177
|
-
@@ -52,6 +
|
|
205
|
+
@@ -52,6 +209,9 @@ function assert(foo: any, msg: string): asserts foo {
|
|
178
206
|
*/
|
|
179
207
|
const HMRClient = {
|
|
180
208
|
enable() {
|
|
@@ -184,7 +212,7 @@ index c0ea71e..be9cb40 100644
|
|
|
184
212
|
if (hmrUnavailableReason !== null) {
|
|
185
213
|
// If HMR became unavailable while you weren't using it,
|
|
186
214
|
// explain why when you try to turn it on.
|
|
187
|
-
@@ -87,6 +
|
|
215
|
+
@@ -87,6 +247,7 @@ const HMRClient = {
|
|
188
216
|
},
|
|
189
217
|
|
|
190
218
|
disable() {
|
|
@@ -192,7 +220,7 @@ index c0ea71e..be9cb40 100644
|
|
|
192
220
|
assert(hmrClient, 'Expected HMRClient.setup() call at startup.');
|
|
193
221
|
hmrClient.disable();
|
|
194
222
|
},
|
|
195
|
-
@@ -180,6 +
|
|
223
|
+
@@ -180,6 +341,13 @@ const HMRClient = {
|
|
196
224
|
const origin = `${serverScheme}://${serverHost}`;
|
|
197
225
|
const client = new MetroHMRClient(`${origin}/hot`);
|
|
198
226
|
hmrClient = client;
|
|
@@ -206,7 +234,7 @@ index c0ea71e..be9cb40 100644
|
|
|
206
234
|
|
|
207
235
|
pendingEntryPoints.push(
|
|
208
236
|
// HMRServer understands regular bundle URLs, so prefer that in case
|
|
209
|
-
@@ -193,6 +
|
|
237
|
+
@@ -193,6 +361,55 @@ const HMRClient = {
|
|
210
238
|
})
|
|
211
239
|
);
|
|
212
240
|
|
|
@@ -233,7 +261,7 @@ index c0ea71e..be9cb40 100644
|
|
|
233
261
|
+ case 'pause':
|
|
234
262
|
+ // 立即闸住 HMR 并隐藏刷新提示,然后回 ack——服务端要等所有客户端
|
|
235
263
|
+ // ack 后才认为暂停完成,ack 之前 Agent 不会开始写文件。
|
|
236
|
-
+
|
|
264
|
+
+ setMiaodaPaused(true);
|
|
237
265
|
+ client.disable();
|
|
238
266
|
+ hideLoading();
|
|
239
267
|
+ client.send(JSON.stringify({type: 'miaoda-hmr-ack', version: 1, id: message.id}));
|
|
@@ -241,12 +269,12 @@ index c0ea71e..be9cb40 100644
|
|
|
241
269
|
+ case 'resume':
|
|
242
270
|
+ // 解除 Agent 闸门;用户自己的 Fast Refresh 开关保持独立,
|
|
243
271
|
+ // 只有它开着才真正恢复更新。
|
|
244
|
-
+
|
|
272
|
+
+ setMiaodaPaused(false);
|
|
245
273
|
+ if (fastRefreshRequested) HMRClient.enable();
|
|
246
274
|
+ break;
|
|
247
275
|
+ case 'reload':
|
|
248
276
|
+ // 完整加载前也保持闸门,避免 reload 完成前又有增量进来。
|
|
249
|
-
+
|
|
277
|
+
+ setMiaodaPaused(true);
|
|
250
278
|
+ client.disable();
|
|
251
279
|
+ reload();
|
|
252
280
|
+ break;
|
|
@@ -262,7 +290,7 @@ index c0ea71e..be9cb40 100644
|
|
|
262
290
|
client.on('connection-error', (e: Error) => {
|
|
263
291
|
setHMRUnavailableReason(getConnectionError(serverHost, e));
|
|
264
292
|
});
|
|
265
|
-
@@ -234,6 +
|
|
293
|
+
@@ -234,6 +451,10 @@ const HMRClient = {
|
|
266
294
|
client.on('error', (data) => this._onMetroError(data));
|
|
267
295
|
|
|
268
296
|
client.on('close', (closeEvent?: { code: number; reason: string }) => {
|
|
@@ -273,7 +301,7 @@ index c0ea71e..be9cb40 100644
|
|
|
273
301
|
hideLoading();
|
|
274
302
|
const reason = closeEvent?.reason;
|
|
275
303
|
const code = closeEvent?.code || 1000;
|
|
276
|
-
@@ -326,6 +
|
|
304
|
+
@@ -326,6 +547,9 @@ function registerBundleEntryPoints(client: MetroHMRClient | null) {
|
|
277
305
|
client?.send(
|
|
278
306
|
JSON.stringify({
|
|
279
307
|
type: 'register-entrypoints',
|
|
@@ -283,3 +311,125 @@ index c0ea71e..be9cb40 100644
|
|
|
283
311
|
entryPoints: pendingEntryPoints,
|
|
284
312
|
})
|
|
285
313
|
);
|
|
314
|
+
diff --git a/src/launch/MiaodaHmrOverlay.tsx b/src/launch/MiaodaHmrOverlay.tsx
|
|
315
|
+
new file mode 100644
|
|
316
|
+
index 0000000..2db61b9
|
|
317
|
+
--- /dev/null
|
|
318
|
+
+++ b/src/launch/MiaodaHmrOverlay.tsx
|
|
319
|
+
@@ -0,0 +1,58 @@
|
|
320
|
+
+import * as React from 'react';
|
|
321
|
+
+import { ActivityIndicator, StyleSheet, Text, View } from 'react-native';
|
|
322
|
+
+
|
|
323
|
+
+import { isMiaodaPaused, subscribeMiaodaPaused } from '../async-require/hmr';
|
|
324
|
+
+
|
|
325
|
+
+// Miaoda HMR 批量编辑期间的「更新中」全屏遮罩。
|
|
326
|
+
+//
|
|
327
|
+
+// Agent 通过 `/_hmr/off` 暂停 HMR 后会连续改写工程文件,这期间设备仍在运行
|
|
328
|
+
+// 上一份完好的 JS,但用户看到的是一个随时会被替换掉的界面,继续操作会产生
|
|
329
|
+
+// 基于旧代码的困惑交互。遮罩在暂停期间盖住整个应用并吃掉所有触摸,
|
|
330
|
+
+// resume(或 reload 导致的整树卸载)后自动消失。
|
|
331
|
+
+//
|
|
332
|
+
+// 状态源是 async-require/hmr 里的 miaodaPaused 广播:WebSocket 控制帧到达时
|
|
333
|
+
+// 同步触发,2 秒轮询兜底路径同样走该广播,因此两种通道下遮罩行为一致。
|
|
334
|
+
+// 纯 JS 实现,挂在 withDevTools 里,只在开发模式出现,不需要原生改动。
|
|
335
|
+
+export default function MiaodaHmrOverlay(): React.ReactElement | null {
|
|
336
|
+
+ // 挂载时用当前值初始化,避免错过订阅注册之前就已经开始的暂停。
|
|
337
|
+
+ const [paused, setPaused] = React.useState(isMiaodaPaused);
|
|
338
|
+
+
|
|
339
|
+
+ React.useEffect(() => subscribeMiaodaPaused(setPaused), []);
|
|
340
|
+
+
|
|
341
|
+
+ if (!paused) return null;
|
|
342
|
+
+
|
|
343
|
+
+ // absoluteFill + 默认的 pointerEvents="auto":作为根组件的最后一个兄弟节点
|
|
344
|
+
+ // 渲染在用户界面之上,拦截全部触摸,直到暂停解除。
|
|
345
|
+
+ return (
|
|
346
|
+
+ <View style={styles.container}>
|
|
347
|
+
+ <ActivityIndicator color="#ffffff" size="large" />
|
|
348
|
+
+ <Text style={styles.title}>应用正在更新中</Text>
|
|
349
|
+
+ <Text style={styles.subtitle}>暂时无法使用,更新完成后将自动恢复</Text>
|
|
350
|
+
+ </View>
|
|
351
|
+
+ );
|
|
352
|
+
+}
|
|
353
|
+
+
|
|
354
|
+
+const styles = StyleSheet.create({
|
|
355
|
+
+ container: {
|
|
356
|
+
+ // 不展开 StyleSheet.absoluteFillObject(RN 新版本中已标记废弃),直接写全。
|
|
357
|
+
+ position: 'absolute',
|
|
358
|
+
+ top: 0,
|
|
359
|
+
+ left: 0,
|
|
360
|
+
+ right: 0,
|
|
361
|
+
+ bottom: 0,
|
|
362
|
+
+ backgroundColor: 'rgba(0, 0, 0, 0.65)',
|
|
363
|
+
+ alignItems: 'center',
|
|
364
|
+
+ justifyContent: 'center',
|
|
365
|
+
+ },
|
|
366
|
+
+ title: {
|
|
367
|
+
+ color: '#ffffff',
|
|
368
|
+
+ fontSize: 17,
|
|
369
|
+
+ fontWeight: '600',
|
|
370
|
+
+ marginTop: 16,
|
|
371
|
+
+ },
|
|
372
|
+
+ subtitle: {
|
|
373
|
+
+ color: 'rgba(255, 255, 255, 0.8)',
|
|
374
|
+
+ fontSize: 13,
|
|
375
|
+
+ marginTop: 8,
|
|
376
|
+
+ },
|
|
377
|
+
+});
|
|
378
|
+
diff --git a/src/launch/withDevTools.ios.tsx b/src/launch/withDevTools.ios.tsx
|
|
379
|
+
index f1f686c..0029bfa 100644
|
|
380
|
+
--- a/src/launch/withDevTools.ios.tsx
|
|
381
|
+
+++ b/src/launch/withDevTools.ios.tsx
|
|
382
|
+
@@ -3,6 +3,8 @@ import * as React from 'react';
|
|
383
|
+
import DevLoadingView from '../environment/DevLoadingView';
|
|
384
|
+
import { isRunningInExpoGo } from '../environment/ExpoGo';
|
|
385
|
+
|
|
386
|
+
+import MiaodaHmrOverlay from './MiaodaHmrOverlay';
|
|
387
|
+
+
|
|
388
|
+
/**
|
|
389
|
+
* Append the Expo Fast Refresh view and optionally
|
|
390
|
+
* keep the screen awake if `expo-keep-awake` is installed.
|
|
391
|
+
@@ -31,11 +33,17 @@ export function withDevTools<TComponent extends React.ComponentType<any>>(
|
|
392
|
+
<>
|
|
393
|
+
<AppRootComponent {...props} />
|
|
394
|
+
<DevLoadingView />
|
|
395
|
+
+ <MiaodaHmrOverlay />
|
|
396
|
+
</>
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
- return <AppRootComponent {...props} />;
|
|
401
|
+
+ return (
|
|
402
|
+
+ <>
|
|
403
|
+
+ <AppRootComponent {...props} />
|
|
404
|
+
+ <MiaodaHmrOverlay />
|
|
405
|
+
+ </>
|
|
406
|
+
+ );
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
410
|
+
diff --git a/src/launch/withDevTools.tsx b/src/launch/withDevTools.tsx
|
|
411
|
+
index 5a98062..6a6c3b7 100644
|
|
412
|
+
--- a/src/launch/withDevTools.tsx
|
|
413
|
+
+++ b/src/launch/withDevTools.tsx
|
|
414
|
+
@@ -1,5 +1,7 @@
|
|
415
|
+
import * as React from 'react';
|
|
416
|
+
|
|
417
|
+
+import MiaodaHmrOverlay from './MiaodaHmrOverlay';
|
|
418
|
+
+
|
|
419
|
+
// Keep the screen awake on Android, we don't use the Fast Refresh overlay here.
|
|
420
|
+
// The default behavior here is to skip the custom Fast Refresh indicator.
|
|
421
|
+
export function withDevTools<TComponent extends React.ComponentType<any>>(
|
|
422
|
+
@@ -18,7 +20,12 @@ export function withDevTools<TComponent extends React.ComponentType<any>>(
|
|
423
|
+
|
|
424
|
+
function WithDevTools(props: React.ComponentProps<TComponent>) {
|
|
425
|
+
useOptionalKeepAwake();
|
|
426
|
+
- return <AppRootComponent {...props} />;
|
|
427
|
+
+ return (
|
|
428
|
+
+ <>
|
|
429
|
+
+ <AppRootComponent {...props} />
|
|
430
|
+
+ <MiaodaHmrOverlay />
|
|
431
|
+
+ </>
|
|
432
|
+
+ );
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (process.env.NODE_ENV !== 'production') {
|
package/pnpmfile.cjs
CHANGED
|
@@ -51,6 +51,7 @@ const managedPatches = {
|
|
|
51
51
|
'expo@55.0.6': {
|
|
52
52
|
filename: 'expo@55.0.6.patch',
|
|
53
53
|
legacyHashes: new Set([
|
|
54
|
+
'3eca6fa71eb9640bccf2075dc37c5c8ed2fa060db77b36bb2c67fba902a9a9c5',
|
|
54
55
|
'1df9b522728e93daf64f9e2298ab835ce52d55b4aa2de92a68b947e7ed08f40a',
|
|
55
56
|
'8ac63998a9852ddafda7af96deb4d7ac4c3a5da5e65e865af32be9d4fd966e02',
|
|
56
57
|
'12efbb4c64bc67744a838cc5d9d88604ad2a99cdd578a5cfab31b8d1c2be1144',
|