1// Copyright 2015-present 650 Industries. All rights reserved.
2
3#import <EXDevMenu/DevMenuRCTBridge.h>
4
5// The search path for the Swift generated headers are different
6// between use_frameworks and non_use_frameworks mode.
7#if __has_include(<EXDevMenuInterface/EXDevMenuInterface-Swift.h>)
8#import <EXDevMenuInterface/EXDevMenuInterface-Swift.h>
9#else
10#import <EXDevMenuInterface-Swift.h>
11#endif
12#if __has_include(<ExpoModulesCore/ExpoModulesCore-Swift.h>)
13#import <ExpoModulesCore/ExpoModulesCore-Swift.h>
14#else
15#import <ExpoModulesCore-Swift.h>
16#endif
17#if __has_include(<EXDevMenu/EXDevMenu-Swift.h>)
18#import <EXDevMenu/EXDevMenu-Swift.h>
19#else
20#import <EXDevMenu-Swift.h>
21#endif
22#import <RCTCxxBridge+Private.h>
23
24#import <React/RCTPerformanceLogger.h>
25#import <React/RCTDevSettings.h>
26#import <React/RCTBridge+Private.h>
27#import <React/RCTDevMenu.h>
28#import <React/RCTCxxBridgeDelegate.h>
29#import <React/RCTJSIExecutorRuntimeInstaller.h>
30#if __has_include(<reacthermes/HermesExecutorFactory.h>)
31#import <reacthermes/HermesExecutorFactory.h>
32#endif
33
34@implementation DevMenuRCTCxxBridge
35
36- (instancetype)initWithParentBridge:(RCTBridge *)bridge
37{
38  if ((self = [super initWithParentBridge:bridge])) {
39    RCTBridge *appBridge = DevMenuManager.shared.currentBridge;
40    // reset the singleton `RCTBridge.currentBridge` to app bridge instance
41    RCTBridge.currentBridge = appBridge != nil ? appBridge.batchedBridge : nil;
42  }
43  return self;
44}
45
46/**
47 * Theoretically, we could overwrite the `RCTDevSettings` module by exporting our version through the bridge.
48 * However, this won't work with the js remote debugging. For some reason, the RN needs to initialized remote tools very early. So it always uses the default module to do it.
49 * When we export our module, it won't be used in the initialized phase. So the dev-menu will start with remote debug support.
50 */
51- (RCTDevSettings *)devSettings
52{
53  // uncomment below to enable fast refresh for development builds of DevMenu
54  //  return super.devSettings;
55  return nil;
56}
57
58- (RCTDevMenu *)devMenu
59{
60  return nil;
61}
62
63- (NSArray<Class> *)filterModuleList:(NSArray<Class> *)modules
64{
65  NSArray<NSString *> *allowedModules = @[@"RCT", @"ExpoBridgeModule", @"EXNativeModulesProxy", @"EXReactNativeEventEmitter"];
66  NSArray<Class> *filteredModuleList = [modules filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id  _Nullable clazz, NSDictionary<NSString *,id> * _Nullable bindings) {
67    NSString* clazzName = NSStringFromClass(clazz);
68
69    if ([clazz conformsToProtocol:@protocol(EXDevExtensionProtocol)]) {
70      return true;
71    }
72
73    for (NSString *allowedModule in allowedModules) {
74      if ([clazzName hasPrefix:allowedModule]) {
75        return true;
76      }
77    }
78    return false;
79  }]];
80
81  return filteredModuleList;
82}
83
84- (NSArray<RCTModuleData *> *)_initializeModules:(NSArray<Class> *)modules
85                               withDispatchGroup:(dispatch_group_t)dispatchGroup
86                                lazilyDiscovered:(BOOL)lazilyDiscovered
87{
88  NSArray<Class> *filteredModuleList = [self filterModuleList: modules];
89  return [super _initializeModules:filteredModuleList withDispatchGroup:dispatchGroup lazilyDiscovered:lazilyDiscovered];
90}
91
92@end
93
94@implementation DevMenuRCTBridge
95
96- (Class)bridgeClass
97{
98  return [DevMenuRCTCxxBridge class];
99}
100
101#pragma clang diagnostic push
102#pragma clang diagnostic ignored "-Wdeprecated-implementations"
103// This method is still used so we need to override it even if it's deprecated
104- (void)reloadWithReason:(NSString *)reason {}
105#pragma clang diagnostic pop
106
107@end
108
109@interface DevMenuRCTCxxBridgeDelegate () <RCTCxxBridgeDelegate>
110
111@end
112
113@implementation DevMenuRCTCxxBridgeDelegate
114
115- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
116{
117#if __has_include(<reacthermes/HermesExecutorFactory.h>)
118  // Disable Hermes debugger to prevent Hermes debugger uses dev-menu
119  // as inspecting target.
120  auto installBindings = facebook::react::RCTJSIExecutorRuntimeInstaller(nullptr);
121  auto *hermesExecutorFactory = new facebook::react::HermesExecutorFactory(installBindings);
122  hermesExecutorFactory->setEnableDebugger(false);
123  std::unique_ptr<facebook::react::JSExecutorFactory> jsExecutorFactory(hermesExecutorFactory);
124  return std::move(jsExecutorFactory);
125#else
126  return nullptr;
127#endif
128}
129
130@end
131