UNPKG

nitro-codegen

Version:

The code-generator for react-native-nitro-modules.

166 lines (141 loc) 5.09 kB
import type { SourceFile } from '../SourceFile.js' import { createFileMetadataString, isNotDuplicate } from '../helpers.js' import { indent } from '../../utils.js' import type { HybridObjectSpec } from '../HybridObjectSpec.js' import { includeHeader, includeNitroHeader } from './includeNitroHeader.js' import { NitroConfig } from '../../config/NitroConfig.js' import { getHybridObjectName } from '../getHybridObjectName.js' import { HybridObjectType } from '../types/HybridObjectType.js' export function createCppHybridObject(spec: HybridObjectSpec): SourceFile[] { // Extra includes const extraFiles = [ ...spec.properties.flatMap((p) => p.getExtraFiles()), ...spec.methods.flatMap((m) => m.getExtraFiles()), ] const extraIncludes = [ ...spec.properties.flatMap((p) => p.getRequiredImports()), ...spec.methods.flatMap((m) => m.getRequiredImports()), ] const cppForwardDeclarations = extraIncludes .map((i) => i.forwardDeclaration) .filter((v) => v != null) .filter(isNotDuplicate) const cppExtraIncludes = extraIncludes .map((i) => includeHeader(i)) .filter(isNotDuplicate) const cxxNamespace = NitroConfig.getCxxNamespace('c++') const name = getHybridObjectName(spec.name) const bases = ['public virtual HybridObject'] for (const base of spec.baseTypes) { const hybridObject = new HybridObjectType(base) bases.push(`public virtual ${getHybridObjectName(base.name).HybridTSpec}`) const imports = hybridObject.getRequiredImports() cppForwardDeclarations.push( ...imports.map((i) => i.forwardDeclaration).filter((f) => f != null) ) cppExtraIncludes.push(...imports.map((i) => includeHeader(i))) } // Generate the full header / code const cppHeaderCode = ` ${createFileMetadataString(`${name.HybridTSpec}.hpp`)} #pragma once ${includeNitroHeader('HybridObject.hpp')} ${cppForwardDeclarations.join('\n')} ${cppExtraIncludes.join('\n')} namespace ${cxxNamespace} { using namespace margelo::nitro; /** * An abstract base class for \`${spec.name}\` * Inherit this class to create instances of \`${name.HybridTSpec}\` in C++. * You must explicitly call \`HybridObject\`'s constructor yourself, because it is virtual. * @example * \`\`\`cpp * class ${name.HybridT}: public ${name.HybridTSpec} { * public: * ${name.HybridT}(...): HybridObject(TAG) { ... } * // ... * }; * \`\`\` */ class ${name.HybridTSpec}: ${bases.join(', ')} { public: // Constructor explicit ${name.HybridTSpec}(): HybridObject(TAG) { } // Destructor ~${name.HybridTSpec}() override = default; public: // Properties ${indent(spec.properties.map((p) => p.getCode('c++', { virtual: true })).join('\n'), ' ')} public: // Methods ${indent(spec.methods.map((m) => m.getCode('c++', { virtual: true })).join('\n'), ' ')} protected: // Hybrid Setup void loadHybridMethods() override; protected: // Tag for logging static constexpr auto TAG = "${spec.name}"; }; } // namespace ${cxxNamespace} ` // Each C++ method needs to be registered in the HybridObject - that's getters, setters and normal methods. const registrations: string[] = [] for (const property of spec.properties) { const getterMethod = property.getGetterName('other') const setterMethod = property.getSetterName('other') // getter registrations.push( `prototype.registerHybridGetter("${property.name}", &${name.HybridTSpec}::${getterMethod});` ) if (!property.isReadonly) { // setter registrations.push( `prototype.registerHybridSetter("${property.name}", &${name.HybridTSpec}::${setterMethod});` ) } } for (const method of spec.methods) { // method registrations.push( `prototype.registerHybridMethod("${method.name}", &${name.HybridTSpec}::${method.name});` ) } const basePrototypeRegistrations = ['HybridObject::loadHybridMethods();'] for (const base of spec.baseTypes) { const hybridObjectName = getHybridObjectName(base.name) basePrototypeRegistrations.push( `${hybridObjectName.HybridTSpec}::loadHybridMethods();` ) } const cppBodyCode = ` ${createFileMetadataString(`${name.HybridTSpec}.cpp`)} #include "${name.HybridTSpec}.hpp" namespace ${cxxNamespace} { void ${name.HybridTSpec}::loadHybridMethods() { // load base methods/properties ${indent(basePrototypeRegistrations.join('\n'), ' ')} // load custom methods/properties registerHybrids(this, [](Prototype& prototype) { ${indent(registrations.join('\n'), ' ')} }); } } // namespace ${cxxNamespace} ` const files: SourceFile[] = [] files.push({ content: cppHeaderCode, name: `${name.HybridTSpec}.hpp`, subdirectory: [], language: 'c++', platform: 'shared', }) files.push({ content: cppBodyCode, name: `${name.HybridTSpec}.cpp`, subdirectory: [], language: 'c++', platform: 'shared', }) files.push(...extraFiles) return files }