react-native-debug-toolkit 3.2.9 → 3.3.0
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/ios/DebugToolkitDevConnect.mm +81 -74
- package/package.json +1 -1
|
@@ -5,11 +5,48 @@
|
|
|
5
5
|
#import <React/RCTBundleURLProvider.h>
|
|
6
6
|
#import <React/RCTDefines.h>
|
|
7
7
|
#import <React/RCTReloadCommand.h>
|
|
8
|
+
#import <objc/runtime.h>
|
|
8
9
|
#include <ifaddrs.h>
|
|
9
10
|
#include <arpa/inet.h>
|
|
10
11
|
#include <net/if.h>
|
|
11
12
|
|
|
12
13
|
static NSString *const DebugToolkitBundleRoot = @"index";
|
|
14
|
+
static NSString *const kDevConnectMetroHost = @"_devconnect_metro_host";
|
|
15
|
+
|
|
16
|
+
#pragma mark - AppDelegate Swizzling
|
|
17
|
+
|
|
18
|
+
static IMP original_sourceURLForBridge = NULL;
|
|
19
|
+
|
|
20
|
+
static NSURL *devconnect_sourceURLForBridge(id self, SEL _cmd, RCTBridge *bridge)
|
|
21
|
+
{
|
|
22
|
+
NSString *metroHost = [[NSUserDefaults standardUserDefaults] stringForKey:kDevConnectMetroHost];
|
|
23
|
+
if (metroHost.length > 0) {
|
|
24
|
+
RCTBundleURLProvider *settings = [RCTBundleURLProvider sharedSettings];
|
|
25
|
+
NSString *saved = settings.jsLocation;
|
|
26
|
+
settings.jsLocation = metroHost;
|
|
27
|
+
NSURL *url = [settings jsBundleURLForBundleRoot:DebugToolkitBundleRoot];
|
|
28
|
+
settings.jsLocation = saved;
|
|
29
|
+
NSLog(@"[DevConnect] swizzle: returning Metro URL %@ (host=%@)", url, metroHost);
|
|
30
|
+
return url;
|
|
31
|
+
}
|
|
32
|
+
if (original_sourceURLForBridge) {
|
|
33
|
+
return ((NSURL *(*)(id, SEL, RCTBridge *))original_sourceURLForBridge)(self, _cmd, bridge);
|
|
34
|
+
}
|
|
35
|
+
return nil;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static void swizzleSourceURLForBridge(Class targetClass)
|
|
39
|
+
{
|
|
40
|
+
if (!targetClass) return;
|
|
41
|
+
SEL selector = @selector(sourceURLForBridge:);
|
|
42
|
+
Method method = class_getInstanceMethod(targetClass, selector);
|
|
43
|
+
if (!method) return;
|
|
44
|
+
if (original_sourceURLForBridge) return; // Already swizzled
|
|
45
|
+
original_sourceURLForBridge = method_setImplementation(method, (IMP)devconnect_sourceURLForBridge);
|
|
46
|
+
NSLog(@"[DevConnect] swizzled sourceURLForBridge: on %@", NSStringFromClass(targetClass));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
#pragma mark - Module
|
|
13
50
|
|
|
14
51
|
@interface DebugToolkitDevConnect : NSObject <RCTBridgeModule>
|
|
15
52
|
@end
|
|
@@ -21,6 +58,24 @@ RCT_EXPORT_MODULE(DebugToolkitDevConnect)
|
|
|
21
58
|
@synthesize bridge = _bridge;
|
|
22
59
|
@synthesize bundleManager = _bundleManager;
|
|
23
60
|
|
|
61
|
+
+ (void)load
|
|
62
|
+
{
|
|
63
|
+
// Stage 1: Try standard AppDelegate class name (covers ObjC and most Swift apps)
|
|
64
|
+
swizzleSourceURLForBridge(NSClassFromString(@"AppDelegate"));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
- (instancetype)init
|
|
68
|
+
{
|
|
69
|
+
if ((self = [super init])) {
|
|
70
|
+
// Stage 2: Fallback — use actual delegate class at runtime
|
|
71
|
+
if (!original_sourceURLForBridge) {
|
|
72
|
+
Class delegateClass = object_getClass([UIApplication sharedApplication].delegate);
|
|
73
|
+
swizzleSourceURLForBridge(delegateClass);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return self;
|
|
77
|
+
}
|
|
78
|
+
|
|
24
79
|
+ (BOOL)requiresMainQueueSetup
|
|
25
80
|
{
|
|
26
81
|
return NO;
|
|
@@ -44,42 +99,6 @@ RCT_EXPORT_MODULE(DebugToolkitDevConnect)
|
|
|
44
99
|
return nil;
|
|
45
100
|
}
|
|
46
101
|
|
|
47
|
-
- (void)saveDiagnostic:(NSDictionary *)info
|
|
48
|
-
{
|
|
49
|
-
NSData *data = [NSJSONSerialization dataWithJSONObject:info options:0 error:nil];
|
|
50
|
-
if (data) {
|
|
51
|
-
NSString *json = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
52
|
-
[[NSUserDefaults standardUserDefaults] setObject:json forKey:@"_devconnect_last_diagnostic"];
|
|
53
|
-
[[NSUserDefaults standardUserDefaults] synchronize];
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
#pragma mark - Apply Bundle URL (multi-strategy)
|
|
58
|
-
|
|
59
|
-
- (BOOL)applyBundleURL:(NSURL *)bundleURL
|
|
60
|
-
{
|
|
61
|
-
RCTBundleManager *bm = [self resolveBundleManager];
|
|
62
|
-
if (bm) {
|
|
63
|
-
if (bundleURL) {
|
|
64
|
-
bm.bundleURL = bundleURL;
|
|
65
|
-
NSLog(@"[DevConnect] applyBundleURL strategy 1: set bm.bundleURL = %@", bundleURL);
|
|
66
|
-
} else {
|
|
67
|
-
[bm resetBundleURL];
|
|
68
|
-
NSLog(@"[DevConnect] applyBundleURL strategy 1: resetBundleURL");
|
|
69
|
-
}
|
|
70
|
-
return YES;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (bundleURL) {
|
|
74
|
-
RCTReloadCommandSetBundleURL(bundleURL);
|
|
75
|
-
NSLog(@"[DevConnect] applyBundleURL strategy 2: RCTReloadCommandSetBundleURL = %@", bundleURL);
|
|
76
|
-
return YES;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
NSLog(@"[DevConnect] applyBundleURL strategy 3: no URL, relying on jsLocation persistence");
|
|
80
|
-
return NO;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
102
|
#pragma mark - Exported Methods
|
|
84
103
|
|
|
85
104
|
RCT_EXPORT_METHOD(getMetroHost:(RCTPromiseResolveBlock)resolve
|
|
@@ -98,8 +117,6 @@ RCT_EXPORT_METHOD(applyMetroHost:(NSString *)hostPort
|
|
|
98
117
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
99
118
|
{
|
|
100
119
|
@try {
|
|
101
|
-
NSLog(@"[DevConnect] applyMetroHost called with: %@", hostPort);
|
|
102
|
-
|
|
103
120
|
if (hostPort.length == 0) {
|
|
104
121
|
reject(@"invalid_host", @"Metro host cannot be empty.", nil);
|
|
105
122
|
return;
|
|
@@ -107,6 +124,7 @@ RCT_EXPORT_METHOD(applyMetroHost:(NSString *)hostPort
|
|
|
107
124
|
|
|
108
125
|
RCTBundleURLProvider *settings = [RCTBundleURLProvider sharedSettings];
|
|
109
126
|
|
|
127
|
+
// Parse host and port
|
|
110
128
|
NSRange separator = [hostPort rangeOfString:@":" options:NSBackwardsSearch];
|
|
111
129
|
NSString *host = separator.location == NSNotFound
|
|
112
130
|
? hostPort
|
|
@@ -128,57 +146,38 @@ RCT_EXPORT_METHOD(applyMetroHost:(NSString *)hostPort
|
|
|
128
146
|
}
|
|
129
147
|
|
|
130
148
|
NSString *normalizedHostPort = [NSString stringWithFormat:@"%@:%d", host, portNumber.intValue];
|
|
131
|
-
NSLog(@"[DevConnect] normalizedHostPort: %@", normalizedHostPort);
|
|
132
149
|
|
|
150
|
+
// Persist for AppDelegate swizzle (Release mode + restart)
|
|
151
|
+
[[NSUserDefaults standardUserDefaults] setObject:normalizedHostPort forKey:kDevConnectMetroHost];
|
|
152
|
+
[[NSUserDefaults standardUserDefaults] synchronize];
|
|
153
|
+
|
|
154
|
+
// Also set jsLocation (Debug mode + hot reload)
|
|
133
155
|
settings.jsLocation = normalizedHostPort;
|
|
134
|
-
NSLog(@"[DevConnect] jsLocation set, verify: %@", settings.jsLocation);
|
|
135
156
|
|
|
157
|
+
// Try hot reload via bundleManager (works in Debug)
|
|
136
158
|
NSURL *bundleURL = nil;
|
|
137
159
|
if (DebugToolkitBundleRoot.length > 0) {
|
|
138
160
|
bundleURL = [settings jsBundleURLForBundleRoot:DebugToolkitBundleRoot];
|
|
139
161
|
}
|
|
140
|
-
NSLog(@"[DevConnect] generated bundleURL: %@", bundleURL);
|
|
141
|
-
|
|
142
162
|
RCTBundleManager *bm = [self resolveBundleManager];
|
|
143
|
-
BOOL applied = [self applyBundleURL:bundleURL];
|
|
144
|
-
NSLog(@"[DevConnect] resolveBundleManager: %@", bm ? @"found" : @"nil");
|
|
145
|
-
NSLog(@"[DevConnect] applyBundleURL result: %@", applied ? @"YES" : @"NO");
|
|
146
|
-
NSLog(@"[DevConnect] _bundleManager injected: %@", _bundleManager ? @"YES" : @"nil");
|
|
147
|
-
NSLog(@"[DevConnect] _bridge available: %@", _bridge ? @"YES" : @"nil");
|
|
148
|
-
|
|
149
163
|
if (bm) {
|
|
150
|
-
|
|
164
|
+
bm.bundleURL = bundleURL;
|
|
165
|
+
} else if (bundleURL) {
|
|
166
|
+
RCTReloadCommandSetBundleURL(bundleURL);
|
|
151
167
|
}
|
|
152
168
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
@"bridgeAvailable" : _bridge ? @"YES" : @"nil",
|
|
158
|
-
@"resolvedBM" : bm ? @"YES" : @"nil",
|
|
159
|
-
@"bundleURL" : bundleURL.absoluteString ?: @"nil",
|
|
160
|
-
@"applied" : applied ? @"YES" : @"NO",
|
|
161
|
-
} mutableCopy];
|
|
162
|
-
if (bm) {
|
|
163
|
-
diagnostic[@"bmBundleURL"] = bm.bundleURL.absoluteString ?: @"nil";
|
|
164
|
-
}
|
|
165
|
-
[self saveDiagnostic:diagnostic];
|
|
169
|
+
NSLog(@"[DevConnect] applyMetroHost: %@ | bm=%@ | bridge=%@ | url=%@",
|
|
170
|
+
normalizedHostPort, bm ? @"YES" : @"nil", _bridge ? @"YES" : @"nil", bundleURL);
|
|
171
|
+
|
|
172
|
+
RCTTriggerReloadCommandListeners(@"Dev menu - apply changes");
|
|
166
173
|
|
|
167
174
|
NSMutableDictionary *result = [@{@"hostPort" : normalizedHostPort} mutableCopy];
|
|
168
175
|
if (bm && bm.bundleURL.absoluteString) {
|
|
169
176
|
result[@"bundleURL"] = bm.bundleURL.absoluteString;
|
|
170
177
|
}
|
|
171
|
-
|
|
172
|
-
NSLog(@"[DevConnect] triggering reload...");
|
|
173
|
-
RCTTriggerReloadCommandListeners(@"Dev menu - apply changes");
|
|
174
178
|
resolve(result);
|
|
175
179
|
} @catch (NSException *exception) {
|
|
176
|
-
NSLog(@"[DevConnect] EXCEPTION: %@
|
|
177
|
-
// Save exception diagnostic
|
|
178
|
-
[self saveDiagnostic:@{
|
|
179
|
-
@"error" : exception.reason ?: @"unknown",
|
|
180
|
-
@"exception" : exception.name ?: @"unknown",
|
|
181
|
-
}];
|
|
180
|
+
NSLog(@"[DevConnect] applyMetroHost EXCEPTION: %@", exception.reason);
|
|
182
181
|
reject(@"native_error", exception.reason, nil);
|
|
183
182
|
}
|
|
184
183
|
}
|
|
@@ -187,13 +186,21 @@ RCT_EXPORT_METHOD(resetMetroHost:(RCTPromiseResolveBlock)resolve
|
|
|
187
186
|
rejecter:(__unused RCTPromiseRejectBlock)reject)
|
|
188
187
|
{
|
|
189
188
|
@try {
|
|
190
|
-
|
|
189
|
+
// Clear stored Metro host
|
|
190
|
+
[[NSUserDefaults standardUserDefaults] removeObjectForKey:kDevConnectMetroHost];
|
|
191
|
+
[[NSUserDefaults standardUserDefaults] synchronize];
|
|
192
|
+
|
|
193
|
+
// Reset RN settings
|
|
191
194
|
RCTBundleURLProvider *settings = [RCTBundleURLProvider sharedSettings];
|
|
192
195
|
[settings resetToDefaults];
|
|
193
196
|
NSURL *bundleURL = [settings jsBundleURLForFallbackExtension:nil];
|
|
194
|
-
NSLog(@"[DevConnect] reset bundleURL: %@", bundleURL);
|
|
195
197
|
|
|
196
|
-
[self
|
|
198
|
+
RCTBundleManager *bm = [self resolveBundleManager];
|
|
199
|
+
if (bm) {
|
|
200
|
+
bm.bundleURL = bundleURL;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
NSLog(@"[DevConnect] resetMetroHost | bm=%@ | url=%@", bm ? @"YES" : @"nil", bundleURL);
|
|
197
204
|
RCTTriggerReloadCommandListeners(@"Dev menu - reset to default");
|
|
198
205
|
resolve([NSNull null]);
|
|
199
206
|
} @catch (NSException *exception) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-debug-toolkit",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "A local-first React Native debug toolkit with Web Console, HTTP API, and MCP support for AI-readable app logs",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|