react-native-debug-toolkit 3.3.3 → 3.3.8
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 +48 -13
- package/README.zh-CN.md +48 -13
- package/android/src/main/java/com/reactnativedebugtoolkit/DebugToolkitDevConnectModule.java +0 -187
- package/bin/debug-toolkit.js +1 -16
- package/ios/DebugToolkitDevConnect.h +0 -12
- package/ios/DebugToolkitDevConnect.mm +0 -354
- package/lib/commonjs/core/initialize.js +8 -1
- package/lib/commonjs/core/initialize.js.map +1 -1
- package/lib/commonjs/features/devConnect/DevConnectTab.js +18 -470
- package/lib/commonjs/features/devConnect/DevConnectTab.js.map +1 -1
- package/lib/commonjs/features/devConnect/devConnectPreferences.js +0 -12
- package/lib/commonjs/features/devConnect/devConnectPreferences.js.map +1 -1
- package/lib/commonjs/features/devConnect/devConnectUtils.js +2 -57
- package/lib/commonjs/features/devConnect/devConnectUtils.js.map +1 -1
- package/lib/commonjs/features/devConnect/index.js +1 -23
- package/lib/commonjs/features/devConnect/index.js.map +1 -1
- package/lib/commonjs/features/devConnect/nativeDevConnect.js +1 -103
- package/lib/commonjs/features/devConnect/nativeDevConnect.js.map +1 -1
- package/lib/commonjs/index.js +7 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/DebugView.js +2 -0
- package/lib/commonjs/ui/DebugView.js.map +1 -1
- package/lib/commonjs/ui/panel/FloatPanelView.js +22 -10
- package/lib/commonjs/ui/panel/FloatPanelView.js.map +1 -1
- package/lib/commonjs/ui/panel/tabPersistence.js +17 -0
- package/lib/commonjs/ui/panel/tabPersistence.js.map +1 -0
- package/lib/commonjs/utils/createDebugTab.js +21 -0
- package/lib/commonjs/utils/createDebugTab.js.map +1 -0
- package/lib/commonjs/utils/debugPreferences.js +0 -1
- package/lib/commonjs/utils/debugPreferences.js.map +1 -1
- package/lib/module/core/initialize.js +8 -1
- package/lib/module/core/initialize.js.map +1 -1
- package/lib/module/features/devConnect/DevConnectTab.js +21 -473
- package/lib/module/features/devConnect/DevConnectTab.js.map +1 -1
- package/lib/module/features/devConnect/devConnectPreferences.js +1 -12
- package/lib/module/features/devConnect/devConnectPreferences.js.map +1 -1
- package/lib/module/features/devConnect/devConnectUtils.js +1 -53
- package/lib/module/features/devConnect/devConnectUtils.js.map +1 -1
- package/lib/module/features/devConnect/index.js +5 -9
- package/lib/module/features/devConnect/index.js.map +1 -1
- package/lib/module/features/devConnect/nativeDevConnect.js +1 -100
- package/lib/module/features/devConnect/nativeDevConnect.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/DebugView.js +2 -0
- package/lib/module/ui/DebugView.js.map +1 -1
- package/lib/module/ui/panel/FloatPanelView.js +22 -10
- package/lib/module/ui/panel/FloatPanelView.js.map +1 -1
- package/lib/module/ui/panel/tabPersistence.js +13 -0
- package/lib/module/ui/panel/tabPersistence.js.map +1 -0
- package/lib/module/utils/createDebugTab.js +17 -0
- package/lib/module/utils/createDebugTab.js.map +1 -0
- package/lib/module/utils/debugPreferences.js +0 -1
- package/lib/module/utils/debugPreferences.js.map +1 -1
- package/lib/typescript/src/core/initialize.d.ts +2 -0
- package/lib/typescript/src/core/initialize.d.ts.map +1 -1
- package/lib/typescript/src/features/devConnect/DevConnectTab.d.ts.map +1 -1
- package/lib/typescript/src/features/devConnect/devConnectPreferences.d.ts +0 -2
- package/lib/typescript/src/features/devConnect/devConnectPreferences.d.ts.map +1 -1
- package/lib/typescript/src/features/devConnect/devConnectUtils.d.ts +0 -20
- package/lib/typescript/src/features/devConnect/devConnectUtils.d.ts.map +1 -1
- package/lib/typescript/src/features/devConnect/index.d.ts +2 -2
- package/lib/typescript/src/features/devConnect/index.d.ts.map +1 -1
- package/lib/typescript/src/features/devConnect/nativeDevConnect.d.ts +0 -25
- package/lib/typescript/src/features/devConnect/nativeDevConnect.d.ts.map +1 -1
- package/lib/typescript/src/features/devConnect/types.d.ts +1 -3
- package/lib/typescript/src/features/devConnect/types.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/ui/DebugView.d.ts +4 -2
- package/lib/typescript/src/ui/DebugView.d.ts.map +1 -1
- package/lib/typescript/src/ui/panel/FloatPanelView.d.ts.map +1 -1
- package/lib/typescript/src/ui/panel/tabPersistence.d.ts +3 -0
- package/lib/typescript/src/ui/panel/tabPersistence.d.ts.map +1 -0
- package/lib/typescript/src/utils/createDebugTab.d.ts +18 -0
- package/lib/typescript/src/utils/createDebugTab.d.ts.map +1 -0
- package/lib/typescript/src/utils/debugPreferences.d.ts +0 -1
- package/lib/typescript/src/utils/debugPreferences.d.ts.map +1 -1
- package/package.json +2 -4
- package/src/core/initialize.ts +17 -1
- package/src/features/devConnect/DevConnectTab.tsx +17 -381
- package/src/features/devConnect/devConnectPreferences.ts +0 -15
- package/src/features/devConnect/devConnectUtils.ts +1 -81
- package/src/features/devConnect/index.ts +2 -9
- package/src/features/devConnect/nativeDevConnect.ts +1 -136
- package/src/features/devConnect/types.ts +1 -3
- package/src/index.ts +2 -0
- package/src/ui/DebugView.tsx +5 -1
- package/src/ui/panel/FloatPanelView.tsx +22 -10
- package/src/ui/panel/tabPersistence.ts +22 -0
- package/src/utils/createDebugTab.ts +32 -0
- package/src/utils/debugPreferences.ts +0 -1
- package/scripts/android-debug-bundle.gradle +0 -23
- package/scripts/eas-postinstall.sh +0 -6
- package/scripts/embed-android.js +0 -116
- package/scripts/embed-expo.js +0 -109
- package/scripts/embed-ios.js +0 -119
- package/scripts/embed.js +0 -224
package/README.md
CHANGED
|
@@ -63,8 +63,8 @@ Run the app in dev mode, then tap `DBG`.
|
|
|
63
63
|
Start the desktop daemon:
|
|
64
64
|
|
|
65
65
|
```bash
|
|
66
|
-
npm exec debug-toolkit --daemon-only
|
|
67
|
-
# or: npx debug-toolkit --daemon-only
|
|
66
|
+
npm exec -- debug-toolkit --daemon-only
|
|
67
|
+
# or: npx react-native-debug-toolkit --daemon-only
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
Open the Web Console:
|
|
@@ -77,12 +77,6 @@ In the app, open Debug Panel -> `DevConnect` -> `Send Once` or `Start Live Sync`
|
|
|
77
77
|
|
|
78
78
|
DevConnect auto-detects simulator/emulator and uses local host settings automatically. On real devices, enter your computer IP to connect.
|
|
79
79
|
|
|
80
|
-
For Remote JS Bundle, run Metro on your computer, enter computer IP and Metro port in `DevConnect`, then tap `Use Metro Bundle`. DevConnect persists the host and hot-reloads from Metro.
|
|
81
|
-
|
|
82
|
-
> **Debug builds only.** Metro host switching works in Debug builds. Release builds load the embedded bundle and the controls are disabled (`release: disabled` badge).
|
|
83
|
-
|
|
84
|
-
**iOS — no AppDelegate changes required.** On install, DevConnect hooks `RCTBundleURLProvider` so the app **cold-starts from the embedded `main.jsbundle`** and only connects to Metro after you apply a host in the panel (fixes Expo `.expo/.virtual-metro-entry` red screens when Metro is off). Use **Reset** to go back to the embedded bundle.
|
|
85
|
-
|
|
86
80
|
The IP and ports are persisted through AsyncStorage when installed, or through the native module after rebuild.
|
|
87
81
|
|
|
88
82
|
QR scan is optional. Install `react-native-camera-kit` or `expo-camera` in the app to enable the scan button. The app must request camera permission before scanning.
|
|
@@ -112,9 +106,9 @@ The daemon stores logs at:
|
|
|
112
106
|
Custom store path:
|
|
113
107
|
|
|
114
108
|
```bash
|
|
115
|
-
npm exec debug-toolkit --daemon-only --store /path/to/devices.json
|
|
116
|
-
# or: npx debug-toolkit --daemon-only --store /path/to/devices.json
|
|
117
|
-
DEBUG_TOOLKIT_DAEMON_STORE=/path/to/devices.json npm exec debug-toolkit --daemon-only
|
|
109
|
+
npm exec -- debug-toolkit --daemon-only --store /path/to/devices.json
|
|
110
|
+
# or: npx react-native-debug-toolkit --daemon-only --store /path/to/devices.json
|
|
111
|
+
DEBUG_TOOLKIT_DAEMON_STORE=/path/to/devices.json npm exec -- debug-toolkit --daemon-only
|
|
118
112
|
```
|
|
119
113
|
|
|
120
114
|
## Read Logs With HTTP
|
|
@@ -156,8 +150,8 @@ GET /console
|
|
|
156
150
|
## Use MCP
|
|
157
151
|
|
|
158
152
|
```bash
|
|
159
|
-
claude mcp add debug-toolkit -- npm exec debug-toolkit
|
|
160
|
-
# or: claude mcp add debug-toolkit -- npx debug-toolkit
|
|
153
|
+
claude mcp add debug-toolkit -- npm exec -- debug-toolkit
|
|
154
|
+
# or: claude mcp add debug-toolkit -- npx react-native-debug-toolkit
|
|
161
155
|
```
|
|
162
156
|
|
|
163
157
|
Tools:
|
|
@@ -177,6 +171,46 @@ Disable features:
|
|
|
177
171
|
</DebugView>
|
|
178
172
|
```
|
|
179
173
|
|
|
174
|
+
Custom tabs:
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
import {
|
|
178
|
+
DebugView,
|
|
179
|
+
createDebugTab,
|
|
180
|
+
type DebugFeatureRenderProps,
|
|
181
|
+
} from 'react-native-debug-toolkit';
|
|
182
|
+
|
|
183
|
+
type UserSnapshot = {
|
|
184
|
+
id?: string;
|
|
185
|
+
role?: string;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
function UserDebugTab({ snapshot }: DebugFeatureRenderProps<UserSnapshot>) {
|
|
189
|
+
return (
|
|
190
|
+
<View>
|
|
191
|
+
<Text>User ID: {snapshot.id ?? '-'}</Text>
|
|
192
|
+
<Text>Role: {snapshot.role ?? '-'}</Text>
|
|
193
|
+
</View>
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const userDebugTab = createDebugTab<UserSnapshot>({
|
|
198
|
+
name: 'user',
|
|
199
|
+
label: 'User',
|
|
200
|
+
getSnapshot: () => ({
|
|
201
|
+
id: authStore.user?.id,
|
|
202
|
+
role: authStore.user?.role,
|
|
203
|
+
}),
|
|
204
|
+
render: UserDebugTab,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
<DebugView customFeatures={[userDebugTab]}>
|
|
208
|
+
<AppContent />
|
|
209
|
+
</DebugView>;
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Each custom feature becomes a panel tab. `name` is the stable tab id, `label` is shown in the tab bar, `getSnapshot` provides tab data, and `render` controls the UI. Add `subscribe` when the tab should refresh after external state changes.
|
|
213
|
+
|
|
180
214
|
Navigation tracking:
|
|
181
215
|
|
|
182
216
|
```tsx
|
|
@@ -206,6 +240,7 @@ addTrackLog({ eventName: 'button_click' });
|
|
|
206
240
|
- `DebugView`
|
|
207
241
|
- `DebugToolkit`
|
|
208
242
|
- `initializeDebugToolkit`
|
|
243
|
+
- `createDebugTab`
|
|
209
244
|
- `createDebugDeviceReport`
|
|
210
245
|
- `checkDaemonConnection`
|
|
211
246
|
- `reportDebugDeviceToDaemon`
|
package/README.zh-CN.md
CHANGED
|
@@ -63,8 +63,8 @@ export function App() {
|
|
|
63
63
|
启动桌面 daemon:
|
|
64
64
|
|
|
65
65
|
```bash
|
|
66
|
-
npm exec debug-toolkit --daemon-only
|
|
67
|
-
# 或:npx debug-toolkit --daemon-only
|
|
66
|
+
npm exec -- debug-toolkit --daemon-only
|
|
67
|
+
# 或:npx react-native-debug-toolkit --daemon-only
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
打开 Web Console:
|
|
@@ -77,12 +77,6 @@ App 内打开 Debug Panel -> `DevConnect` -> `Send Once` 或 `Start Live Sync`
|
|
|
77
77
|
|
|
78
78
|
DevConnect 自动识别模拟器/真机,模拟器下自动使用本机 Metro/daemon 地址。真机需输入电脑 IP 地址。
|
|
79
79
|
|
|
80
|
-
Remote JS Bundle:先在电脑启动 Metro,在 `DevConnect` 输入电脑 IP 和 Metro 端口,然后点 `Use Metro Bundle`。DevConnect 会持久化 host 并从 Metro 热重载。
|
|
81
|
-
|
|
82
|
-
> **仅 Debug 包可用。** Release 包控件会显示 `release: disabled` 并禁用。
|
|
83
|
-
|
|
84
|
-
**iOS — 无需改 AppDelegate。** 安装本库后会 hook `RCTBundleURLProvider`:**未配置 IP 时冷启动走内置 `main.jsbundle`**,只有在面板里应用 host 后才连 Metro(可避免 Metro 未开时的 `.expo/.virtual-metro-entry` 红屏)。点 **Reset** 可回到内置 bundle。
|
|
85
|
-
|
|
86
80
|
IP 和端口会通过 AsyncStorage 持久化;如果没装 AsyncStorage,则在重建后通过本库原生模块持久化。
|
|
87
81
|
|
|
88
82
|
扫码是可选能力。App 安装 `react-native-camera-kit` 或 `expo-camera` 后,DevConnect 才显示扫码按钮。App 仍需自己配置相机权限文案,并在使用扫码前申请相机权限。
|
|
@@ -112,9 +106,9 @@ daemon 默认日志文件:
|
|
|
112
106
|
自定义存储路径:
|
|
113
107
|
|
|
114
108
|
```bash
|
|
115
|
-
npm exec debug-toolkit --daemon-only --store /path/to/devices.json
|
|
116
|
-
# 或:npx debug-toolkit --daemon-only --store /path/to/devices.json
|
|
117
|
-
DEBUG_TOOLKIT_DAEMON_STORE=/path/to/devices.json npm exec debug-toolkit --daemon-only
|
|
109
|
+
npm exec -- debug-toolkit --daemon-only --store /path/to/devices.json
|
|
110
|
+
# 或:npx react-native-debug-toolkit --daemon-only --store /path/to/devices.json
|
|
111
|
+
DEBUG_TOOLKIT_DAEMON_STORE=/path/to/devices.json npm exec -- debug-toolkit --daemon-only
|
|
118
112
|
```
|
|
119
113
|
|
|
120
114
|
## 用 HTTP 读取日志
|
|
@@ -156,8 +150,8 @@ GET /console
|
|
|
156
150
|
## 使用 MCP
|
|
157
151
|
|
|
158
152
|
```bash
|
|
159
|
-
claude mcp add debug-toolkit -- npm exec debug-toolkit
|
|
160
|
-
# 或:claude mcp add debug-toolkit -- npx debug-toolkit
|
|
153
|
+
claude mcp add debug-toolkit -- npm exec -- debug-toolkit
|
|
154
|
+
# 或:claude mcp add debug-toolkit -- npx react-native-debug-toolkit
|
|
161
155
|
```
|
|
162
156
|
|
|
163
157
|
工具:
|
|
@@ -177,6 +171,46 @@ claude mcp add debug-toolkit -- npm exec debug-toolkit
|
|
|
177
171
|
</DebugView>
|
|
178
172
|
```
|
|
179
173
|
|
|
174
|
+
自定义 Tab:
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
import {
|
|
178
|
+
DebugView,
|
|
179
|
+
createDebugTab,
|
|
180
|
+
type DebugFeatureRenderProps,
|
|
181
|
+
} from 'react-native-debug-toolkit';
|
|
182
|
+
|
|
183
|
+
type UserSnapshot = {
|
|
184
|
+
id?: string;
|
|
185
|
+
role?: string;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
function UserDebugTab({ snapshot }: DebugFeatureRenderProps<UserSnapshot>) {
|
|
189
|
+
return (
|
|
190
|
+
<View>
|
|
191
|
+
<Text>User ID: {snapshot.id ?? '-'}</Text>
|
|
192
|
+
<Text>Role: {snapshot.role ?? '-'}</Text>
|
|
193
|
+
</View>
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const userDebugTab = createDebugTab<UserSnapshot>({
|
|
198
|
+
name: 'user',
|
|
199
|
+
label: 'User',
|
|
200
|
+
getSnapshot: () => ({
|
|
201
|
+
id: authStore.user?.id,
|
|
202
|
+
role: authStore.user?.role,
|
|
203
|
+
}),
|
|
204
|
+
render: UserDebugTab,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
<DebugView customFeatures={[userDebugTab]}>
|
|
208
|
+
<AppContent />
|
|
209
|
+
</DebugView>;
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
每个自定义 feature 都会变成一个面板 Tab。`name` 是稳定 Tab id,`label` 显示在 Tab 栏,`getSnapshot` 提供展示数据,`render` 控制展示 UI。外部状态变化后需要自动刷新时,给它加 `subscribe`。
|
|
213
|
+
|
|
180
214
|
导航追踪:
|
|
181
215
|
|
|
182
216
|
```tsx
|
|
@@ -206,6 +240,7 @@ addTrackLog({ eventName: 'button_click' });
|
|
|
206
240
|
- `DebugView`
|
|
207
241
|
- `DebugToolkit`
|
|
208
242
|
- `initializeDebugToolkit`
|
|
243
|
+
- `createDebugTab`
|
|
209
244
|
- `createDebugDeviceReport`
|
|
210
245
|
- `checkDaemonConnection`
|
|
211
246
|
- `reportDebugDeviceToDaemon`
|
|
@@ -8,16 +8,11 @@ import androidx.annotation.NonNull;
|
|
|
8
8
|
import androidx.annotation.Nullable;
|
|
9
9
|
|
|
10
10
|
import com.reactnativedebugtoolkit.BuildConfig;
|
|
11
|
-
import com.facebook.react.bridge.Arguments;
|
|
12
11
|
import com.facebook.react.bridge.Promise;
|
|
13
12
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
14
13
|
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
15
14
|
import com.facebook.react.bridge.ReactMethod;
|
|
16
|
-
import com.facebook.react.bridge.UiThreadUtil;
|
|
17
|
-
import com.facebook.react.bridge.WritableMap;
|
|
18
15
|
|
|
19
|
-
import java.lang.reflect.Method;
|
|
20
|
-
import java.lang.reflect.Proxy;
|
|
21
16
|
import java.net.Inet4Address;
|
|
22
17
|
import java.net.InetAddress;
|
|
23
18
|
import java.net.NetworkInterface;
|
|
@@ -25,11 +20,6 @@ import java.util.Enumeration;
|
|
|
25
20
|
|
|
26
21
|
public class DebugToolkitDevConnectModule extends ReactContextBaseJavaModule {
|
|
27
22
|
private static final String MODULE_NAME = "DebugToolkitDevConnect";
|
|
28
|
-
private static final String BUNDLE_ROOT = "index";
|
|
29
|
-
private static final String DEBUG_SERVER_HOST_KEY = "debug_http_host";
|
|
30
|
-
private static final String KOTLIN_FUNCTION1_CLASS = "kotlin.jvm.functions.Function1";
|
|
31
|
-
private static final String APPLY_RELOAD_REASON = "DebugToolkit DevConnect Metro host changed";
|
|
32
|
-
private static final String RESET_RELOAD_REASON = "DebugToolkit DevConnect Metro host reset";
|
|
33
23
|
|
|
34
24
|
public DebugToolkitDevConnectModule(ReactApplicationContext reactContext) {
|
|
35
25
|
super(reactContext);
|
|
@@ -45,183 +35,6 @@ public class DebugToolkitDevConnectModule extends ReactContextBaseJavaModule {
|
|
|
45
35
|
return PreferenceManager.getDefaultSharedPreferences(getReactApplicationContext());
|
|
46
36
|
}
|
|
47
37
|
|
|
48
|
-
@Nullable
|
|
49
|
-
private Object callGetter(Object target, String methodName) {
|
|
50
|
-
try {
|
|
51
|
-
Method method = target.getClass().getMethod(methodName);
|
|
52
|
-
return method.invoke(target);
|
|
53
|
-
} catch (Exception ignored) {
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
private void setDebugServerHost(@Nullable Object devSupportManager, String hostPort) throws Exception {
|
|
59
|
-
if (devSupportManager == null) {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
Object devSettings = callGetter(devSupportManager, "getDevSettings");
|
|
64
|
-
Object packagerConnectionSettings = devSettings == null
|
|
65
|
-
? null
|
|
66
|
-
: callGetter(devSettings, "getPackagerConnectionSettings");
|
|
67
|
-
if (packagerConnectionSettings == null) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
Method setter = packagerConnectionSettings.getClass().getMethod("setDebugServerHost", String.class);
|
|
72
|
-
setter.invoke(packagerConnectionSettings, hostPort);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
private boolean setReactHostBundleSource(Object reactHost, String hostPort) throws Exception {
|
|
76
|
-
if (hostPort.length() == 0) {
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
try {
|
|
81
|
-
Class<?> function1Class = Class.forName(KOTLIN_FUNCTION1_CLASS);
|
|
82
|
-
Method setBundleSourceMethod = reactHost.getClass().getMethod(
|
|
83
|
-
"setBundleSource",
|
|
84
|
-
String.class,
|
|
85
|
-
String.class,
|
|
86
|
-
function1Class
|
|
87
|
-
);
|
|
88
|
-
Object queryMapper = Proxy.newProxyInstance(
|
|
89
|
-
function1Class.getClassLoader(),
|
|
90
|
-
new Class<?>[] { function1Class },
|
|
91
|
-
(proxy, method, args) -> {
|
|
92
|
-
String methodName = method.getName();
|
|
93
|
-
if ("invoke".equals(methodName)) {
|
|
94
|
-
return args != null && args.length > 0 ? args[0] : null;
|
|
95
|
-
}
|
|
96
|
-
if ("toString".equals(methodName)) {
|
|
97
|
-
return "DebugToolkitIdentityQueryMapper";
|
|
98
|
-
}
|
|
99
|
-
if ("hashCode".equals(methodName)) {
|
|
100
|
-
return System.identityHashCode(proxy);
|
|
101
|
-
}
|
|
102
|
-
if ("equals".equals(methodName)) {
|
|
103
|
-
return proxy == (args != null && args.length > 0 ? args[0] : null);
|
|
104
|
-
}
|
|
105
|
-
return null;
|
|
106
|
-
}
|
|
107
|
-
);
|
|
108
|
-
setBundleSourceMethod.invoke(reactHost, hostPort, BUNDLE_ROOT, queryMapper);
|
|
109
|
-
return true;
|
|
110
|
-
} catch (ClassNotFoundException | NoSuchMethodException ignored) {
|
|
111
|
-
return false;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
private boolean triggerDevSupportReload(
|
|
116
|
-
@Nullable Object devSupportManager,
|
|
117
|
-
String hostPort
|
|
118
|
-
) throws Exception {
|
|
119
|
-
if (devSupportManager == null) {
|
|
120
|
-
return false;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
Object enabled = callGetter(devSupportManager, "getDevSupportEnabled");
|
|
124
|
-
if (enabled instanceof Boolean && !((Boolean) enabled)) {
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
setDebugServerHost(devSupportManager, hostPort);
|
|
129
|
-
Method reloadMethod = devSupportManager.getClass().getMethod("handleReloadJS");
|
|
130
|
-
reloadMethod.invoke(devSupportManager);
|
|
131
|
-
return true;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
private boolean reloadFromReactHost(
|
|
135
|
-
Context applicationContext,
|
|
136
|
-
String reason,
|
|
137
|
-
String hostPort
|
|
138
|
-
) throws Exception {
|
|
139
|
-
Object reactHost = callGetter(applicationContext, "getReactHost");
|
|
140
|
-
if (reactHost == null) {
|
|
141
|
-
return false;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (setReactHostBundleSource(reactHost, hostPort)) {
|
|
145
|
-
return true;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (triggerDevSupportReload(callGetter(reactHost, "getDevSupportManager"), hostPort)) {
|
|
149
|
-
return true;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
Method reloadMethod = reactHost.getClass().getMethod("reload", String.class);
|
|
153
|
-
reloadMethod.invoke(reactHost, reason);
|
|
154
|
-
return true;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
private boolean reloadFromReactNativeHost(Context applicationContext, String hostPort) throws Exception {
|
|
158
|
-
Object reactNativeHost = callGetter(applicationContext, "getReactNativeHost");
|
|
159
|
-
Object instanceManager = reactNativeHost == null
|
|
160
|
-
? null
|
|
161
|
-
: callGetter(reactNativeHost, "getReactInstanceManager");
|
|
162
|
-
Object devSupportManager = instanceManager == null
|
|
163
|
-
? null
|
|
164
|
-
: callGetter(instanceManager, "getDevSupportManager");
|
|
165
|
-
return triggerDevSupportReload(devSupportManager, hostPort);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
private void resolveAfterReload(
|
|
169
|
-
String reason,
|
|
170
|
-
@Nullable WritableMap result,
|
|
171
|
-
String hostPort,
|
|
172
|
-
Promise promise
|
|
173
|
-
) {
|
|
174
|
-
UiThreadUtil.runOnUiThread(() -> {
|
|
175
|
-
try {
|
|
176
|
-
Context applicationContext = getReactApplicationContext().getApplicationContext();
|
|
177
|
-
boolean reloaded = reloadFromReactHost(applicationContext, reason, hostPort)
|
|
178
|
-
|| reloadFromReactNativeHost(applicationContext, hostPort);
|
|
179
|
-
if (!reloaded) {
|
|
180
|
-
promise.reject("reload_unavailable", "Unable to trigger React Native reload after updating Metro host.");
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
promise.resolve(result);
|
|
184
|
-
} catch (Exception error) {
|
|
185
|
-
promise.reject("reload_failed", "Unable to trigger React Native reload after updating Metro host.", error);
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
@ReactMethod
|
|
191
|
-
public void getMetroHost(Promise promise) {
|
|
192
|
-
@Nullable String host = getPreferences().getString(DEBUG_SERVER_HOST_KEY, null);
|
|
193
|
-
promise.resolve(host);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
@ReactMethod
|
|
197
|
-
public void applyMetroHost(String hostPort, Promise promise) {
|
|
198
|
-
if (hostPort == null || hostPort.length() == 0) {
|
|
199
|
-
promise.reject("invalid_host", "Metro host cannot be empty.");
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
boolean stored = getPreferences().edit().putString(DEBUG_SERVER_HOST_KEY, hostPort).commit();
|
|
204
|
-
if (!stored) {
|
|
205
|
-
promise.reject("storage_failed", "Unable to persist Metro host.");
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
WritableMap result = Arguments.createMap();
|
|
210
|
-
result.putString("hostPort", hostPort);
|
|
211
|
-
resolveAfterReload(APPLY_RELOAD_REASON, result, hostPort, promise);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
@ReactMethod
|
|
215
|
-
public void resetMetroHost(Promise promise) {
|
|
216
|
-
boolean stored = getPreferences().edit().remove(DEBUG_SERVER_HOST_KEY).commit();
|
|
217
|
-
if (!stored) {
|
|
218
|
-
promise.reject("storage_failed", "Unable to reset Metro host.");
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
resolveAfterReload(RESET_RELOAD_REASON, null, "", promise);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
38
|
@ReactMethod
|
|
226
39
|
public void getPreference(String key, Promise promise) {
|
|
227
40
|
@Nullable String value = getPreferences().getString(key, null);
|
package/bin/debug-toolkit.js
CHANGED
|
@@ -27,21 +27,12 @@ function hasHelpFlag(args) {
|
|
|
27
27
|
function printHelp() {
|
|
28
28
|
process.stderr.write(
|
|
29
29
|
'Usage: debug-toolkit [--host 0.0.0.0] [--port 3799] [--token dev-token] [--store ~/.react-native-debug-toolkit/daemon-devices.json] [--daemon-only]\n'
|
|
30
|
-
+ ' debug-toolkit embed [--platform ios|android] [--undo] [--yes]\n'
|
|
31
30
|
+ '\n'
|
|
32
31
|
+ 'Starts the debug toolkit: daemon (HTTP + Web Console) and MCP stdio server.\n'
|
|
33
32
|
+ '\n'
|
|
34
|
-
+ 'Commands:\n'
|
|
35
|
-
+ ' embed Embed JS bundle in debug builds (run in host app root)\n'
|
|
36
|
-
+ '\n'
|
|
37
|
-
+ 'Embed options:\n'
|
|
38
|
-
+ ' --platform <p> Target platform: ios or android (default: both)\n'
|
|
39
|
-
+ ' --undo Remove embed injections\n'
|
|
40
|
-
+ ' --yes Skip confirmations (CI/EAS)\n'
|
|
41
|
-
+ '\n'
|
|
42
33
|
+ 'Daemon options:\n'
|
|
43
34
|
+ ' --host <addr> Host to bind (default: 0.0.0.0)\n'
|
|
44
|
-
|
|
35
|
+
+ ' --port <port> Port to bind (default: 3799)\n'
|
|
45
36
|
+ ' --token <str> Auth token for daemon endpoints\n'
|
|
46
37
|
+ ' --store <path> Device log store path\n'
|
|
47
38
|
+ ' --daemon-only Start only the HTTP daemon and Web Console\n'
|
|
@@ -66,12 +57,6 @@ async function main() {
|
|
|
66
57
|
return;
|
|
67
58
|
}
|
|
68
59
|
|
|
69
|
-
// Route embed subcommand
|
|
70
|
-
if (args[0] === 'embed') {
|
|
71
|
-
const { main: embedMain } = require('../scripts/embed');
|
|
72
|
-
return embedMain(args.slice(1));
|
|
73
|
-
}
|
|
74
|
-
|
|
75
60
|
const host = readOption(args, '--host', process.env.DEBUG_TOOLKIT_DAEMON_HOST || DEFAULT_HOST);
|
|
76
61
|
const port = Number(readOption(args, '--port', process.env.DEBUG_TOOLKIT_DAEMON_PORT || DEFAULT_PORT));
|
|
77
62
|
const token = readOption(args, '--token', process.env.DEBUG_TOOLKIT_DAEMON_TOKEN || '');
|
|
@@ -2,16 +2,4 @@
|
|
|
2
2
|
|
|
3
3
|
NS_ASSUME_NONNULL_BEGIN
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
Returns a Metro bundle URL when the user has applied a host via DevConnect; otherwise nil.
|
|
7
|
-
|
|
8
|
-
Zero-config: installing this pod hooks RCTBundleURLProvider so cold start uses the embedded
|
|
9
|
-
main.jsbundle until DevConnect applies a host. You do not need to change AppDelegate.
|
|
10
|
-
|
|
11
|
-
Optional override for custom bundleURL() implementations:
|
|
12
|
-
|
|
13
|
-
if let metro = DebugToolkitMetroBundleURL() { return metro }
|
|
14
|
-
*/
|
|
15
|
-
FOUNDATION_EXPORT NSURL * _Nullable DebugToolkitMetroBundleURL(void);
|
|
16
|
-
|
|
17
5
|
NS_ASSUME_NONNULL_END
|