miaoda-expo-devkit 0.1.1-dev.development-build.1 → 0.1.1-dev.development-build.3
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 +18 -10
- package/dist/babel/plugin-lucide-react-native.js +1 -1
- package/dist/rules/no-bare-web-crypto.js +235 -0
- package/dist/rules/no-expo-file-system-compat.js +2130 -0
- package/dist/rules/no-lucide-star-import.js +78 -0
- package/dist/rules/no-sentry-default-import.js +83 -0
- package/dist/rules/no-typeof-window-check.js +85 -0
- package/oxlint-config.json +11 -1
- package/package.json +6 -1
- package/pnpm-patches/@expo__cli@55.0.16-55.0.22.patch +237 -150
- package/pnpm-patches/expo@55.0.6.patch +81 -31
- package/pnpm-patches/metro-file-map@0.83.3.patch +7 -3
- package/pnpm-patches/metro-runtime@0.83.3.patch +5 -1
- package/pnpm-patches/react-native-worklets@0.7.4.patch +91 -0
- package/pnpmfile.cjs +87 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
diff --git a/bundledNativeModules.json b/bundledNativeModules.json
|
|
2
|
-
index
|
|
2
|
+
index e2b8941..355deae 100644
|
|
3
3
|
--- a/bundledNativeModules.json
|
|
4
4
|
+++ b/bundledNativeModules.json
|
|
5
5
|
@@ -19,7 +19,7 @@
|
|
@@ -21,9 +21,10 @@ index e2b8941923b274b1c5c9bda2269d28cf574aff7f..355deae878b654e1e1d9db114ee88c1e
|
|
|
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..be9cb40 100644
|
|
24
25
|
--- a/src/async-require/hmr.ts
|
|
25
26
|
+++ b/src/async-require/hmr.ts
|
|
26
|
-
@@ -17,6 +17,7 @@
|
|
27
|
+
@@ -17,6 +17,7 @@ import {
|
|
27
28
|
handleCompileError,
|
|
28
29
|
hideLoading,
|
|
29
30
|
resetErrorOverlay,
|
|
@@ -31,32 +32,52 @@ diff --git a/src/async-require/hmr.ts b/src/async-require/hmr.ts
|
|
|
31
32
|
showLoading,
|
|
32
33
|
} from './hmrUtils';
|
|
33
34
|
|
|
34
|
-
@@ -29,6 +30,
|
|
35
|
+
@@ -29,6 +30,38 @@ let hmrClient: MetroHMRClient | null = null;
|
|
35
36
|
let hmrUnavailableReason: string | null = null;
|
|
36
37
|
const buildErrorQueue = new Set<unknown>();
|
|
37
38
|
let didConnect: boolean = false;
|
|
38
|
-
+//
|
|
39
|
+
+// ---- Miaoda HMR 批量编辑控制(客户端侧)----
|
|
40
|
+
+// miaodaPaused 是 Agent 的编辑闸门,与用户自己的 Fast Refresh 开关
|
|
41
|
+
+// (fastRefreshRequested)相互独立:放行后只有用户开着 Fast Refresh 才恢复更新。
|
|
39
42
|
+let miaodaPaused = false;
|
|
40
43
|
+let fastRefreshRequested = false;
|
|
44
|
+
+// 断线兜底轮询间隔。控制消息走 /hot WebSocket,轮询只用于 socket 断开期间的
|
|
45
|
+
+// 状态同步,不需要很频繁。
|
|
41
46
|
+const MIAODA_HMR_POLL_INTERVAL_MS = 2000;
|
|
42
47
|
+type MiaodaStatusProtocol = 'http' | 'https';
|
|
43
48
|
+let miaodaStatusHost: string | null = null;
|
|
49
|
+
+// 首次探测使用的协议,取 bundle URL 的 scheme(设备怎么加载 bundle 就先试什么协议)。
|
|
44
50
|
+let miaodaStatusInitialProtocol: MiaodaStatusProtocol = 'http';
|
|
51
|
+
+// 自适应成功后记住的协议;null 表示还没有成功过,每轮都先试初始协议。
|
|
45
52
|
+let miaodaStatusProtocol: MiaodaStatusProtocol | null = null;
|
|
53
|
+
+// 收到明确 404(服务端未启用控制路由)后停止轮询;WebSocket 重连时重置,
|
|
54
|
+
+// 因为重连对面的 Metro 可能换了一个开启了控制的进程。
|
|
46
55
|
+let miaodaStatusPollingStopped = false;
|
|
47
56
|
+let miaodaPollTimer: ReturnType<typeof setTimeout> | null = null;
|
|
57
|
+
+// 轮询最近一次见到的服务端状态版本。
|
|
48
58
|
+let miaodaStateVersion: number | null = null;
|
|
59
|
+
+// 已通过 WebSocket 控制消息同步到的版本。轮询发现比它新的版本,说明 socket
|
|
60
|
+
+// 断开期间错过了控制消息,增量 revision 的连续性无法证明,必须完整 reload。
|
|
49
61
|
+let miaodaLastControlVersion: number | null = null;
|
|
62
|
+
+// 本次 JS 会话首次观察到的服务端版本(WebSocket 或轮询,谁先到用谁),此后
|
|
63
|
+
+// 不再更新。新会话的 bundle 刚从服务器取回,内容本身就对应该时刻的服务端
|
|
64
|
+
+// 状态,因此它是「当前运行代码已知完好」的锚点:之后版本号超过它且
|
|
65
|
+
+// lastMode 为 reload,才说明本会话错过了一次必须完整加载的迁移。
|
|
66
|
+
+let miaodaSessionBaseVersion: number | null = null;
|
|
67
|
+
+// 轮询曾观察到 paused=true:之后的版本变化即使 lastMode 不是 reload
|
|
68
|
+
+// 也要按 reload 处理(暂停期间错过的批量编辑不能靠增量补齐)。
|
|
50
69
|
+let miaodaObservedPaused = false;
|
|
51
70
|
+let miaodaWebSocketDisconnected = false;
|
|
52
71
|
const pendingLogs: [LogLevel, any[]][] = [];
|
|
53
72
|
|
|
54
73
|
type LogLevel =
|
|
55
|
-
@@ -42,6 +
|
|
74
|
+
@@ -42,6 +75,102 @@ type LogLevel =
|
|
56
75
|
| 'groupEnd'
|
|
57
76
|
| 'debug';
|
|
58
77
|
|
|
59
78
|
+function scheduleMiaodaStatusPoll() {
|
|
79
|
+
+ // Web 端不轮询(跨域响应会被浏览器拦截,且没有必要);无 host、已 404 停止、
|
|
80
|
+
+ // 或已有定时器在跑时都直接跳过。unref 避免定时器阻止进程退出(如 Jest 环境)。
|
|
60
81
|
+ if (process.env.EXPO_OS === 'web' || !miaodaStatusHost || miaodaStatusPollingStopped || miaodaPollTimer != null) return;
|
|
61
82
|
+ miaodaPollTimer = setTimeout(() => {
|
|
62
83
|
+ miaodaPollTimer = null;
|
|
@@ -66,7 +87,9 @@ diff --git a/src/async-require/hmr.ts b/src/async-require/hmr.ts
|
|
|
66
87
|
+}
|
|
67
88
|
+
|
|
68
89
|
+function miaodaStatusUrl(protocol: MiaodaStatusProtocol) {
|
|
69
|
-
+
|
|
90
|
+
+ // 控制接口直接挂在 Metro 根路径(/_hmr/*):端侧裸轮询请求不携带平台凭证,
|
|
91
|
+
+ // 带内部 API 前缀的路径可能被路由层权限拦截。
|
|
92
|
+
+ return `${protocol}://${miaodaStatusHost}/_hmr/status`;
|
|
70
93
|
+}
|
|
71
94
|
+
|
|
72
95
|
+function alternateMiaodaStatusProtocol(protocol: MiaodaStatusProtocol): MiaodaStatusProtocol {
|
|
@@ -81,15 +104,17 @@ diff --git a/src/async-require/hmr.ts b/src/async-require/hmr.ts
|
|
|
81
104
|
+ try {
|
|
82
105
|
+ response = await fetch(miaodaStatusUrl(protocol), {headers: {'Cache-Control': 'no-cache'}});
|
|
83
106
|
+ } catch {
|
|
84
|
-
+ //
|
|
85
|
-
+ //
|
|
107
|
+
+ // 协议自适应:部分开发环境的 HTTPS 证书无效、设备会拒绝,需要降级 HTTP;
|
|
108
|
+
+ // 也有环境只暴露 HTTPS。只有网络/证书类失败(fetch reject)才换协议重试,
|
|
109
|
+
+ // HTTP 状态码不算——那是服务器给出的确定答复。
|
|
86
110
|
+ protocol = alternateMiaodaStatusProtocol(protocol);
|
|
87
111
|
+ response = await fetch(miaodaStatusUrl(protocol), {headers: {'Cache-Control': 'no-cache'}});
|
|
88
112
|
+ }
|
|
89
113
|
+
|
|
90
114
|
+ if (response.status === 404) {
|
|
91
|
-
+ //
|
|
92
|
-
+ //
|
|
115
|
+
+ // 404 是确定性信号:这个 Metro 实例没有挂载控制路由(未开启
|
|
116
|
+
+ // EXPO_MIAODA_HMR_CONTROL)。停止探测直到 WebSocket 重连,
|
|
117
|
+
+ // 避免对普通开发服务器永久空轮询。注意不要在 404 时换协议重试。
|
|
93
118
|
+ miaodaStatusPollingStopped = true;
|
|
94
119
|
+ if (miaodaPollTimer != null) {
|
|
95
120
|
+ clearTimeout(miaodaPollTimer);
|
|
@@ -98,28 +123,37 @@ diff --git a/src/async-require/hmr.ts b/src/async-require/hmr.ts
|
|
|
98
123
|
+ return;
|
|
99
124
|
+ }
|
|
100
125
|
+ if (!response.ok) {
|
|
101
|
-
+ // 5xx
|
|
126
|
+
+ // 5xx 等暂时性 HTTP 失败不停止轮询,下一轮继续重试。
|
|
102
127
|
+ return;
|
|
103
128
|
+ }
|
|
104
129
|
+
|
|
105
|
-
+ //
|
|
130
|
+
+ // 记住本次到达服务器的协议,后续轮询固定使用它。
|
|
106
131
|
+ miaodaStatusProtocol = protocol;
|
|
107
132
|
+ const state = (await response.json()) as {
|
|
108
133
|
+ paused?: boolean; reloadPending?: boolean; stateVersion?: number; lastMode?: string | null;
|
|
109
134
|
+ };
|
|
110
|
-
+
|
|
135
|
+
+ // Number.isInteger 不是 TS 类型守卫,先收窄 number 再判整数,
|
|
136
|
+
+ // 使 version 的类型收敛为 number | null。
|
|
137
|
+
+ const version = typeof state.stateVersion === 'number' && Number.isInteger(state.stateVersion) ? state.stateVersion : null;
|
|
111
138
|
+ const firstObservation = miaodaStateVersion === null;
|
|
112
139
|
+ miaodaStateVersion = version;
|
|
140
|
+
+ if (miaodaSessionBaseVersion === null && version !== null) miaodaSessionBaseVersion = version;
|
|
113
141
|
+ if (state.paused) {
|
|
114
142
|
+ miaodaObservedPaused = true;
|
|
115
143
|
+ miaodaPaused = true;
|
|
116
144
|
+ hmrClient?.disable();
|
|
117
|
-
+ //
|
|
118
|
-
+ //
|
|
145
|
+
+ // 把观察到的这次暂停作为本地基线版本。之后若出现没有对应 WebSocket
|
|
146
|
+
+ // 控制消息的新版本,说明错过了状态迁移,必须完整 Bundle reload。
|
|
119
147
|
+ miaodaLastControlVersion = version;
|
|
120
148
|
+ } else {
|
|
149
|
+
+ // 错过控制消息且(曾处于暂停或 socket 断连中)→ 增量连续性无法证明,
|
|
150
|
+
+ // 只能 reload。lastMode === 'reload' 则必须再满足「版本号相对本次会话
|
|
151
|
+
+ // 基线前进过」才视为未执行的 reload 指令:服务端会把 lastMode 持久化并
|
|
152
|
+
+ // 一直原样返回,若不做版本门控,已经执行过 reload 的客户端重连后会反复
|
|
153
|
+
+ // 执行同一条指令,陷入无限重载循环。
|
|
121
154
|
+ const missedControl = !firstObservation && version !== null && version !== miaodaLastControlVersion;
|
|
122
|
-
+ const
|
|
155
|
+
+ const missedReload = version !== null && miaodaSessionBaseVersion !== null && version > miaodaSessionBaseVersion && state.lastMode === 'reload';
|
|
156
|
+
+ const shouldReload = missedReload || (missedControl && (miaodaObservedPaused || miaodaWebSocketDisconnected));
|
|
123
157
|
+ miaodaObservedPaused = false;
|
|
124
158
|
+ miaodaPaused = false;
|
|
125
159
|
+ miaodaLastControlVersion = version;
|
|
@@ -130,7 +164,8 @@ diff --git a/src/async-require/hmr.ts b/src/async-require/hmr.ts
|
|
|
130
164
|
+ if (fastRefreshRequested) HMRClient.enable();
|
|
131
165
|
+ }
|
|
132
166
|
+ } catch {
|
|
133
|
-
+ //
|
|
167
|
+
+ // 网络错误、超时、响应解析失败都属于暂时故障:不解除编辑闸门,
|
|
168
|
+
+ // 也不停止轮询,下一轮继续。
|
|
134
169
|
+ } finally {
|
|
135
170
|
+ scheduleMiaodaStatusPoll();
|
|
136
171
|
+ }
|
|
@@ -139,16 +174,17 @@ diff --git a/src/async-require/hmr.ts b/src/async-require/hmr.ts
|
|
|
139
174
|
function assert(foo: any, msg: string): asserts foo {
|
|
140
175
|
if (!foo) throw new Error(msg);
|
|
141
176
|
}
|
|
142
|
-
@@ -52,6 +
|
|
177
|
+
@@ -52,6 +181,9 @@ function assert(foo: any, msg: string): asserts foo {
|
|
143
178
|
*/
|
|
144
179
|
const HMRClient = {
|
|
145
180
|
enable() {
|
|
146
181
|
+ fastRefreshRequested = true;
|
|
182
|
+
+ // Agent 编辑闸门优先于用户操作:暂停期间只记录意图,等 resume 再恢复。
|
|
147
183
|
+ if (miaodaPaused) return;
|
|
148
184
|
if (hmrUnavailableReason !== null) {
|
|
149
185
|
// If HMR became unavailable while you weren't using it,
|
|
150
186
|
// explain why when you try to turn it on.
|
|
151
|
-
@@ -87,6 +
|
|
187
|
+
@@ -87,6 +219,7 @@ const HMRClient = {
|
|
152
188
|
},
|
|
153
189
|
|
|
154
190
|
disable() {
|
|
@@ -156,79 +192,93 @@ diff --git a/src/async-require/hmr.ts b/src/async-require/hmr.ts
|
|
|
156
192
|
assert(hmrClient, 'Expected HMRClient.setup() call at startup.');
|
|
157
193
|
hmrClient.disable();
|
|
158
194
|
},
|
|
159
|
-
@@ -180,6 +
|
|
195
|
+
@@ -180,6 +313,13 @@ const HMRClient = {
|
|
160
196
|
const origin = `${serverScheme}://${serverHost}`;
|
|
161
197
|
const client = new MetroHMRClient(`${origin}/hot`);
|
|
162
198
|
hmrClient = client;
|
|
163
199
|
+ if (process.env.EXPO_OS !== 'web') {
|
|
164
|
-
+ //
|
|
165
|
-
+ //
|
|
166
|
-
+ //
|
|
200
|
+
+ // 仅 native 客户端启用状态轮询。初始协议跟随 bundle URL 的 scheme:
|
|
201
|
+
+ // 设备可能拿到证书无效的 HTTPS 开发地址,也有环境只暴露 HTTPS,
|
|
202
|
+
+ // 因此先按 bundle 协议探测,成功后记住可用的那个协议。
|
|
167
203
|
+ miaodaStatusHost = serverHost;
|
|
168
204
|
+ miaodaStatusInitialProtocol = serverScheme === 'https' || serverScheme === 'wss' ? 'https' : 'http';
|
|
169
205
|
+ }
|
|
170
206
|
|
|
171
207
|
pendingEntryPoints.push(
|
|
172
208
|
// HMRServer understands regular bundle URLs, so prefer that in case
|
|
173
|
-
@@ -
|
|
174
|
-
platform: typeof platformOrOptions === 'string' ? platformOrOptions : undefined,
|
|
209
|
+
@@ -193,6 +333,55 @@ const HMRClient = {
|
|
175
210
|
})
|
|
176
211
|
);
|
|
177
|
-
|
|
212
|
+
|
|
178
213
|
+ client.on('open', () => {
|
|
179
214
|
+ miaodaWebSocketDisconnected = false;
|
|
180
|
-
+ //
|
|
215
|
+
+ // 重连对面的 Metro 可能是另一个进程(例如这次开启了 HMR 控制),
|
|
216
|
+
+ // 重置 404 停止标志,重新探测一次。
|
|
181
217
|
+ miaodaStatusPollingStopped = false;
|
|
182
218
|
+ scheduleMiaodaStatusPoll();
|
|
183
219
|
+ });
|
|
184
220
|
+
|
|
221
|
+
+ // 服务端编辑闸门控制消息(协议 v1)。stateVersion 随每条消息捎带,
|
|
222
|
+
+ // 收到即同步本地版本基线,轮询兜底据此判断有没有丢消息。
|
|
185
223
|
+ client.on('miaoda-hmr-control', (message: {
|
|
186
224
|
+ version: number; action: string; id?: number; error?: any; stateVersion?: number;
|
|
187
225
|
+ }) => {
|
|
188
226
|
+ if (message.version !== 1) return;
|
|
189
227
|
+ if (Number.isInteger(message.stateVersion)) {
|
|
228
|
+
+ if (miaodaSessionBaseVersion === null) miaodaSessionBaseVersion = message.stateVersion!;
|
|
190
229
|
+ miaodaStateVersion = message.stateVersion!;
|
|
191
230
|
+ miaodaLastControlVersion = message.stateVersion!;
|
|
192
231
|
+ }
|
|
193
232
|
+ switch (message.action) {
|
|
194
233
|
+ case 'pause':
|
|
234
|
+
+ // 立即闸住 HMR 并隐藏刷新提示,然后回 ack——服务端要等所有客户端
|
|
235
|
+
+ // ack 后才认为暂停完成,ack 之前 Agent 不会开始写文件。
|
|
195
236
|
+ miaodaPaused = true;
|
|
196
237
|
+ client.disable();
|
|
197
238
|
+ hideLoading();
|
|
198
239
|
+ client.send(JSON.stringify({type: 'miaoda-hmr-ack', version: 1, id: message.id}));
|
|
199
240
|
+ break;
|
|
200
241
|
+ case 'resume':
|
|
242
|
+
+ // 解除 Agent 闸门;用户自己的 Fast Refresh 开关保持独立,
|
|
243
|
+
+ // 只有它开着才真正恢复更新。
|
|
201
244
|
+ miaodaPaused = false;
|
|
202
245
|
+ if (fastRefreshRequested) HMRClient.enable();
|
|
203
246
|
+ break;
|
|
204
247
|
+ case 'reload':
|
|
248
|
+
+ // 完整加载前也保持闸门,避免 reload 完成前又有增量进来。
|
|
205
249
|
+ miaodaPaused = true;
|
|
206
250
|
+ client.disable();
|
|
207
251
|
+ reload();
|
|
208
252
|
+ break;
|
|
209
253
|
+ case 'error':
|
|
210
|
-
+ //
|
|
254
|
+
+ // 批次最终编译失败:闸门仍然关着(服务端等 Agent 修好重试),
|
|
255
|
+
+ // 但错误本身要展示出来,因为它在设备端是可行动的。
|
|
211
256
|
+ hideLoading();
|
|
212
257
|
+ handleCompileError(message.error);
|
|
213
258
|
+ break;
|
|
214
259
|
+ }
|
|
215
260
|
+ });
|
|
216
|
-
|
|
261
|
+
+
|
|
217
262
|
client.on('connection-error', (e: Error) => {
|
|
218
263
|
setHMRUnavailableReason(getConnectionError(serverHost, e));
|
|
219
|
-
|
|
264
|
+
});
|
|
265
|
+
@@ -234,6 +423,10 @@ const HMRClient = {
|
|
220
266
|
client.on('error', (data) => this._onMetroError(data));
|
|
221
267
|
|
|
222
268
|
client.on('close', (closeEvent?: { code: number; reason: string }) => {
|
|
269
|
+
+ // 控制通道断开:之后的版本变化都无法通过 socket 同步,
|
|
270
|
+
+ // 启动轮询兜底直到重连。
|
|
223
271
|
+ miaodaWebSocketDisconnected = true;
|
|
224
272
|
+ scheduleMiaodaStatusPoll();
|
|
225
273
|
hideLoading();
|
|
226
274
|
const reason = closeEvent?.reason;
|
|
227
275
|
const code = closeEvent?.code || 1000;
|
|
228
|
-
@@ -326,6 +
|
|
276
|
+
@@ -326,6 +519,9 @@ function registerBundleEntryPoints(client: MetroHMRClient | null) {
|
|
229
277
|
client?.send(
|
|
230
278
|
JSON.stringify({
|
|
231
279
|
type: 'register-entrypoints',
|
|
280
|
+
+ // Miaoda 控制协议 v1 的协商标记:带此标记的客户端才会收到
|
|
281
|
+
+ // miaoda-hmr-control 消息;不带标记的老客户端会触发服务端 409。
|
|
232
282
|
+ miaodaHmr: 1,
|
|
233
283
|
entryPoints: pendingEntryPoints,
|
|
234
284
|
})
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
diff --git a/src/index.js b/src/index.js
|
|
2
|
+
index ec21a55..b8a8f68 100644
|
|
2
3
|
--- a/src/index.js
|
|
3
4
|
+++ b/src/index.js
|
|
4
|
-
@@ -670,6 +670,
|
|
5
|
+
@@ -670,6 +670,23 @@ class FileMap extends _events.default {
|
|
5
6
|
);
|
|
6
7
|
});
|
|
7
8
|
};
|
|
8
|
-
+ // Miaoda
|
|
9
|
-
+ //
|
|
9
|
+
+ // Miaoda 批量编辑栅栏:resume 时用来证明「watcher 已经看到了 Agent 的
|
|
10
|
+
+ // 最后一次写入」。checkHealth 的 cookie 沿 watcher 通道走一遍,返回即代表
|
|
11
|
+
+ // 此前所有文件事件都已投递到本进程;再把 changeQueue 排空(处理中的事件
|
|
12
|
+
+ // 可能继续入队新任务,所以循环到队列不再变化);最后 emitChange() 立即
|
|
13
|
+
+ // 发出累积的变更,让 DeltaCalculator 不等 CHANGE_INTERVAL 就能看到它们。
|
|
10
14
|
+ this.miaodaFlushChanges = async (timeout = 5000) => {
|
|
11
15
|
+ const result = await this._watcher.checkHealth(timeout);
|
|
12
16
|
+ if (result.type !== "success") {
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
diff --git a/src/modules/HMRClient.js b/src/modules/HMRClient.js
|
|
2
|
+
index 3e2652d..8225714 100644
|
|
2
3
|
--- a/src/modules/HMRClient.js
|
|
3
4
|
+++ b/src/modules/HMRClient.js
|
|
4
|
-
@@ -35,6 +35,
|
|
5
|
+
@@ -35,6 +35,12 @@ class HMRClient extends EventEmitter {
|
|
5
6
|
this._ws.onmessage = (message) => {
|
|
6
7
|
const data = JSON.parse(String(message.data));
|
|
7
8
|
switch (data.type) {
|
|
9
|
+
+ // Miaoda HMR 批量编辑控制:服务端复用这条 /hot socket 下发
|
|
10
|
+
+ // 控制消息。HMRClient 只做透传转发,具体处理在 expo 包的
|
|
11
|
+
+ // async-require/hmr.ts(该补丁同时加了版本协商,version !== 1 直接忽略)。
|
|
8
12
|
+ case "miaoda-hmr-control":
|
|
9
13
|
+ if (data.version === 1) this.emit("miaoda-hmr-control", data);
|
|
10
14
|
+ break;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
diff --git a/plugin/index.js b/plugin/index.js
|
|
2
|
+
--- a/plugin/index.js
|
|
3
|
+
+++ b/plugin/index.js
|
|
4
|
+
@@ -752,6 +752,31 @@ var require_workletStringCode = __commonJS({
|
|
5
|
+
var types_2 = require_types();
|
|
6
|
+
var utils_1 = require_utils();
|
|
7
|
+
var MOCK_SOURCE_MAP = "mock source map";
|
|
8
|
+
+ function normalizeTypeScriptFunctionTypes(node) {
|
|
9
|
+
+ if (node == null || typeof node !== "object") {
|
|
10
|
+
+ return;
|
|
11
|
+
+ }
|
|
12
|
+
+ if (node.type === "TSFunctionType" || node.type === "TSConstructorType") {
|
|
13
|
+
+ const parameters = Array.isArray(node.parameters) ? node.parameters : Array.isArray(node.params) ? node.params : [];
|
|
14
|
+
+ node.parameters = parameters;
|
|
15
|
+
+ node.params = parameters;
|
|
16
|
+
+ const returnType = node.typeAnnotation != null ? node.typeAnnotation : node.returnType != null ? node.returnType : null;
|
|
17
|
+
+ node.typeAnnotation = returnType;
|
|
18
|
+
+ node.returnType = returnType;
|
|
19
|
+
+ }
|
|
20
|
+
+ if (node.type === "TSTypeReference") {
|
|
21
|
+
+ const typeArguments = node.typeParameters != null ? node.typeParameters : node.typeArguments != null ? node.typeArguments : null;
|
|
22
|
+
+ node.typeParameters = typeArguments;
|
|
23
|
+
+ node.typeArguments = typeArguments;
|
|
24
|
+
+ }
|
|
25
|
+
+ for (const value of Object.values(node)) {
|
|
26
|
+
+ if (Array.isArray(value)) {
|
|
27
|
+
+ value.forEach(normalizeTypeScriptFunctionTypes);
|
|
28
|
+
+ } else {
|
|
29
|
+
+ normalizeTypeScriptFunctionTypes(value);
|
|
30
|
+
+ }
|
|
31
|
+
+ }
|
|
32
|
+
+ }
|
|
33
|
+
function buildWorkletString(fun, state, closureVariables, workletName, inputMap) {
|
|
34
|
+
var _a;
|
|
35
|
+
restoreRecursiveCalls(fun, workletName);
|
|
36
|
+
@@ -784,6 +809,7 @@ var require_workletStringCode = __commonJS({
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
const workletFunction = (0, types_12.functionExpression)((0, types_12.identifier)(workletName), expression.params, expression.body, expression.generator, expression.async);
|
|
40
|
+
+ normalizeTypeScriptFunctionTypes(workletFunction);
|
|
41
|
+
const code = (0, generator_1.default)(workletFunction).code;
|
|
42
|
+
(0, assert_1.strict)(inputMap, "[Reanimated] `inputMap` is undefined.");
|
|
43
|
+
const includeSourceMap = !((0, utils_1.isRelease)() || state.opts.disableSourceMaps);
|
|
44
|
+
@@ -892,10 +918,36 @@ var require_workletFactory = __commonJS({
|
|
45
|
+
var workletStringCode_1 = require_workletStringCode();
|
|
46
|
+
var REAL_VERSION = require("../package.json").version;
|
|
47
|
+
var MOCK_VERSION = "x.y.z";
|
|
48
|
+
+ function normalizeTypeScriptFunctionTypes(node) {
|
|
49
|
+
+ if (node == null || typeof node !== "object") {
|
|
50
|
+
+ return;
|
|
51
|
+
+ }
|
|
52
|
+
+ if (node.type === "TSFunctionType" || node.type === "TSConstructorType") {
|
|
53
|
+
+ const parameters = Array.isArray(node.parameters) ? node.parameters : Array.isArray(node.params) ? node.params : [];
|
|
54
|
+
+ node.parameters = parameters;
|
|
55
|
+
+ node.params = parameters;
|
|
56
|
+
+ const returnType = node.typeAnnotation != null ? node.typeAnnotation : node.returnType != null ? node.returnType : null;
|
|
57
|
+
+ node.typeAnnotation = returnType;
|
|
58
|
+
+ node.returnType = returnType;
|
|
59
|
+
+ }
|
|
60
|
+
+ if (node.type === "TSTypeReference") {
|
|
61
|
+
+ const typeArguments = node.typeParameters != null ? node.typeParameters : node.typeArguments != null ? node.typeArguments : null;
|
|
62
|
+
+ node.typeParameters = typeArguments;
|
|
63
|
+
+ node.typeArguments = typeArguments;
|
|
64
|
+
+ }
|
|
65
|
+
+ for (const value of Object.values(node)) {
|
|
66
|
+
+ if (Array.isArray(value)) {
|
|
67
|
+
+ value.forEach(normalizeTypeScriptFunctionTypes);
|
|
68
|
+
+ } else {
|
|
69
|
+
+ normalizeTypeScriptFunctionTypes(value);
|
|
70
|
+
+ }
|
|
71
|
+
+ }
|
|
72
|
+
+ }
|
|
73
|
+
function makeWorkletFactory(fun, state) {
|
|
74
|
+
var _a;
|
|
75
|
+
removeWorkletDirective(fun);
|
|
76
|
+
(0, assert_1.strict)(state.file.opts.filename, "[Reanimated] `state.file.opts.filename` is undefined.");
|
|
77
|
+
+ normalizeTypeScriptFunctionTypes(fun.node);
|
|
78
|
+
const codeObject = (0, generator_1.default)(fun.node, {
|
|
79
|
+
sourceMaps: true,
|
|
80
|
+
sourceFileName: state.file.opts.filename
|
|
81
|
+
@@ -977,8 +1029,8 @@ var require_workletFactory = __commonJS({
|
|
82
|
+
statements.unshift((0, types_12.variableDeclaration)("const", [
|
|
83
|
+
(0, types_12.variableDeclarator)((0, types_12.identifier)("_e"), (0, types_12.arrayExpression)([
|
|
84
|
+
(0, types_12.newExpression)((0, types_12.memberExpression)((0, types_12.identifier)("global"), (0, types_12.identifier)("Error")), []),
|
|
85
|
+
- (0, types_12.numericLiteral)(lineOffset),
|
|
86
|
+
- (0, types_12.numericLiteral)(-27)
|
|
87
|
+
+ lineOffset < 0 ? (0, types_12.unaryExpression)("-", (0, types_12.numericLiteral)(-lineOffset)) : (0, types_12.numericLiteral)(lineOffset),
|
|
88
|
+
+ (0, types_12.unaryExpression)("-", (0, types_12.numericLiteral)(27))
|
|
89
|
+
]))
|
|
90
|
+
]));
|
|
91
|
+
statements.push((0, types_12.expressionStatement)((0, types_12.assignmentExpression)("=", (0, types_12.memberExpression)((0, types_12.identifier)(reactName), (0, types_12.identifier)("__stackDetails"), false), (0, types_12.identifier)("_e"))));
|
package/pnpmfile.cjs
CHANGED
|
@@ -18,16 +18,22 @@ const managedPatches = {
|
|
|
18
18
|
[EXPO_CLI_PATCH_SELECTOR]: {
|
|
19
19
|
filename: '@expo__cli@55.0.16-55.0.22.patch',
|
|
20
20
|
legacyHashes: new Set([
|
|
21
|
+
'29f03d7d2633dad8e8ff99815c38a4f4059e46a235a5e66c508125975d920385',
|
|
22
|
+
'98c96a6bae56f47c0a47efe9a68e7a6751022d812b569b68d03b93753635c32c',
|
|
21
23
|
'9a0d662675fba8088396c6cdbab7d36d81fbe9acb3727144a50826ff3500b093',
|
|
22
24
|
]),
|
|
23
25
|
},
|
|
24
26
|
'metro-runtime@0.83.3': {
|
|
25
27
|
filename: 'metro-runtime@0.83.3.patch',
|
|
26
|
-
legacyHashes: new Set(
|
|
28
|
+
legacyHashes: new Set([
|
|
29
|
+
'bd14ae6037bcf49028607c01caf67d4bd03e543460609a62e72647664c27b242',
|
|
30
|
+
]),
|
|
27
31
|
},
|
|
28
32
|
'metro-file-map@0.83.3': {
|
|
29
33
|
filename: 'metro-file-map@0.83.3.patch',
|
|
30
|
-
legacyHashes: new Set(
|
|
34
|
+
legacyHashes: new Set([
|
|
35
|
+
'41bc590f22f0e8ba73f090394db218bbb9c808a1eafdc70bfb746abbd3017b3b',
|
|
36
|
+
]),
|
|
31
37
|
},
|
|
32
38
|
'@shopify/react-native-skia@2.4.18': {
|
|
33
39
|
filename: '@shopify__react-native-skia@2.4.18.patch',
|
|
@@ -44,6 +50,9 @@ const managedPatches = {
|
|
|
44
50
|
'expo@55.0.6': {
|
|
45
51
|
filename: 'expo@55.0.6.patch',
|
|
46
52
|
legacyHashes: new Set([
|
|
53
|
+
'1df9b522728e93daf64f9e2298ab835ce52d55b4aa2de92a68b947e7ed08f40a',
|
|
54
|
+
'8ac63998a9852ddafda7af96deb4d7ac4c3a5da5e65e865af32be9d4fd966e02',
|
|
55
|
+
'12efbb4c64bc67744a838cc5d9d88604ad2a99cdd578a5cfab31b8d1c2be1144',
|
|
47
56
|
'd2507f55976f6c5da8f35d26906f4bd46415e042570afc3bd7c543c86abc5a61',
|
|
48
57
|
'5dda484d5acb6475495587d96c8cbae5bc7514a5781159c92faa9637e6e257ec',
|
|
49
58
|
]),
|
|
@@ -60,14 +69,32 @@ const managedPatches = {
|
|
|
60
69
|
'b2c55038b2fa525ad5e84bd4940ddd2b7298b559de2d0d1709f32ced57e929e9',
|
|
61
70
|
]),
|
|
62
71
|
},
|
|
72
|
+
'react-native@0.83.2': {
|
|
73
|
+
filename: 'react-native@0.83.2.patch',
|
|
74
|
+
legacyHashes: new Set(),
|
|
75
|
+
},
|
|
76
|
+
'react-native-worklets@0.7.4': {
|
|
77
|
+
filename: 'react-native-worklets@0.7.4.patch',
|
|
78
|
+
legacyHashes: new Set([
|
|
79
|
+
'8fee2c39a582a0ec150a3fd5af632fba6736dc03565763d21376b54820fe6668',
|
|
80
|
+
'd9e02591e77a9f510e215ce273330381f81c68659c5736374f33e231d0bc9611',
|
|
81
|
+
]),
|
|
82
|
+
},
|
|
63
83
|
'react-native-worklets@0.7.2': {
|
|
64
84
|
filename: 'react-native-worklets@0.7.2.patch',
|
|
65
85
|
legacyHashes: new Set([
|
|
66
86
|
'8fee2c39a582a0ec150a3fd5af632fba6736dc03565763d21376b54820fe6668',
|
|
87
|
+
'd9e02591e77a9f510e215ce273330381f81c68659c5736374f33e231d0bc9611',
|
|
67
88
|
]),
|
|
68
89
|
},
|
|
69
90
|
};
|
|
70
91
|
|
|
92
|
+
const legacyPatchSelectors = new Set([
|
|
93
|
+
'@shopify/react-native-skia@2.4.18',
|
|
94
|
+
'react-native-css-interop@0.2.6',
|
|
95
|
+
'react-native-worklets@0.7.2',
|
|
96
|
+
]);
|
|
97
|
+
|
|
71
98
|
function getPatchHash(patchPath) {
|
|
72
99
|
return crypto.createHash('sha256').update(fs.readFileSync(patchPath)).digest('hex');
|
|
73
100
|
}
|
|
@@ -96,6 +123,51 @@ function getBundledPatchPath(config, filename) {
|
|
|
96
123
|
return path.join(__dirname, 'pnpm-patches', filename);
|
|
97
124
|
}
|
|
98
125
|
|
|
126
|
+
function projectRequestsLegacySelector(config, selector) {
|
|
127
|
+
const at = selector.lastIndexOf('@');
|
|
128
|
+
if (at <= 0) return false;
|
|
129
|
+
const name = selector.slice(0, at);
|
|
130
|
+
const version = selector.slice(at + 1);
|
|
131
|
+
const projectRoot = getProjectRoot(config);
|
|
132
|
+
const manifests = [path.join(projectRoot, 'package.json')];
|
|
133
|
+
for (const directory of ['template', 'packages']) {
|
|
134
|
+
const candidate = path.join(projectRoot, directory);
|
|
135
|
+
if (!fs.existsSync(candidate)) continue;
|
|
136
|
+
for (const entry of fs.readdirSync(candidate, { withFileTypes: true })) {
|
|
137
|
+
if (entry.isDirectory()) manifests.push(path.join(candidate, entry.name, 'package.json'));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
for (const manifest of manifests) {
|
|
141
|
+
try {
|
|
142
|
+
const packageJson = JSON.parse(fs.readFileSync(manifest, 'utf8'));
|
|
143
|
+
for (const section of ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']) {
|
|
144
|
+
for (const [dependency, spec] of Object.entries(packageJson[section] ?? {})) {
|
|
145
|
+
if (typeof spec !== 'string') continue;
|
|
146
|
+
if (
|
|
147
|
+
(dependency === name || spec.includes(name)) &&
|
|
148
|
+
(spec === version || spec === `${name}@${version}` || spec.endsWith(`@${version}`))
|
|
149
|
+
) return true;
|
|
150
|
+
if (spec === 'catalog:' || spec === 'catalog') {
|
|
151
|
+
const workspace = fs.existsSync(path.join(projectRoot, 'pnpm-workspace.yaml'))
|
|
152
|
+
? fs.readFileSync(path.join(projectRoot, 'pnpm-workspace.yaml'), 'utf8')
|
|
153
|
+
: '';
|
|
154
|
+
const match = new RegExp(`^\\s{2}${name.replace(/[.*+?^${}()|[\\]\\]/g, '\\\\$&')}:\\s*([^#\\n]+)`, 'm').exec(workspace);
|
|
155
|
+
if (match?.[1].trim() === version) return true;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
} catch {
|
|
160
|
+
// Defer malformed manifest errors to pnpm.
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
const lockfile = fs.readFileSync(path.join(projectRoot, 'pnpm-lock.yaml'), 'utf8');
|
|
165
|
+
return new RegExp(`(?:^|\\n)\\s{2}${selector.replace(/[.*+?^${}()|[\\]\\]/g, '\\\\$&')}:`).test(lockfile);
|
|
166
|
+
} catch {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
99
171
|
function applyManagedPatches(config) {
|
|
100
172
|
const patchedDependencies = { ...config.patchedDependencies };
|
|
101
173
|
|
|
@@ -103,6 +175,19 @@ function applyManagedPatches(config) {
|
|
|
103
175
|
const bundledPatchPath = getBundledPatchPath(config, managedPatch.filename);
|
|
104
176
|
const projectPatch = patchedDependencies[selector];
|
|
105
177
|
|
|
178
|
+
if (legacyPatchSelectors.has(selector) && !projectRequestsLegacySelector(config, selector)) {
|
|
179
|
+
if (projectPatch != null) {
|
|
180
|
+
try {
|
|
181
|
+
if (managedPatch.legacyHashes.has(getPatchHash(resolveProjectPatchPath(config, projectPatch)))) {
|
|
182
|
+
delete patchedDependencies[selector];
|
|
183
|
+
}
|
|
184
|
+
} catch {
|
|
185
|
+
// Keep unreadable or custom project patches for pnpm's normal diagnostics.
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
|
|
106
191
|
if (projectPatch == null) {
|
|
107
192
|
patchedDependencies[selector] = bundledPatchPath;
|
|
108
193
|
continue;
|