UNPKG

react-native-codegen

Version:
258 lines (236 loc) 7.57 kB
/** * 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 * @format */ 'use strict'; const {getSafePropertyName, getNamespacedStructName} = require('../Utils'); const {capitalize} = require('../../../Utils'); import type {Nullable} from '../../../../CodegenSchema'; import type {StructTypeAnnotation, RegularStruct} from '../StructCollector'; import type {StructSerilizationOutput} from './serializeStruct'; const {unwrapNullable} = require('../../../../parsers/flow/modules/utils'); const StructTemplate = ({ hasteModuleName, structName, structProperties, }: $ReadOnly<{ hasteModuleName: string, structName: string, structProperties: string, }>) => `namespace JS { namespace ${hasteModuleName} { struct ${structName} { ${structProperties} ${structName}(NSDictionary *const v) : _v(v) {} private: NSDictionary *_v; }; } } @interface RCTCxxConvert (${hasteModuleName}_${structName}) + (RCTManagedPointer *)JS_${hasteModuleName}_${structName}:(id)json; @end`; const MethodTemplate = ({ returnType, returnValue, hasteModuleName, structName, propertyName, safePropertyName, }: $ReadOnly<{ returnType: string, returnValue: string, hasteModuleName: string, structName: string, propertyName: string, safePropertyName: string, }>) => `inline ${returnType}JS::${hasteModuleName}::${structName}::${safePropertyName}() const { id const p = _v[@"${propertyName}"]; return ${returnValue}; }`; function toObjCType( hasteModuleName: string, nullableTypeAnnotation: Nullable<StructTypeAnnotation>, isOptional: boolean = false, ): string { const [typeAnnotation, nullable] = unwrapNullable(nullableTypeAnnotation); const isRequired = !nullable && !isOptional; const wrapOptional = (type: string) => { return isRequired ? type : `std::optional<${type}>`; }; switch (typeAnnotation.type) { case 'ReservedTypeAnnotation': switch (typeAnnotation.name) { case 'RootTag': return wrapOptional('double'); default: (typeAnnotation.name: empty); throw new Error(`Unknown prop type, found: ${typeAnnotation.name}"`); } case 'StringTypeAnnotation': return 'NSString *'; case 'NumberTypeAnnotation': return wrapOptional('double'); case 'FloatTypeAnnotation': return wrapOptional('double'); case 'Int32TypeAnnotation': return wrapOptional('double'); case 'DoubleTypeAnnotation': return wrapOptional('double'); case 'BooleanTypeAnnotation': return wrapOptional('bool'); case 'GenericObjectTypeAnnotation': return isRequired ? 'id<NSObject> ' : 'id<NSObject> _Nullable'; case 'ArrayTypeAnnotation': if (typeAnnotation.elementType == null) { return isRequired ? 'id<NSObject> ' : 'id<NSObject> _Nullable'; } return wrapOptional( `facebook::react::LazyVector<${toObjCType( hasteModuleName, typeAnnotation.elementType, )}>`, ); case 'TypeAliasTypeAnnotation': const structName = capitalize(typeAnnotation.name); const namespacedStructName = getNamespacedStructName( hasteModuleName, structName, ); return wrapOptional(namespacedStructName); default: (typeAnnotation.type: empty); throw new Error( `Couldn't convert into ObjC type: ${typeAnnotation.type}"`, ); } } function toObjCValue( hasteModuleName: string, nullableTypeAnnotation: Nullable<StructTypeAnnotation>, value: string, depth: number, isOptional: boolean = false, ): string { const [typeAnnotation, nullable] = unwrapNullable(nullableTypeAnnotation); const isRequired = !nullable && !isOptional; const RCTBridgingTo = (type: string, arg?: string) => { const args = [value, arg].filter(Boolean).join(', '); return isRequired ? `RCTBridgingTo${type}(${args})` : `RCTBridgingToOptional${type}(${args})`; }; switch (typeAnnotation.type) { case 'ReservedTypeAnnotation': switch (typeAnnotation.name) { case 'RootTag': return RCTBridgingTo('Double'); default: (typeAnnotation.name: empty); throw new Error( `Couldn't convert into ObjC type: ${typeAnnotation.type}"`, ); } case 'StringTypeAnnotation': return RCTBridgingTo('String'); case 'NumberTypeAnnotation': return RCTBridgingTo('Double'); case 'FloatTypeAnnotation': return RCTBridgingTo('Double'); case 'Int32TypeAnnotation': return RCTBridgingTo('Double'); case 'DoubleTypeAnnotation': return RCTBridgingTo('Double'); case 'BooleanTypeAnnotation': return RCTBridgingTo('Bool'); case 'GenericObjectTypeAnnotation': return value; case 'ArrayTypeAnnotation': const {elementType} = typeAnnotation; if (elementType == null) { return value; } const localVarName = `itemValue_${depth}`; const elementObjCType = toObjCType(hasteModuleName, elementType); const elementObjCValue = toObjCValue( hasteModuleName, elementType, localVarName, depth + 1, ); return RCTBridgingTo( 'Vec', `^${elementObjCType}(id ${localVarName}) { return ${elementObjCValue}; }`, ); case 'TypeAliasTypeAnnotation': const structName = capitalize(typeAnnotation.name); const namespacedStructName = getNamespacedStructName( hasteModuleName, structName, ); return !isRequired ? `(${value} == nil ? std::nullopt : std::make_optional(${namespacedStructName}(${value})))` : `${namespacedStructName}(${value})`; default: (typeAnnotation.type: empty); throw new Error( `Couldn't convert into ObjC value: ${typeAnnotation.type}"`, ); } } function serializeRegularStruct( hasteModuleName: string, struct: RegularStruct, ): StructSerilizationOutput { const declaration = StructTemplate({ hasteModuleName: hasteModuleName, structName: struct.name, structProperties: struct.properties .map(property => { const {typeAnnotation, optional} = property; const safePropName = getSafePropertyName(property); const returnType = toObjCType( hasteModuleName, typeAnnotation, optional, ); const padding = ' '.repeat(returnType.endsWith('*') ? 0 : 1); return `${returnType}${padding}${safePropName}() const;`; }) .join('\n '), }); // $FlowFixMe[missing-type-arg] const methods = struct.properties .map<string>(property => { const {typeAnnotation, optional, name: propName} = property; const safePropertyName = getSafePropertyName(property); const returnType = toObjCType(hasteModuleName, typeAnnotation, optional); const returnValue = toObjCValue( hasteModuleName, typeAnnotation, 'p', 0, optional, ); const padding = ' '.repeat(returnType.endsWith('*') ? 0 : 1); return MethodTemplate({ hasteModuleName, structName: struct.name, returnType: returnType + padding, returnValue: returnValue, propertyName: propName, safePropertyName, }); }) .join('\n'); return {methods, declaration}; } module.exports = { serializeRegularStruct, };