react-native-debug-toolkit 3.2.9 → 3.3.1
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 +87 -74
- package/package.json +1 -1
|
@@ -5,11 +5,46 @@
|
|
|
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
|
+
NSString *urlStr = [NSString stringWithFormat:
|
|
25
|
+
@"http://%@/%@.bundle?platform=ios&dev=true&minify=false&lazy=true", metroHost, DebugToolkitBundleRoot];
|
|
26
|
+
NSURL *url = [NSURL URLWithString:urlStr];
|
|
27
|
+
NSLog(@"[DevConnect] swizzle: returning Metro URL %@ (host=%@)", url, metroHost);
|
|
28
|
+
return url;
|
|
29
|
+
}
|
|
30
|
+
if (original_sourceURLForBridge) {
|
|
31
|
+
return ((NSURL *(*)(id, SEL, RCTBridge *))original_sourceURLForBridge)(self, _cmd, bridge);
|
|
32
|
+
}
|
|
33
|
+
return nil;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static void swizzleSourceURLForBridge(Class targetClass)
|
|
37
|
+
{
|
|
38
|
+
if (!targetClass) return;
|
|
39
|
+
SEL selector = @selector(sourceURLForBridge:);
|
|
40
|
+
Method method = class_getInstanceMethod(targetClass, selector);
|
|
41
|
+
if (!method) return;
|
|
42
|
+
if (original_sourceURLForBridge) return; // Already swizzled
|
|
43
|
+
original_sourceURLForBridge = method_setImplementation(method, (IMP)devconnect_sourceURLForBridge);
|
|
44
|
+
NSLog(@"[DevConnect] swizzled sourceURLForBridge: on %@", NSStringFromClass(targetClass));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
#pragma mark - Module
|
|
13
48
|
|
|
14
49
|
@interface DebugToolkitDevConnect : NSObject <RCTBridgeModule>
|
|
15
50
|
@end
|
|
@@ -21,6 +56,25 @@ RCT_EXPORT_MODULE(DebugToolkitDevConnect)
|
|
|
21
56
|
@synthesize bridge = _bridge;
|
|
22
57
|
@synthesize bundleManager = _bundleManager;
|
|
23
58
|
|
|
59
|
+
__attribute__((constructor))
|
|
60
|
+
static void devconnect_swizzle_init(void)
|
|
61
|
+
{
|
|
62
|
+
// Runs after all +load methods, before main() — same timing as +load but no conflict with RCT_EXPORT_MODULE
|
|
63
|
+
swizzleSourceURLForBridge(NSClassFromString(@"AppDelegate"));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
- (instancetype)init
|
|
67
|
+
{
|
|
68
|
+
if ((self = [super init])) {
|
|
69
|
+
// Fallback — use actual delegate class at runtime (covers custom class names / Swift)
|
|
70
|
+
if (!original_sourceURLForBridge) {
|
|
71
|
+
Class delegateClass = object_getClass([UIApplication sharedApplication].delegate);
|
|
72
|
+
swizzleSourceURLForBridge(delegateClass);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return self;
|
|
76
|
+
}
|
|
77
|
+
|
|
24
78
|
+ (BOOL)requiresMainQueueSetup
|
|
25
79
|
{
|
|
26
80
|
return NO;
|
|
@@ -44,42 +98,6 @@ RCT_EXPORT_MODULE(DebugToolkitDevConnect)
|
|
|
44
98
|
return nil;
|
|
45
99
|
}
|
|
46
100
|
|
|
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
101
|
#pragma mark - Exported Methods
|
|
84
102
|
|
|
85
103
|
RCT_EXPORT_METHOD(getMetroHost:(RCTPromiseResolveBlock)resolve
|
|
@@ -98,8 +116,6 @@ RCT_EXPORT_METHOD(applyMetroHost:(NSString *)hostPort
|
|
|
98
116
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
99
117
|
{
|
|
100
118
|
@try {
|
|
101
|
-
NSLog(@"[DevConnect] applyMetroHost called with: %@", hostPort);
|
|
102
|
-
|
|
103
119
|
if (hostPort.length == 0) {
|
|
104
120
|
reject(@"invalid_host", @"Metro host cannot be empty.", nil);
|
|
105
121
|
return;
|
|
@@ -107,6 +123,7 @@ RCT_EXPORT_METHOD(applyMetroHost:(NSString *)hostPort
|
|
|
107
123
|
|
|
108
124
|
RCTBundleURLProvider *settings = [RCTBundleURLProvider sharedSettings];
|
|
109
125
|
|
|
126
|
+
// Parse host and port
|
|
110
127
|
NSRange separator = [hostPort rangeOfString:@":" options:NSBackwardsSearch];
|
|
111
128
|
NSString *host = separator.location == NSNotFound
|
|
112
129
|
? hostPort
|
|
@@ -124,61 +141,49 @@ RCT_EXPORT_METHOD(applyMetroHost:(NSString *)hostPort
|
|
|
124
141
|
formatter.numberStyle = NSNumberFormatterDecimalStyle;
|
|
125
142
|
NSNumber *portNumber = [formatter numberFromString:port];
|
|
126
143
|
if (portNumber == nil) {
|
|
144
|
+
#ifdef RCT_METRO_PORT
|
|
127
145
|
portNumber = [NSNumber numberWithInt:RCT_METRO_PORT];
|
|
146
|
+
#else
|
|
147
|
+
portNumber = [NSNumber numberWithInt:8081];
|
|
148
|
+
#endif
|
|
128
149
|
}
|
|
129
150
|
|
|
130
151
|
NSString *normalizedHostPort = [NSString stringWithFormat:@"%@:%d", host, portNumber.intValue];
|
|
131
|
-
NSLog(@"[DevConnect] normalizedHostPort: %@", normalizedHostPort);
|
|
132
152
|
|
|
153
|
+
// Persist for AppDelegate swizzle (Release mode + restart)
|
|
154
|
+
[[NSUserDefaults standardUserDefaults] setObject:normalizedHostPort forKey:kDevConnectMetroHost];
|
|
155
|
+
[[NSUserDefaults standardUserDefaults] synchronize];
|
|
156
|
+
|
|
157
|
+
// Also set jsLocation (Debug mode + hot reload)
|
|
133
158
|
settings.jsLocation = normalizedHostPort;
|
|
134
|
-
NSLog(@"[DevConnect] jsLocation set, verify: %@", settings.jsLocation);
|
|
135
159
|
|
|
160
|
+
// Try hot reload via bundleManager (works in Debug)
|
|
136
161
|
NSURL *bundleURL = nil;
|
|
137
162
|
if (DebugToolkitBundleRoot.length > 0) {
|
|
138
163
|
bundleURL = [settings jsBundleURLForBundleRoot:DebugToolkitBundleRoot];
|
|
139
164
|
}
|
|
140
|
-
NSLog(@"[DevConnect] generated bundleURL: %@", bundleURL);
|
|
141
|
-
|
|
142
165
|
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
166
|
if (bm) {
|
|
150
|
-
|
|
167
|
+
bm.bundleURL = bundleURL;
|
|
151
168
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
@"hostPort" : normalizedHostPort ?: @"",
|
|
156
|
-
@"bundleManagerInjected" : _bundleManager ? @"YES" : @"nil",
|
|
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";
|
|
169
|
+
#ifdef RCTReloadCommandSetBundleURL
|
|
170
|
+
else if (bundleURL) {
|
|
171
|
+
RCTReloadCommandSetBundleURL(bundleURL);
|
|
164
172
|
}
|
|
165
|
-
|
|
173
|
+
#endif
|
|
174
|
+
|
|
175
|
+
NSLog(@"[DevConnect] applyMetroHost: %@ | bm=%@ | bridge=%@ | url=%@",
|
|
176
|
+
normalizedHostPort, bm ? @"YES" : @"nil", _bridge ? @"YES" : @"nil", bundleURL);
|
|
177
|
+
|
|
178
|
+
RCTTriggerReloadCommandListeners(@"Dev menu - apply changes");
|
|
166
179
|
|
|
167
180
|
NSMutableDictionary *result = [@{@"hostPort" : normalizedHostPort} mutableCopy];
|
|
168
181
|
if (bm && bm.bundleURL.absoluteString) {
|
|
169
182
|
result[@"bundleURL"] = bm.bundleURL.absoluteString;
|
|
170
183
|
}
|
|
171
|
-
|
|
172
|
-
NSLog(@"[DevConnect] triggering reload...");
|
|
173
|
-
RCTTriggerReloadCommandListeners(@"Dev menu - apply changes");
|
|
174
184
|
resolve(result);
|
|
175
185
|
} @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
|
-
}];
|
|
186
|
+
NSLog(@"[DevConnect] applyMetroHost EXCEPTION: %@", exception.reason);
|
|
182
187
|
reject(@"native_error", exception.reason, nil);
|
|
183
188
|
}
|
|
184
189
|
}
|
|
@@ -187,13 +192,21 @@ RCT_EXPORT_METHOD(resetMetroHost:(RCTPromiseResolveBlock)resolve
|
|
|
187
192
|
rejecter:(__unused RCTPromiseRejectBlock)reject)
|
|
188
193
|
{
|
|
189
194
|
@try {
|
|
190
|
-
|
|
195
|
+
// Clear stored Metro host
|
|
196
|
+
[[NSUserDefaults standardUserDefaults] removeObjectForKey:kDevConnectMetroHost];
|
|
197
|
+
[[NSUserDefaults standardUserDefaults] synchronize];
|
|
198
|
+
|
|
199
|
+
// Reset RN settings
|
|
191
200
|
RCTBundleURLProvider *settings = [RCTBundleURLProvider sharedSettings];
|
|
192
201
|
[settings resetToDefaults];
|
|
193
202
|
NSURL *bundleURL = [settings jsBundleURLForFallbackExtension:nil];
|
|
194
|
-
NSLog(@"[DevConnect] reset bundleURL: %@", bundleURL);
|
|
195
203
|
|
|
196
|
-
[self
|
|
204
|
+
RCTBundleManager *bm = [self resolveBundleManager];
|
|
205
|
+
if (bm) {
|
|
206
|
+
bm.bundleURL = bundleURL;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
NSLog(@"[DevConnect] resetMetroHost | bm=%@ | url=%@", bm ? @"YES" : @"nil", bundleURL);
|
|
197
210
|
RCTTriggerReloadCommandListeners(@"Dev menu - reset to default");
|
|
198
211
|
resolve([NSNull null]);
|
|
199
212
|
} @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.1",
|
|
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",
|