UNPKG

nitro-codegen

Version:

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

147 lines (129 loc) 5.09 kB
import { createFileMetadataString, isNotDuplicate } from '../helpers.js'; import { indent } from '../../utils.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) { // 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 = []; 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 = []; 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; }