diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/Utils.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/Utils.js index 59121a4b52faa1..c711ce8c74f251 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/Utils.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/Utils.js @@ -10,7 +10,15 @@ 'use strict'; -import type {StructProperty} from './StructCollector'; +import type {Nullable} from '../../../CodegenSchema'; +import type {StructProperty, StructTypeAnnotation} from './StructCollector'; + +const {unwrapNullable} = require('../../../parsers/parsers-commons'); +const {wrapOptional: wrapCxxOptional} = require('../../TypeUtils/Cxx'); +const { + wrapOptional: wrapObjCOptional, +} = require('../../TypeUtils/Objective-C'); +const {capitalize} = require('../../Utils'); function getSafePropertyName(property: StructProperty): string { if (property.name === 'id') { @@ -26,7 +34,93 @@ function getNamespacedStructName( return `JS::${hasteModuleName}::${structName}`; } +type ArrayTypeWrapperType = 'std::vector' | 'facebook::react::LazyVector'; + +function toObjCType( + hasteModuleName: string, + nullableTypeAnnotation: Nullable, + isOptional: boolean = false, + arrayTypeWrapper: ArrayTypeWrapperType = 'std::vector', +): string { + const [typeAnnotation, nullable] = unwrapNullable(nullableTypeAnnotation); + const isRequired = !nullable && !isOptional; + + switch (typeAnnotation.type) { + case 'ReservedTypeAnnotation': + switch (typeAnnotation.name) { + case 'RootTag': + return wrapCxxOptional('double', isRequired); + default: + (typeAnnotation.name: empty); + throw new Error(`Unknown prop type, found: ${typeAnnotation.name}"`); + } + case 'StringTypeAnnotation': + return 'NSString *'; + case 'StringLiteralTypeAnnotation': + return 'NSString *'; + case 'StringLiteralUnionTypeAnnotation': + return 'NSString *'; + case 'NumberTypeAnnotation': + return wrapCxxOptional('double', isRequired); + case 'NumberLiteralTypeAnnotation': + return wrapCxxOptional('double', isRequired); + case 'FloatTypeAnnotation': + return wrapCxxOptional('double', isRequired); + case 'Int32TypeAnnotation': + return wrapCxxOptional('double', isRequired); + case 'DoubleTypeAnnotation': + return wrapCxxOptional('double', isRequired); + case 'BooleanTypeAnnotation': + return wrapCxxOptional('bool', isRequired); + case 'EnumDeclaration': + switch (typeAnnotation.memberType) { + case 'NumberTypeAnnotation': + return wrapCxxOptional('double', isRequired); + case 'StringTypeAnnotation': + return 'NSString *'; + default: + throw new Error( + `Couldn't convert enum into ObjC type: ${typeAnnotation.type}"`, + ); + } + case 'GenericObjectTypeAnnotation': + return wrapObjCOptional('id', isRequired); + case 'ArrayTypeAnnotation': + if (typeAnnotation.elementType.type === 'AnyTypeAnnotation') { + return wrapObjCOptional('id', isRequired); + } + + return wrapCxxOptional( + `${arrayTypeWrapper}<${toObjCType( + hasteModuleName, + typeAnnotation.elementType, + false, + arrayTypeWrapper, + )}>`, + isRequired, + ); + case 'TypeAliasTypeAnnotation': + const structName = capitalize(typeAnnotation.name); + const namespacedStructName = getNamespacedStructName( + hasteModuleName, + structName, + ); + return wrapCxxOptional( + arrayTypeWrapper === 'std::vector' + ? `${namespacedStructName}::Builder` + : namespacedStructName, + isRequired, + ); + default: + (typeAnnotation.type: empty); + throw new Error( + `Couldn't convert into ObjC type: ${typeAnnotation.type}"`, + ); + } +} + module.exports = { getSafePropertyName, getNamespacedStructName, + toObjCType, }; diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js index 9cee0665e8f396..2f52252ef69ecb 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js @@ -15,12 +15,7 @@ import type {ConstantsStruct, StructTypeAnnotation} from '../StructCollector'; import type {StructSerilizationOutput} from './serializeStruct'; const {unwrapNullable} = require('../../../../parsers/parsers-commons'); -const {wrapOptional: wrapCxxOptional} = require('../../../TypeUtils/Cxx'); -const { - wrapOptional: wrapObjCOptional, -} = require('../../../TypeUtils/Objective-C'); -const {capitalize} = require('../../../Utils'); -const {getNamespacedStructName, getSafePropertyName} = require('../Utils'); +const {getSafePropertyName, toObjCType: sharedToObjCType} = require('../Utils'); const StructTemplate = ({ hasteModuleName, @@ -80,74 +75,12 @@ function toObjCType( nullableTypeAnnotation: Nullable, isOptional: boolean = false, ): string { - const [typeAnnotation, nullable] = unwrapNullable(nullableTypeAnnotation); - const isRequired = !nullable && !isOptional; - - switch (typeAnnotation.type) { - case 'ReservedTypeAnnotation': - switch (typeAnnotation.name) { - case 'RootTag': - return wrapCxxOptional('double', isRequired); - default: - (typeAnnotation.name: empty); - throw new Error(`Unknown prop type, found: ${typeAnnotation.name}"`); - } - case 'StringTypeAnnotation': - return 'NSString *'; - case 'StringLiteralTypeAnnotation': - return 'NSString *'; - case 'StringLiteralUnionTypeAnnotation': - return 'NSString *'; - case 'NumberTypeAnnotation': - return wrapCxxOptional('double', isRequired); - case 'NumberLiteralTypeAnnotation': - return wrapCxxOptional('double', isRequired); - case 'FloatTypeAnnotation': - return wrapCxxOptional('double', isRequired); - case 'Int32TypeAnnotation': - return wrapCxxOptional('double', isRequired); - case 'DoubleTypeAnnotation': - return wrapCxxOptional('double', isRequired); - case 'BooleanTypeAnnotation': - return wrapCxxOptional('bool', isRequired); - case 'EnumDeclaration': - switch (typeAnnotation.memberType) { - case 'NumberTypeAnnotation': - return wrapCxxOptional('double', isRequired); - case 'StringTypeAnnotation': - return 'NSString *'; - default: - throw new Error( - `Couldn't convert enum into ObjC type: ${typeAnnotation.type}"`, - ); - } - case 'GenericObjectTypeAnnotation': - return wrapObjCOptional('id', isRequired); - case 'ArrayTypeAnnotation': - if (typeAnnotation.elementType.type === 'AnyTypeAnnotation') { - return wrapObjCOptional('id', isRequired); - } - - return wrapCxxOptional( - `std::vector<${toObjCType( - hasteModuleName, - typeAnnotation.elementType, - )}>`, - isRequired, - ); - case 'TypeAliasTypeAnnotation': - const structName = capitalize(typeAnnotation.name); - const namespacedStructName = getNamespacedStructName( - hasteModuleName, - structName, - ); - return wrapCxxOptional(`${namespacedStructName}::Builder`, isRequired); - default: - (typeAnnotation.type: empty); - throw new Error( - `Couldn't convert into ObjC type: ${typeAnnotation.type}"`, - ); - } + return sharedToObjCType( + hasteModuleName, + nullableTypeAnnotation, + isOptional, + 'std::vector', + ); } function toObjCValue( diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js index 9fca06510ad933..97323bd4b977ef 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js @@ -15,12 +15,12 @@ import type {RegularStruct, StructTypeAnnotation} from '../StructCollector'; import type {StructSerilizationOutput} from './serializeStruct'; const {unwrapNullable} = require('../../../../parsers/parsers-commons'); -const {wrapOptional: wrapCxxOptional} = require('../../../TypeUtils/Cxx'); -const { - wrapOptional: wrapObjCOptional, -} = require('../../../TypeUtils/Objective-C'); const {capitalize} = require('../../../Utils'); -const {getNamespacedStructName, getSafePropertyName} = require('../Utils'); +const { + getNamespacedStructName, + getSafePropertyName, + toObjCType: sharedToObjCType, +} = require('../Utils'); const StructTemplate = ({ hasteModuleName, @@ -71,73 +71,12 @@ function toObjCType( nullableTypeAnnotation: Nullable, isOptional: boolean = false, ): string { - const [typeAnnotation, nullable] = unwrapNullable(nullableTypeAnnotation); - const isRequired = !nullable && !isOptional; - - switch (typeAnnotation.type) { - case 'ReservedTypeAnnotation': - switch (typeAnnotation.name) { - case 'RootTag': - return wrapCxxOptional('double', isRequired); - default: - (typeAnnotation.name: empty); - throw new Error(`Unknown prop type, found: ${typeAnnotation.name}"`); - } - case 'StringTypeAnnotation': - return 'NSString *'; - case 'StringLiteralTypeAnnotation': - return 'NSString *'; - case 'StringLiteralUnionTypeAnnotation': - return 'NSString *'; - case 'NumberTypeAnnotation': - return wrapCxxOptional('double', isRequired); - case 'NumberLiteralTypeAnnotation': - return wrapCxxOptional('double', isRequired); - case 'FloatTypeAnnotation': - return wrapCxxOptional('double', isRequired); - case 'Int32TypeAnnotation': - return wrapCxxOptional('double', isRequired); - case 'DoubleTypeAnnotation': - return wrapCxxOptional('double', isRequired); - case 'BooleanTypeAnnotation': - return wrapCxxOptional('bool', isRequired); - case 'EnumDeclaration': - switch (typeAnnotation.memberType) { - case 'NumberTypeAnnotation': - return wrapCxxOptional('double', isRequired); - case 'StringTypeAnnotation': - return 'NSString *'; - default: - throw new Error( - `Couldn't convert enum into ObjC type: ${typeAnnotation.type}"`, - ); - } - case 'GenericObjectTypeAnnotation': - return wrapObjCOptional('id', isRequired); - case 'ArrayTypeAnnotation': - if (typeAnnotation.elementType.type === 'AnyTypeAnnotation') { - return wrapObjCOptional('id', isRequired); - } - return wrapCxxOptional( - `facebook::react::LazyVector<${toObjCType( - hasteModuleName, - typeAnnotation.elementType, - )}>`, - isRequired, - ); - case 'TypeAliasTypeAnnotation': - const structName = capitalize(typeAnnotation.name); - const namespacedStructName = getNamespacedStructName( - hasteModuleName, - structName, - ); - return wrapCxxOptional(namespacedStructName, isRequired); - default: - (typeAnnotation.type: empty); - throw new Error( - `Couldn't convert into ObjC type: ${typeAnnotation.type}"`, - ); - } + return sharedToObjCType( + hasteModuleName, + nullableTypeAnnotation, + isOptional, + 'facebook::react::LazyVector', + ); } function toObjCValue( diff --git a/packages/react-native/scripts/replace-build-utils.js b/packages/react-native/scripts/replace-build-utils.js new file mode 100644 index 00000000000000..3ad35ba29e7ccb --- /dev/null +++ b/packages/react-native/scripts/replace-build-utils.js @@ -0,0 +1,90 @@ +/** + * 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. + * + * @flow strict-local + * @format + */ + +'use strict'; + +const {spawnSync} = require('child_process'); +const fs = require('fs'); + +function validateBuildConfiguration(configuration /*: string */) { + if (!['Debug', 'Release'].includes(configuration)) { + throw new Error(`Invalid configuration ${configuration}`); + } +} + +function validateVersion(version /*: ?string */) { + if (version == null || version === '') { + throw new Error('Version cannot be empty'); + } +} + +function shouldReplaceConfiguration( + lastBuildFilename /*: string */, + configuration /*: string */, + componentName /*: string */, +) /*: boolean */ { + const fileExists = fs.existsSync(lastBuildFilename); + + if (fileExists) { + console.log(`Found ${lastBuildFilename} file`); + const oldConfiguration = fs.readFileSync(lastBuildFilename).toString(); + if (oldConfiguration === configuration) { + console.log( + `Same config of the previous build. No need to replace ${componentName}`, + ); + return false; + } + } + + // Assumption: if there is no stored last build, we assume that it was build for debug. + if (!fileExists && configuration === 'Debug') { + console.log( + `No previous build detected, but Debug Configuration. No need to replace ${componentName}`, + ); + return false; + } + + return true; +} + +function replaceConfiguration( + tarballPath /*: string */, + finalLocation /*: string */, + postExtractCallback /*: ?() => void */, +) { + console.log('Preparing the final location', finalLocation); + fs.rmSync(finalLocation, {force: true, recursive: true}); + fs.mkdirSync(finalLocation, {recursive: true}); + + console.log('Extracting the tarball', tarballPath); + spawnSync('tar', ['-xf', tarballPath, '-C', finalLocation], { + stdio: 'inherit', + }); + + if (postExtractCallback != null) { + postExtractCallback(); + } +} + +function updateLastBuildConfiguration( + lastBuildFilename /*: string */, + configuration /*: string */, +) { + console.log(`Updating ${lastBuildFilename} with ${configuration}`); + fs.writeFileSync(lastBuildFilename, configuration); +} + +module.exports = { + validateBuildConfiguration, + validateVersion, + shouldReplaceConfiguration, + replaceConfiguration, + updateLastBuildConfiguration, +}; diff --git a/packages/react-native/scripts/replace-rncore-version.js b/packages/react-native/scripts/replace-rncore-version.js index 1385b71ffb4acb..3d04bcd7476abf 100644 --- a/packages/react-native/scripts/replace-rncore-version.js +++ b/packages/react-native/scripts/replace-rncore-version.js @@ -10,71 +10,27 @@ 'use strict'; -const {spawnSync} = require('child_process'); -const fs = require('fs'); +const { + replaceConfiguration, + shouldReplaceConfiguration, + updateLastBuildConfiguration, + validateBuildConfiguration, + validateVersion, +} = require('./replace-build-utils'); const yargs = require('yargs'); const LAST_BUILD_FILENAME = 'React-Core-prebuilt/.last_build_configuration'; -function validateBuildConfiguration(configuration /*: string */) { - if (!['Debug', 'Release'].includes(configuration)) { - throw new Error(`Invalid configuration ${configuration}`); - } -} - -function validateVersion(version /*: ?string */) { - if (version == null || version === '') { - throw new Error('Version cannot be empty'); - } -} - -function shouldReplaceRnCoreConfiguration(configuration /*: string */) { - const fileExists = fs.existsSync(LAST_BUILD_FILENAME); - - if (fileExists) { - console.log(`Found ${LAST_BUILD_FILENAME} file`); - const oldConfiguration = fs.readFileSync(LAST_BUILD_FILENAME).toString(); - if (oldConfiguration === configuration) { - console.log( - 'Same config of the previous build. No need to replace React-Core-prebuilt', - ); - return false; - } - } - - // Assumption: if there is no stored last build, we assume that it was build for debug. - if (!fileExists && configuration === 'Debug') { - console.log( - 'No previous build detected, but Debug Configuration. No need to replace React-Core-prebuilt', - ); - return false; - } - - return true; -} - function replaceRNCoreConfiguration( configuration /*: string */, version /*: string */, podsRoot /*: string */, ) { // Filename comes from rncore.rb - const tarballURLPath = `${podsRoot}/ReactNativeCore-artifacts/reactnative-core-${version.toLowerCase()}-${configuration.toLowerCase()}.tar.gz`; - + const tarballPath = `${podsRoot}/ReactNativeCore-artifacts/reactnative-core-${version.toLowerCase()}-${configuration.toLowerCase()}.tar.gz`; const finalLocation = 'React-Core-prebuilt'; - console.log('Preparing the final location', finalLocation); - fs.rmSync(finalLocation, {force: true, recursive: true}); - fs.mkdirSync(finalLocation, {recursive: true}); - - console.log('Extracting the tarball', tarballURLPath); - spawnSync('tar', ['-xf', tarballURLPath, '-C', finalLocation], { - stdio: 'inherit', - }); -} -function updateLastBuildConfiguration(configuration /*: string */) { - console.log(`Updating ${LAST_BUILD_FILENAME} with ${configuration}`); - fs.writeFileSync(LAST_BUILD_FILENAME, configuration); + replaceConfiguration(tarballPath, finalLocation); } function main( @@ -85,12 +41,18 @@ function main( validateBuildConfiguration(configuration); validateVersion(version); - if (!shouldReplaceRnCoreConfiguration(configuration)) { + if ( + !shouldReplaceConfiguration( + LAST_BUILD_FILENAME, + configuration, + 'React-Core-prebuilt', + ) + ) { return; } replaceRNCoreConfiguration(configuration, version, podsRoot); - updateLastBuildConfiguration(configuration); + updateLastBuildConfiguration(LAST_BUILD_FILENAME, configuration); console.log('Done replacing React Native prebuilt'); } diff --git a/packages/react-native/third-party-podspecs/replace_dependencies_version.js b/packages/react-native/third-party-podspecs/replace_dependencies_version.js index 66fa888758ebb5..fb3fc0d3c38d19 100644 --- a/packages/react-native/third-party-podspecs/replace_dependencies_version.js +++ b/packages/react-native/third-party-podspecs/replace_dependencies_version.js @@ -10,89 +10,48 @@ 'use strict'; -const {spawnSync} = require('child_process'); +const { + replaceConfiguration, + shouldReplaceConfiguration, + updateLastBuildConfiguration, + validateBuildConfiguration, + validateVersion, +} = require('../scripts/replace-build-utils'); const fs = require('fs'); const yargs = require('yargs'); const LAST_BUILD_FILENAME = 'ReactNativeDependencies/.last_build_configuration'; -function validateBuildConfiguration(configuration /*: string */) { - if (!['Debug', 'Release'].includes(configuration)) { - throw new Error(`Invalid configuration ${configuration}`); - } -} - -function validateVersion(version /*: ?string */) { - if (version == null || version === '') { - throw new Error('Version cannot be empty'); - } -} - -function shouldReplaceRnDepsConfiguration(configuration /*: string */) { - const fileExists = fs.existsSync(LAST_BUILD_FILENAME); - - if (fileExists) { - console.log(`Found ${LAST_BUILD_FILENAME} file`); - const oldConfiguration = fs.readFileSync(LAST_BUILD_FILENAME).toString(); - if (oldConfiguration === configuration) { - console.log( - 'Same config of the previous build. No need to replace RNDeps', - ); - return false; - } - } - - // Assumption: if there is no stored last build, we assume that it was build for debug. - if (!fileExists && configuration === 'Debug') { - console.log( - 'No previous build detected, but Debug Configuration. No need to replace RNDeps', - ); - return false; - } - - return true; -} - function replaceRNDepsConfiguration( configuration /*: string */, version /*: string */, podsRoot /*: string */, ) { - const tarballURLPath = `${podsRoot}/ReactNativeDependencies-artifacts/reactnative-dependencies-${version.toLowerCase()}-${configuration.toLowerCase()}.tar.gz`; - + const tarballPath = `${podsRoot}/ReactNativeDependencies-artifacts/reactnative-dependencies-${version.toLowerCase()}-${configuration.toLowerCase()}.tar.gz`; const finalLocation = 'ReactNativeDependencies/framework'; - console.log('Preparing the final location', finalLocation); - fs.rmSync(finalLocation, {force: true, recursive: true}); - fs.mkdirSync(finalLocation, {recursive: true}); - console.log('Extracting the tarball', tarballURLPath); - spawnSync('tar', ['-xf', tarballURLPath, '-C', finalLocation], { - stdio: 'inherit', - }); - - // Now we need to remove the extra third-party folder as we do in the podspec's prepare-script - // We need to take the ReactNativeDependencies.xcframework folder and move it up one level - // from ${finalLocation}/packages/react-native/third-party/ to ${finalLocation}/packages/react-native/ - console.log('Resolving ReactNativeDependencies.xcframework folder structure'); - const thirdPartyPath = `${finalLocation}/packages/react-native/third-party`; - const sourcePath = `${thirdPartyPath}/ReactNativeDependencies.xcframework`; - const destinationPath = `${finalLocation}/packages/react-native/ReactNativeDependencies.xcframework`; - if (fs.existsSync(sourcePath)) { - fs.renameSync(sourcePath, destinationPath); - } else { - throw new Error( - `Expected ReactNativeDependencies.xcframework to be at ${sourcePath}, but it was not found.`, + replaceConfiguration(tarballPath, finalLocation, () => { + // Now we need to remove the extra third-party folder as we do in the podspec's prepare-script + // We need to take the ReactNativeDependencies.xcframework folder and move it up one level + // from ${finalLocation}/packages/react-native/third-party/ to ${finalLocation}/packages/react-native/ + console.log( + 'Resolving ReactNativeDependencies.xcframework folder structure', ); - } - - if (fs.existsSync(thirdPartyPath)) { - fs.rmSync(thirdPartyPath, {force: true, recursive: true}); - } -} + const thirdPartyPath = `${finalLocation}/packages/react-native/third-party`; + const sourcePath = `${thirdPartyPath}/ReactNativeDependencies.xcframework`; + const destinationPath = `${finalLocation}/packages/react-native/ReactNativeDependencies.xcframework`; + if (fs.existsSync(sourcePath)) { + fs.renameSync(sourcePath, destinationPath); + } else { + throw new Error( + `Expected ReactNativeDependencies.xcframework to be at ${sourcePath}, but it was not found.`, + ); + } -function updateLastBuildConfiguration(configuration /*: string */) { - console.log(`Updating ${LAST_BUILD_FILENAME} with ${configuration}`); - fs.writeFileSync(LAST_BUILD_FILENAME, configuration); + if (fs.existsSync(thirdPartyPath)) { + fs.rmSync(thirdPartyPath, {force: true, recursive: true}); + } + }); } function main( @@ -103,12 +62,18 @@ function main( validateBuildConfiguration(configuration); validateVersion(version); - if (!shouldReplaceRnDepsConfiguration(configuration)) { + if ( + !shouldReplaceConfiguration( + LAST_BUILD_FILENAME, + configuration, + 'RNDeps', + ) + ) { return; } replaceRNDepsConfiguration(configuration, version, podsRoot); - updateLastBuildConfiguration(configuration); + updateLastBuildConfiguration(LAST_BUILD_FILENAME, configuration); console.log('Done replacing React Native Dependencies'); }