diff --git a/Libraries/AppDelegate/RCTAppDelegate.mm b/Libraries/AppDelegate/RCTAppDelegate.mm index f1e0dee830390d..92c6603af13d2b 100644 --- a/Libraries/AppDelegate/RCTAppDelegate.mm +++ b/Libraries/AppDelegate/RCTAppDelegate.mm @@ -13,12 +13,14 @@ #import #import #import +#import #import #import #import #import #import #import +#import "RCTLegacyInteropComponents.h" static NSString *const kRNConcurrentRoot = @"concurrentRoot"; @@ -60,6 +62,8 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( self.bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:self.bridge contextContainer:_contextContainer]; self.bridge.surfacePresenter = self.bridgeAdapter.surfacePresenter; + + [self unstable_registerLegacyComponents]; #endif NSDictionary *initProps = [self prepareInitialProps]; @@ -70,6 +74,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( rootViewController.view = rootView; self.window.rootViewController = rootViewController; [self.window makeKeyAndVisible]; + return YES; } @@ -172,6 +177,15 @@ - (BOOL)fabricEnabled return YES; } +#pragma mark - New Arch Utilities + +- (void)unstable_registerLegacyComponents +{ + for (NSString *legacyComponent in [RCTLegacyInteropComponents legacyInteropComponents]) { + [RCTLegacyViewManagerInteropComponentView supportLegacyViewManagerWithName:legacyComponent]; + } +} + #endif @end diff --git a/Libraries/AppDelegate/RCTLegacyInteropComponents.h b/Libraries/AppDelegate/RCTLegacyInteropComponents.h new file mode 100644 index 00000000000000..9d3bccc886dc1b --- /dev/null +++ b/Libraries/AppDelegate/RCTLegacyInteropComponents.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface RCTLegacyInteropComponents : NSObject + ++ (NSArray *)legacyInteropComponents; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Libraries/AppDelegate/RCTLegacyInteropComponents.mm b/Libraries/AppDelegate/RCTLegacyInteropComponents.mm new file mode 100644 index 00000000000000..d1d68376b08493 --- /dev/null +++ b/Libraries/AppDelegate/RCTLegacyInteropComponents.mm @@ -0,0 +1,17 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "RCTLegacyInteropComponents.h" + +@implementation RCTLegacyInteropComponents + ++ (NSArray *)legacyInteropComponents +{ + return @[]; +} + +@end diff --git a/Libraries/AppDelegate/React-RCTAppDelegate.podspec b/Libraries/AppDelegate/React-RCTAppDelegate.podspec index ccc57afe0688d0..0f43d56192110f 100644 --- a/Libraries/AppDelegate/React-RCTAppDelegate.podspec +++ b/Libraries/AppDelegate/React-RCTAppDelegate.podspec @@ -77,5 +77,16 @@ Pod::Spec.new do |s| if is_new_arch_enabled s.dependency "React-RCTFabric" s.dependency "React-graphics" + + s.script_phases = { + :name => "Generate Legacy Components Interop", + :script => " +. ${PODS_ROOT}/../.xcode.env +${NODE_BINARY} ${REACT_NATIVE_PATH}/scripts/codegen/generate-legacy-interop-components.js -p #{ENV['APP_PATH']} -o ${REACT_NATIVE_PATH}/Libraries/AppDelegate + ", + :execution_position => :before_compile, + :input_files => ["#{ENV['APP_PATH']}/react-native.config.js"], + :output_files => ["${REACT_NATIVE_PATH}/Libraries/AppDelegate/RCTLegacyInteropComponents.mm"], + } end end diff --git a/package.json b/package.json index 048e5be439bdb8..39fae1fedf481e 100644 --- a/package.json +++ b/package.json @@ -42,9 +42,9 @@ "scripts/generate-codegen-artifacts.js", "scripts/generate-provider-cli.js", "scripts/generate-specs-cli.js", - "scripts/codegen/codegen-utils.js", - "scripts/codegen/generate-artifacts-executor.js", - "scripts/codegen/generate-specs-cli-executor.js", + "scripts/codegen", + "!scripts/codegen/__tests__", + "!scripts/codegen/__test_fixtures__", "scripts/hermes/hermes-utils.js", "scripts/hermes/prepare-hermes-for-build.js", "scripts/ios-configure-glog.sh", @@ -59,6 +59,7 @@ "scripts/react_native_pods_utils/script_phases.sh", "scripts/react_native_pods.rb", "scripts/cocoapods", + "!scripts/cocoapods/__tests__", "scripts/react-native-xcode.sh", "sdks/.hermesversion", "sdks/hermes-engine", diff --git a/scripts/codegen/generate-legacy-interop-components.js b/scripts/codegen/generate-legacy-interop-components.js new file mode 100644 index 00000000000000..ebe3ce1fb3e2ad --- /dev/null +++ b/scripts/codegen/generate-legacy-interop-components.js @@ -0,0 +1,89 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +'use strict'; + +const yargs = require('yargs'); +const fs = require('fs'); + +const CONFIG_FILE_NAME = 'react-native.config.js'; +const LEGACY_COMPONENTS_FIELD = 'unstable_reactLegacyComponent'; +const OUTPUT_FILE_NAME = 'RCTLegacyInteropComponents.mm'; + +const argv = yargs + .option('p', { + alias: 'path', + description: 'Path to React Native application', + }) + .option('o', { + alias: 'outputPath', + description: 'Path where generated artifacts will be output to', + }) + .usage('Usage: $0 -p [path to app]') + .demandOption(['p']).argv; + +const appRoot = argv.path; +const outputPath = argv.outputPath; + +function fileBody(components) { + // eslint-disable duplicate-license-header + return ` +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "RCTLegacyInteropComponents.h" + +@implementation RCTLegacyInteropComponents + ++ (NSArray *)legacyInteropComponents +{ + return @[ +${components} + ]; +} + +@end +`; + // eslint-enable duplicate-license-header +} + +function generateRCTLegacyInteropComponents() { + const configFilePath = `${appRoot}/${CONFIG_FILE_NAME}`; + let reactNativeConfig = null; + try { + reactNativeConfig = require(configFilePath); + } catch (error) { + console.log(`No ${configFilePath}. Skip LegacyInterop generation`); + } + + if (reactNativeConfig && reactNativeConfig[LEGACY_COMPONENTS_FIELD]) { + let componentsArray = reactNativeConfig[LEGACY_COMPONENTS_FIELD].map( + name => `\t\t\t@"${name}",`, + ); + // Remove the last comma + if (componentsArray.length > 0) { + componentsArray[componentsArray.length - 1] = componentsArray[ + componentsArray.length - 1 + ].slice(0, -1); + } + + const filePath = `${outputPath}/${OUTPUT_FILE_NAME}`; + fs.writeFileSync(filePath, fileBody(componentsArray.join('\n'))); + } else { + console.log( + `No '${LEGACY_COMPONENTS_FIELD}' field. Skip LegacyInterop generation`, + ); + } +} + +generateRCTLegacyInteropComponents(); diff --git a/scripts/react_native_pods.rb b/scripts/react_native_pods.rb index aec2cdb24bb447..b3894cc85f68e5 100644 --- a/scripts/react_native_pods.rb +++ b/scripts/react_native_pods.rb @@ -64,6 +64,9 @@ def use_react_native! ( ios_folder: 'ios' ) + # Set the app_path as env variable so the podspecs can access it. + ENV['APP_PATH'] = app_path + # Current target definition is provided by Cocoapods and it refers to the target # that has invoked the `use_react_native!` function. ReactNativePodsUtils.detect_use_frameworks(current_target_definition)