react-native-codegen
Version:
⚛️ Code generation tools for React Native
284 lines (267 loc) • 8.58 kB
JavaScript
/**
* 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.
*
*
* @format
*/
;
function _slicedToArray(arr, i) {
return (
_arrayWithHoles(arr) ||
_iterableToArrayLimit(arr, i) ||
_unsupportedIterableToArray(arr, i) ||
_nonIterableRest()
);
}
function _nonIterableRest() {
throw new TypeError(
'Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.',
);
}
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === 'string') return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === 'Object' && o.constructor) n = o.constructor.name;
if (n === 'Map' || n === 'Set') return Array.from(o);
if (n === 'Arguments' || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))
return _arrayLikeToArray(o, minLen);
}
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
return arr2;
}
function _iterableToArrayLimit(r, l) {
var t =
null == r
? null
: ('undefined' != typeof Symbol && r[Symbol.iterator]) || r['@@iterator'];
if (null != t) {
var e,
n,
i,
u,
a = [],
f = !0,
o = !1;
try {
if (((i = (t = t.call(r)).next), 0 === l)) {
if (Object(t) !== t) return;
f = !1;
} else
for (
;
!(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l);
f = !0
);
} catch (r) {
(o = !0), (n = r);
} finally {
try {
if (!f && null != t.return && ((u = t.return()), Object(u) !== u))
return;
} finally {
if (o) throw n;
}
}
return a;
}
}
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
const _require = require('./Utils'),
createAliasResolver = _require.createAliasResolver,
getModules = _require.getModules;
const _require2 = require('../Utils'),
indent = _require2.indent;
const _require3 = require('../../parsers/flow/modules/utils'),
unwrapNullable = _require3.unwrapNullable;
const ModuleClassDeclarationTemplate = ({
hasteModuleName,
moduleProperties,
}) => {
return `class JSI_EXPORT ${hasteModuleName}CxxSpecJSI : public TurboModule {
protected:
${hasteModuleName}CxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker);
public:
${indent(moduleProperties.join('\n'), 2)}
};`;
};
const ModuleSpecClassDeclarationTemplate = ({
hasteModuleName,
moduleName,
moduleProperties,
}) => {
return `template <typename T>
class JSI_EXPORT ${hasteModuleName}CxxSpec : public TurboModule {
public:
jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &propName) override {
return delegate_.get(rt, propName);
}
protected:
${hasteModuleName}CxxSpec(std::shared_ptr<CallInvoker> jsInvoker)
: TurboModule("${moduleName}", jsInvoker),
delegate_(static_cast<T*>(this), jsInvoker) {}
private:
class Delegate : public ${hasteModuleName}CxxSpecJSI {
public:
Delegate(T *instance, std::shared_ptr<CallInvoker> jsInvoker) :
${hasteModuleName}CxxSpecJSI(std::move(jsInvoker)), instance_(instance) {}
${indent(moduleProperties.join('\n'), 4)}
private:
T *instance_;
};
Delegate delegate_;
};`;
};
const FileTemplate = ({modules}) => {
return `/**
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
*
* Do not edit this file as changes may cause incorrect behavior and will be lost
* once the code is regenerated.
*
* ${'@'}generated by codegen project: GenerateModuleH.js
*/
#pragma once
#include <ReactCommon/TurboModule.h>
#include <react/bridging/Bridging.h>
namespace facebook {
namespace react {
${modules.join('\n\n')}
} // namespace react
} // namespace facebook
`;
};
function translatePrimitiveJSTypeToCpp(
nullableTypeAnnotation,
createErrorMessage,
resolveAlias,
) {
const _unwrapNullable = unwrapNullable(nullableTypeAnnotation),
_unwrapNullable2 = _slicedToArray(_unwrapNullable, 2),
typeAnnotation = _unwrapNullable2[0],
nullable = _unwrapNullable2[1];
let realTypeAnnotation = typeAnnotation;
if (realTypeAnnotation.type === 'TypeAliasTypeAnnotation') {
realTypeAnnotation = resolveAlias(realTypeAnnotation.name);
}
function wrap(type) {
return nullable ? `std::optional<${type}>` : type;
}
switch (realTypeAnnotation.type) {
case 'ReservedTypeAnnotation':
switch (realTypeAnnotation.name) {
case 'RootTag':
return wrap('double');
default:
realTypeAnnotation.name;
throw new Error(createErrorMessage(realTypeAnnotation.name));
}
case 'VoidTypeAnnotation':
return 'void';
case 'StringTypeAnnotation':
return wrap('jsi::String');
case 'NumberTypeAnnotation':
return wrap('double');
case 'DoubleTypeAnnotation':
return wrap('double');
case 'FloatTypeAnnotation':
return wrap('double');
case 'Int32TypeAnnotation':
return wrap('int');
case 'BooleanTypeAnnotation':
return wrap('bool');
case 'GenericObjectTypeAnnotation':
return wrap('jsi::Object');
case 'ObjectTypeAnnotation':
return wrap('jsi::Object');
case 'ArrayTypeAnnotation':
return wrap('jsi::Array');
case 'FunctionTypeAnnotation':
return wrap('jsi::Function');
case 'PromiseTypeAnnotation':
return wrap('jsi::Value');
case 'MixedTypeAnnotation':
return wrap('jsi::Value');
default:
realTypeAnnotation.type;
throw new Error(createErrorMessage(realTypeAnnotation.type));
}
}
function translatePropertyToCpp(prop, resolveAlias, abstract = false) {
const _unwrapNullable3 = unwrapNullable(prop.typeAnnotation),
_unwrapNullable4 = _slicedToArray(_unwrapNullable3, 1),
propTypeAnnotation = _unwrapNullable4[0];
const params = propTypeAnnotation.params.map(
param => `std::move(${param.name})`,
);
const paramTypes = propTypeAnnotation.params.map(param => {
const translatedParam = translatePrimitiveJSTypeToCpp(
param.typeAnnotation,
typeName =>
`Unsupported type for param "${param.name}" in ${prop.name}. Found: ${typeName}`,
resolveAlias,
);
return `${translatedParam} ${param.name}`;
});
const returnType = translatePrimitiveJSTypeToCpp(
propTypeAnnotation.returnTypeAnnotation,
typeName => `Unsupported return type for ${prop.name}. Found: ${typeName}`,
resolveAlias,
);
// The first param will always be the runtime reference.
paramTypes.unshift('jsi::Runtime &rt');
const method = `${returnType} ${prop.name}(${paramTypes.join(', ')})`;
if (abstract) {
return `virtual ${method} = 0;`;
}
return `${method} override {
static_assert(
bridging::getParameterCount(&T::${prop.name}) == ${paramTypes.length},
"Expected ${prop.name}(...) to have ${paramTypes.length} parameters");
return bridging::callFromJs<${returnType}>(
rt, &T::${prop.name}, jsInvoker_, ${['instance_', ...params].join(', ')});
}`;
}
module.exports = {
generate(libraryName, schema, packageName, assumeNonnull = false) {
const nativeModules = getModules(schema);
const modules = Object.keys(nativeModules).flatMap(hasteModuleName => {
const _nativeModules$hasteM = nativeModules[hasteModuleName],
aliases = _nativeModules$hasteM.aliases,
properties = _nativeModules$hasteM.spec.properties,
_nativeModules$hasteM2 = _slicedToArray(
_nativeModules$hasteM.moduleNames,
1,
),
moduleName = _nativeModules$hasteM2[0];
const resolveAlias = createAliasResolver(aliases);
return [
ModuleClassDeclarationTemplate({
hasteModuleName,
moduleProperties: properties.map(prop =>
translatePropertyToCpp(prop, resolveAlias, true),
),
}),
ModuleSpecClassDeclarationTemplate({
hasteModuleName,
moduleName,
moduleProperties: properties.map(prop =>
translatePropertyToCpp(prop, resolveAlias),
),
}),
];
});
const fileName = `${libraryName}JSI.h`;
const replacedTemplate = FileTemplate({
modules,
});
return new Map([[fileName, replacedTemplate]]);
},
};