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.
@@ -1,5 +1,5 @@
1
1
  diff --git a/bundledNativeModules.json b/bundledNativeModules.json
2
- index e2b8941923b274b1c5c9bda2269d28cf574aff7f..355deae878b654e1e1d9db114ee88c1e7e504276 100644
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,20 @@
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
- +// Agent pause is separate from the user's native Fast Refresh preference.
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 +57,86 @@
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
- + return `${protocol}://${miaodaStatusHost}/innerapi/v1/sourcecode/__hmr_status`;
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
- + // Devices may reject development certificates and need HTTP, while other
85
- + // environments expose HTTPS only. Switch only after a network/SSL failure.
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
- + // A 404 is a definitive signal that this Metro instance has no control route.
92
- + // Stop probing until the WebSocket reconnects, which may point at a new server.
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 and other temporary HTTP failures are retried on the next interval.
126
+ + // 5xx 等暂时性 HTTP 失败不停止轮询,下一轮继续重试。
102
127
  + return;
103
128
  + }
104
129
  +
105
- + // Remember the protocol that reached the server for subsequent polls.
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
- + const version = Number.isInteger(state.stateVersion) ? state.stateVersion : null;
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
- + // The observed pause is now our local baseline. A later version without a
118
- + // matching WebSocket message requires a full Bundle reload.
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 shouldReload = !firstObservation && (state.lastMode === 'reload' || (missedControl && (miaodaObservedPaused || miaodaWebSocketDisconnected)));
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
- + // Network, timeout and invalid-response failures must not release the edit gate.
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 +147,8 @@
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 +184,7 @@
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 +278,13 @@
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
- + // Native clients may receive HTTPS bundle URLs with invalid development certificates,
165
- + // while some deployments expose only HTTPS. Probe the bundle protocol first, then
166
- + // remember the protocol that succeeds for subsequent polls.
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
- @@ -192,6 +297,45 @@
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
- + // A reconnect may target a Metro process with HMR control enabled.
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
- + // Final compilation failure is actionable even while the edit gate stays closed.
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
- @@ -234,6 +378,8 @@
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 +472,7 @@
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,20 @@
5
+ @@ -670,6 +670,23 @@ class FileMap extends _events.default {
5
6
  );
6
7
  });
7
8
  };
8
- + // Miaoda edit-batch barrier. The cookie fences watcher delivery; drain file
9
- + // processing before emitting the changes used by DeltaCalculator.
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,9 @@
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;