UNPKG

create-expo-cljs-app

Version:

Create a react native application with Expo and Shadow-CLJS!

235 lines (210 loc) 6.46 kB
/** * Copyright (c) Facebook, Inc. and its 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'; import type { SchemaType, Nullable, NamedShape, NativeModulePropertyShape, NativeModuleFunctionTypeAnnotation, NativeModuleParamTypeAnnotation, } from '../../CodegenSchema'; import type {AliasResolver} from './Utils'; const {createAliasResolver, getModules} = require('./Utils'); const {unwrapNullable} = require('../../parsers/flow/modules/utils'); type FilesOutput = Map<string, string>; const HostFunctionTemplate = ({ hasteModuleName, methodName, isVoid, args, }: $ReadOnly<{ hasteModuleName: string, methodName: string, isVoid: boolean, args: Array<string>, }>) => { const methodCallArgs = ['rt', ...args].join(', '); const methodCall = `static_cast<${hasteModuleName}CxxSpecJSI *>(&turboModule)->${methodName}(${methodCallArgs});`; return `static jsi::Value __hostFunction_${hasteModuleName}CxxSpecJSI_${methodName}(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {${ isVoid ? `\n ${methodCall}` : '' } return ${isVoid ? 'jsi::Value::undefined();' : methodCall} }`; }; const ModuleTemplate = ({ hasteModuleName, hostFunctions, moduleName, methods, }: $ReadOnly<{ hasteModuleName: string, hostFunctions: $ReadOnlyArray<string>, moduleName: string, methods: $ReadOnlyArray<$ReadOnly<{methodName: string, paramCount: number}>>, }>) => { return `${hostFunctions.join('\n')} ${hasteModuleName}CxxSpecJSI::${hasteModuleName}CxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker) : TurboModule("${moduleName}", jsInvoker) { ${methods .map(({methodName, paramCount}) => { return ` methodMap_["${methodName}"] = MethodMetadata {${paramCount}, __hostFunction_${hasteModuleName}CxxSpecJSI_${methodName}};`; }) .join('\n')} }`; }; const FileTemplate = ({ libraryName, modules, }: $ReadOnly<{ libraryName: string, modules: string, }>) => { return `/** * ${'C'}opyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * ${'@'}generated by codegen project: GenerateModuleH.js */ #include <react/modules/${libraryName}/NativeModules.h> namespace facebook { namespace react { ${modules} } // namespace react } // namespace facebook `; }; type Param = NamedShape<Nullable<NativeModuleParamTypeAnnotation>>; function serializeArg( arg: Param, index: number, resolveAlias: AliasResolver, ): string { function wrap(suffix) { return `args[${index}]${suffix}`; } const {typeAnnotation: nullableTypeAnnotation} = arg; const [typeAnnotation] = unwrapNullable<NativeModuleParamTypeAnnotation>( nullableTypeAnnotation, ); let realTypeAnnotation = typeAnnotation; if (realTypeAnnotation.type === 'TypeAliasTypeAnnotation') { realTypeAnnotation = resolveAlias(realTypeAnnotation.name); } switch (realTypeAnnotation.type) { case 'ReservedTypeAnnotation': switch (realTypeAnnotation.name) { case 'RootTag': return wrap('.getNumber()'); default: (realTypeAnnotation.name: empty); throw new Error( `Unknown prop type for "${arg.name}, found: ${realTypeAnnotation.name}"`, ); } case 'StringTypeAnnotation': return wrap('.getString(rt)'); case 'BooleanTypeAnnotation': return wrap('.getBool()'); case 'NumberTypeAnnotation': return wrap('.getNumber()'); case 'FloatTypeAnnotation': return wrap('.getNumber()'); case 'DoubleTypeAnnotation': return wrap('.getNumber()'); case 'Int32TypeAnnotation': return wrap('.getNumber()'); case 'ArrayTypeAnnotation': return wrap('.getObject(rt).getArray(rt)'); case 'FunctionTypeAnnotation': return `std::move(${wrap('.getObject(rt).getFunction(rt)')})`; case 'GenericObjectTypeAnnotation': return wrap('.getObject(rt)'); case 'ObjectTypeAnnotation': return wrap('.getObject(rt)'); default: (realTypeAnnotation.type: empty); throw new Error( `Unknown prop type for "${arg.name}, found: ${realTypeAnnotation.type}"`, ); } } function serializePropertyIntoHostFunction( hasteModuleName: string, property: NativeModulePropertyShape, resolveAlias: AliasResolver, ): string { const [ propertyTypeAnnotation, ] = unwrapNullable<NativeModuleFunctionTypeAnnotation>( property.typeAnnotation, ); const isVoid = propertyTypeAnnotation.returnTypeAnnotation.type === 'VoidTypeAnnotation'; return HostFunctionTemplate({ hasteModuleName, methodName: property.name, isVoid, args: propertyTypeAnnotation.params.map((p, i) => serializeArg(p, i, resolveAlias), ), }); } module.exports = { generate( libraryName: string, schema: SchemaType, moduleSpecName: string, packageName?: string, ): FilesOutput { const nativeModules = getModules(schema); const modules = Object.keys(nativeModules) .map((hasteModuleName: string) => { const nativeModule = nativeModules[hasteModuleName]; const { aliases, spec: {properties}, moduleNames, } = nativeModule; const resolveAlias = createAliasResolver(aliases); const hostFunctions = properties.map(property => serializePropertyIntoHostFunction( hasteModuleName, property, resolveAlias, ), ); return ModuleTemplate({ hasteModuleName, hostFunctions, // TODO: What happens when there are more than one NativeModule requires? moduleName: moduleNames[0], methods: properties.map( ({name: propertyName, typeAnnotation: nullableTypeAnnotation}) => { const [{params}] = unwrapNullable(nullableTypeAnnotation); return { methodName: propertyName, paramCount: params.length, }; }, ), }); }) .join('\n'); const fileName = 'NativeModules.cpp'; const replacedTemplate = FileTemplate({ modules, libraryName, }); return new Map([[fileName, replacedTemplate]]); }, };