nitro-codegen
Version:
The code-generator for react-native-nitro-modules.
147 lines (129 loc) • 5.09 kB
JavaScript
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;
}