react-native-codegen
Version:
⚛️ Code generation tools for React Native
887 lines (878 loc) • 27.2 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
*/
'use strict';
function ownKeys(e, r) {
var t = Object.keys(e);
if (Object.getOwnPropertySymbols) {
var o = Object.getOwnPropertySymbols(e);
r &&
(o = o.filter(function (r) {
return Object.getOwnPropertyDescriptor(e, r).enumerable;
})),
t.push.apply(t, o);
}
return t;
}
function _objectSpread(e) {
for (var r = 1; r < arguments.length; r++) {
var t = null != arguments[r] ? arguments[r] : {};
r % 2
? ownKeys(Object(t), !0).forEach(function (r) {
_defineProperty(e, r, t[r]);
})
: Object.getOwnPropertyDescriptors
? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t))
: ownKeys(Object(t)).forEach(function (r) {
Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
});
}
return e;
}
function _defineProperty(obj, key, value) {
key = _toPropertyKey(key);
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true,
});
} else {
obj[key] = value;
}
return obj;
}
function _toPropertyKey(arg) {
var key = _toPrimitive(arg, 'string');
return typeof key === 'symbol' ? key : String(key);
}
function _toPrimitive(input, hint) {
if (typeof input !== 'object' || input === null) return input;
var prim = input[Symbol.toPrimitive];
if (prim !== undefined) {
var res = prim.call(input, hint || 'default');
if (typeof res !== 'object') return res;
throw new TypeError('@@toPrimitive must return a primitive value.');
}
return (hint === 'string' ? String : Number)(input);
}
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.js'),
resolveTypeAnnotation = _require.resolveTypeAnnotation,
getTypes = _require.getTypes,
visit = _require.visit,
isModuleRegistryCall = _require.isModuleRegistryCall;
const _require2 = require('./utils'),
unwrapNullable = _require2.unwrapNullable,
wrapNullable = _require2.wrapNullable;
const _require3 = require('./errors.js'),
IncorrectlyParameterizedTypeScriptGenericParserError =
_require3.IncorrectlyParameterizedTypeScriptGenericParserError,
MisnamedModuleTypeScriptInterfaceParserError =
_require3.MisnamedModuleTypeScriptInterfaceParserError,
ModuleTypeScriptInterfaceNotFoundParserError =
_require3.ModuleTypeScriptInterfaceNotFoundParserError,
MoreThanOneModuleTypeScriptInterfaceParserError =
_require3.MoreThanOneModuleTypeScriptInterfaceParserError,
UnnamedFunctionParamParserError = _require3.UnnamedFunctionParamParserError,
UnsupportedArrayElementTypeAnnotationParserError =
_require3.UnsupportedArrayElementTypeAnnotationParserError,
UnsupportedTypeScriptGenericParserError =
_require3.UnsupportedTypeScriptGenericParserError,
UnsupportedTypeScriptTypeAnnotationParserError =
_require3.UnsupportedTypeScriptTypeAnnotationParserError,
UnsupportedFunctionParamTypeAnnotationParserError =
_require3.UnsupportedFunctionParamTypeAnnotationParserError,
UnsupportedFunctionReturnTypeAnnotationParserError =
_require3.UnsupportedFunctionReturnTypeAnnotationParserError,
UnsupportedModulePropertyParserError =
_require3.UnsupportedModulePropertyParserError,
UnsupportedObjectPropertyTypeAnnotationParserError =
_require3.UnsupportedObjectPropertyTypeAnnotationParserError,
UnsupportedObjectPropertyValueTypeAnnotationParserError =
_require3.UnsupportedObjectPropertyValueTypeAnnotationParserError,
UnusedModuleTypeScriptInterfaceParserError =
_require3.UnusedModuleTypeScriptInterfaceParserError,
MoreThanOneModuleRegistryCallsParserError =
_require3.MoreThanOneModuleRegistryCallsParserError,
UntypedModuleRegistryCallParserError =
_require3.UntypedModuleRegistryCallParserError,
IncorrectModuleRegistryCallTypeParameterParserError =
_require3.IncorrectModuleRegistryCallTypeParameterParserError,
IncorrectModuleRegistryCallArityParserError =
_require3.IncorrectModuleRegistryCallArityParserError,
IncorrectModuleRegistryCallArgumentTypeParserError =
_require3.IncorrectModuleRegistryCallArgumentTypeParserError;
const invariant = require('invariant');
function nullGuard(fn) {
return fn();
}
function translateArrayTypeAnnotation(
hasteModuleName,
types,
aliasMap,
cxxOnly,
tsArrayType,
tsElementType,
nullable,
) {
try {
/**
* TODO(T72031674): Migrate all our NativeModule specs to not use
* invalid Array ElementTypes. Then, make the elementType a required
* parameter.
*/
const _unwrapNullable = unwrapNullable(
translateTypeAnnotation(
hasteModuleName,
tsElementType,
types,
aliasMap,
/**
* TODO(T72031674): Ensure that all ParsingErrors that are thrown
* while parsing the array element don't get captured and collected.
* Why? If we detect any parsing error while parsing the element,
* we should default it to null down the line, here. This is
* the correct behaviour until we migrate all our NativeModule specs
* to be parseable.
*/
nullGuard,
cxxOnly,
),
),
_unwrapNullable2 = _slicedToArray(_unwrapNullable, 2),
elementType = _unwrapNullable2[0],
isElementTypeNullable = _unwrapNullable2[1];
if (elementType.type === 'VoidTypeAnnotation') {
throw new UnsupportedArrayElementTypeAnnotationParserError(
hasteModuleName,
tsElementType,
tsArrayType,
'void',
);
}
if (elementType.type === 'PromiseTypeAnnotation') {
throw new UnsupportedArrayElementTypeAnnotationParserError(
hasteModuleName,
tsElementType,
tsArrayType,
'Promise',
);
}
if (elementType.type === 'FunctionTypeAnnotation') {
throw new UnsupportedArrayElementTypeAnnotationParserError(
hasteModuleName,
tsElementType,
tsArrayType,
'FunctionTypeAnnotation',
);
}
const finalTypeAnnotation = {
type: 'ArrayTypeAnnotation',
elementType: wrapNullable(isElementTypeNullable, elementType),
};
return wrapNullable(nullable, finalTypeAnnotation);
} catch (ex) {
return wrapNullable(nullable, {
type: 'ArrayTypeAnnotation',
});
}
}
function translateTypeAnnotation(
hasteModuleName,
/**
* TODO(T108222691): Use flow-types for @babel/parser
*/
typeScriptTypeAnnotation,
types,
aliasMap,
tryParse,
cxxOnly,
) {
const _resolveTypeAnnotatio = resolveTypeAnnotation(
typeScriptTypeAnnotation,
types,
),
nullable = _resolveTypeAnnotatio.nullable,
typeAnnotation = _resolveTypeAnnotatio.typeAnnotation,
typeAliasResolutionStatus = _resolveTypeAnnotatio.typeAliasResolutionStatus;
switch (typeAnnotation.type) {
case 'TSArrayType': {
return translateArrayTypeAnnotation(
hasteModuleName,
types,
aliasMap,
cxxOnly,
'Array',
typeAnnotation.elementType,
nullable,
);
}
case 'TSTypeOperator': {
if (
typeAnnotation.operator === 'readonly' &&
typeAnnotation.typeAnnotation.type === 'TSArrayType'
) {
return translateArrayTypeAnnotation(
hasteModuleName,
types,
aliasMap,
cxxOnly,
'ReadonlyArray',
typeAnnotation.typeAnnotation.elementType,
nullable,
);
} else {
throw new UnsupportedTypeScriptGenericParserError(
hasteModuleName,
typeAnnotation,
);
}
}
case 'TSTypeReference': {
switch (typeAnnotation.typeName.name) {
case 'RootTag': {
return wrapNullable(nullable, {
type: 'ReservedTypeAnnotation',
name: 'RootTag',
});
}
case 'Promise': {
assertGenericTypeAnnotationHasExactlyOneTypeParameter(
hasteModuleName,
typeAnnotation,
);
return wrapNullable(nullable, {
type: 'PromiseTypeAnnotation',
});
}
case 'Array':
case 'ReadonlyArray': {
assertGenericTypeAnnotationHasExactlyOneTypeParameter(
hasteModuleName,
typeAnnotation,
);
return translateArrayTypeAnnotation(
hasteModuleName,
types,
aliasMap,
cxxOnly,
typeAnnotation.type,
typeAnnotation.typeParameters.params[0],
nullable,
);
}
case 'Readonly': {
assertGenericTypeAnnotationHasExactlyOneTypeParameter(
hasteModuleName,
typeAnnotation,
);
const _unwrapNullable3 = unwrapNullable(
translateTypeAnnotation(
hasteModuleName,
typeAnnotation.typeParameters.params[0],
types,
aliasMap,
tryParse,
cxxOnly,
),
),
_unwrapNullable4 = _slicedToArray(_unwrapNullable3, 2),
paramType = _unwrapNullable4[0],
isParamNullable = _unwrapNullable4[1];
return wrapNullable(nullable || isParamNullable, paramType);
}
case 'Stringish': {
return wrapNullable(nullable, {
type: 'StringTypeAnnotation',
});
}
case 'Int32': {
return wrapNullable(nullable, {
type: 'Int32TypeAnnotation',
});
}
case 'Double': {
return wrapNullable(nullable, {
type: 'DoubleTypeAnnotation',
});
}
case 'Float': {
return wrapNullable(nullable, {
type: 'FloatTypeAnnotation',
});
}
case 'UnsafeObject':
case 'Object': {
return wrapNullable(nullable, {
type: 'GenericObjectTypeAnnotation',
});
}
default: {
throw new UnsupportedTypeScriptGenericParserError(
hasteModuleName,
typeAnnotation,
);
}
}
}
case 'TSTypeLiteral': {
const objectTypeAnnotation = {
type: 'ObjectTypeAnnotation',
// $FlowFixMe[missing-type-arg]
properties: typeAnnotation.members
.map(property => {
return tryParse(() => {
if (property.type !== 'TSPropertySignature') {
throw new UnsupportedObjectPropertyTypeAnnotationParserError(
hasteModuleName,
property,
property.type,
);
}
const _property$optional = property.optional,
optional =
_property$optional === void 0 ? false : _property$optional,
key = property.key;
const _unwrapNullable5 = unwrapNullable(
translateTypeAnnotation(
hasteModuleName,
property.typeAnnotation.typeAnnotation,
types,
aliasMap,
tryParse,
cxxOnly,
),
),
_unwrapNullable6 = _slicedToArray(_unwrapNullable5, 2),
propertyTypeAnnotation = _unwrapNullable6[0],
isPropertyNullable = _unwrapNullable6[1];
if (propertyTypeAnnotation.type === 'FunctionTypeAnnotation') {
throw new UnsupportedObjectPropertyValueTypeAnnotationParserError(
hasteModuleName,
property.typeAnnotation.typeAnnotation,
property.key,
propertyTypeAnnotation.type,
);
}
if (propertyTypeAnnotation.type === 'VoidTypeAnnotation') {
throw new UnsupportedObjectPropertyValueTypeAnnotationParserError(
hasteModuleName,
property.typeAnnotation.typeAnnotation,
property.key,
'void',
);
}
if (propertyTypeAnnotation.type === 'PromiseTypeAnnotation') {
throw new UnsupportedObjectPropertyValueTypeAnnotationParserError(
hasteModuleName,
property.typeAnnotation.typeAnnotation,
property.key,
'Promise',
);
}
return {
name: key.name,
optional,
typeAnnotation: wrapNullable(
isPropertyNullable,
propertyTypeAnnotation,
),
};
});
})
.filter(Boolean),
};
if (!typeAliasResolutionStatus.successful) {
return wrapNullable(nullable, objectTypeAnnotation);
}
/**
* All aliases RHS are required.
*/
aliasMap[typeAliasResolutionStatus.aliasName] = objectTypeAnnotation;
/**
* Nullability of type aliases is transitive.
*
* Consider this case:
*
* type Animal = ?{
* name: string,
* };
*
* type B = Animal
*
* export interface Spec extends TurboModule {
* +greet: (animal: B) => void;
* }
*
* In this case, we follow B to Animal, and then Animal to ?{name: string}.
*
* We:
* 1. Replace `+greet: (animal: B) => void;` with `+greet: (animal: ?Animal) => void;`,
* 2. Pretend that Animal = {name: string}.
*
* Why do we do this?
* 1. In ObjC, we need to generate a struct called Animal, not B.
* 2. This design is simpler than managing nullability within both the type alias usage, and the type alias RHS.
* 3. What does it mean for a C++ struct, which is what this type alias RHS will generate, to be nullable? ¯\_(ツ)_/¯
* Nullability is a concept that only makes sense when talking about instances (i.e: usages) of the C++ structs.
* Hence, it's better to manage nullability within the actual TypeAliasTypeAnnotation nodes, and not the
* associated ObjectTypeAnnotations.
*/
return wrapNullable(nullable, {
type: 'TypeAliasTypeAnnotation',
name: typeAliasResolutionStatus.aliasName,
});
}
case 'TSBooleanKeyword': {
return wrapNullable(nullable, {
type: 'BooleanTypeAnnotation',
});
}
case 'TSNumberKeyword': {
return wrapNullable(nullable, {
type: 'NumberTypeAnnotation',
});
}
case 'TSVoidKeyword': {
return wrapNullable(nullable, {
type: 'VoidTypeAnnotation',
});
}
case 'TSStringKeyword': {
return wrapNullable(nullable, {
type: 'StringTypeAnnotation',
});
}
case 'TSFunctionType': {
return wrapNullable(
nullable,
translateFunctionTypeAnnotation(
hasteModuleName,
typeAnnotation,
types,
aliasMap,
tryParse,
cxxOnly,
),
);
}
case 'TSUnknownKeyword': {
if (cxxOnly) {
return wrapNullable(nullable, {
type: 'MixedTypeAnnotation',
});
}
// Fallthrough
}
default: {
throw new UnsupportedTypeScriptTypeAnnotationParserError(
hasteModuleName,
typeAnnotation,
);
}
}
}
function assertGenericTypeAnnotationHasExactlyOneTypeParameter(
moduleName,
/**
* TODO(T108222691): Use flow-types for @babel/parser
*/
typeAnnotation,
) {
if (typeAnnotation.typeParameters == null) {
throw new IncorrectlyParameterizedTypeScriptGenericParserError(
moduleName,
typeAnnotation,
);
}
invariant(
typeAnnotation.typeParameters.type === 'TSTypeParameterInstantiation',
"assertGenericTypeAnnotationHasExactlyOneTypeParameter: Type parameters must be an AST node of type 'TSTypeParameterInstantiation'",
);
if (typeAnnotation.typeParameters.params.length !== 1) {
throw new IncorrectlyParameterizedTypeScriptGenericParserError(
moduleName,
typeAnnotation,
);
}
}
function translateFunctionTypeAnnotation(
hasteModuleName,
// TODO(T108222691): Use flow-types for @babel/parser
typescriptFunctionTypeAnnotation,
types,
aliasMap,
tryParse,
cxxOnly,
) {
const params = [];
for (const typeScriptParam of typescriptFunctionTypeAnnotation.parameters) {
const parsedParam = tryParse(() => {
if (typeScriptParam.typeAnnotation == null) {
throw new UnnamedFunctionParamParserError(
typeScriptParam,
hasteModuleName,
);
}
const paramName = typeScriptParam.name;
const _unwrapNullable7 = unwrapNullable(
translateTypeAnnotation(
hasteModuleName,
typeScriptParam.typeAnnotation.typeAnnotation,
types,
aliasMap,
tryParse,
cxxOnly,
),
),
_unwrapNullable8 = _slicedToArray(_unwrapNullable7, 2),
paramTypeAnnotation = _unwrapNullable8[0],
isParamTypeAnnotationNullable = _unwrapNullable8[1];
if (paramTypeAnnotation.type === 'VoidTypeAnnotation') {
throw new UnsupportedFunctionParamTypeAnnotationParserError(
hasteModuleName,
typeScriptParam.typeAnnotation,
paramName,
'void',
);
}
if (paramTypeAnnotation.type === 'PromiseTypeAnnotation') {
throw new UnsupportedFunctionParamTypeAnnotationParserError(
hasteModuleName,
typeScriptParam.typeAnnotation,
paramName,
'Promise',
);
}
return {
name: typeScriptParam.name,
optional: Boolean(typeScriptParam.optional),
typeAnnotation: wrapNullable(
isParamTypeAnnotationNullable,
paramTypeAnnotation,
),
};
});
if (parsedParam != null) {
params.push(parsedParam);
}
}
const _unwrapNullable9 = unwrapNullable(
translateTypeAnnotation(
hasteModuleName,
typescriptFunctionTypeAnnotation.typeAnnotation.typeAnnotation,
types,
aliasMap,
tryParse,
cxxOnly,
),
),
_unwrapNullable10 = _slicedToArray(_unwrapNullable9, 2),
returnTypeAnnotation = _unwrapNullable10[0],
isReturnTypeAnnotationNullable = _unwrapNullable10[1];
if (!cxxOnly && returnTypeAnnotation.type === 'FunctionTypeAnnotation') {
throw new UnsupportedFunctionReturnTypeAnnotationParserError(
hasteModuleName,
typescriptFunctionTypeAnnotation.returnType,
'FunctionTypeAnnotation',
);
}
return {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: wrapNullable(
isReturnTypeAnnotationNullable,
returnTypeAnnotation,
),
params,
};
}
function buildPropertySchema(
hasteModuleName,
// TODO(T108222691): Use flow-types for @babel/parser
property,
types,
aliasMap,
tryParse,
cxxOnly,
) {
let nullable = false;
let key = property.key;
let value =
property.type === 'TSMethodSignature' ? property : property.typeAnnotation;
const methodName = key.name;
var _resolveTypeAnnotatio2 = resolveTypeAnnotation(value, types);
nullable = _resolveTypeAnnotatio2.nullable;
value = _resolveTypeAnnotatio2.typeAnnotation;
if (value.type !== 'TSFunctionType' && value.type !== 'TSMethodSignature') {
throw new UnsupportedModulePropertyParserError(
hasteModuleName,
property.value,
property.key.name,
value.type,
);
}
return {
name: methodName,
optional: Boolean(property.optional),
typeAnnotation: wrapNullable(
nullable,
translateFunctionTypeAnnotation(
hasteModuleName,
value,
types,
aliasMap,
tryParse,
cxxOnly,
),
),
};
}
function isModuleInterface(node) {
return (
node.type === 'TSInterfaceDeclaration' &&
node.extends.length === 1 &&
node.extends[0].type === 'TSExpressionWithTypeArguments' &&
node.extends[0].expression.name === 'TurboModule'
);
}
function buildModuleSchema(
hasteModuleName,
/**
* TODO(T108222691): Use flow-types for @babel/parser
*/
ast,
tryParse,
) {
const types = getTypes(ast);
const moduleSpecs = Object.values(types).filter(isModuleInterface);
if (moduleSpecs.length === 0) {
throw new ModuleTypeScriptInterfaceNotFoundParserError(
hasteModuleName,
ast,
);
}
if (moduleSpecs.length > 1) {
throw new MoreThanOneModuleTypeScriptInterfaceParserError(
hasteModuleName,
moduleSpecs,
moduleSpecs.map(node => node.id.name),
);
}
const _moduleSpecs = _slicedToArray(moduleSpecs, 1),
moduleSpec = _moduleSpecs[0];
if (moduleSpec.id.name !== 'Spec') {
throw new MisnamedModuleTypeScriptInterfaceParserError(
hasteModuleName,
moduleSpec.id,
);
}
// Parse Module Names
const moduleName = tryParse(() => {
const callExpressions = [];
visit(ast, {
CallExpression(node) {
if (isModuleRegistryCall(node)) {
callExpressions.push(node);
}
},
});
if (callExpressions.length === 0) {
throw new UnusedModuleTypeScriptInterfaceParserError(
hasteModuleName,
moduleSpec,
);
}
if (callExpressions.length > 1) {
throw new MoreThanOneModuleRegistryCallsParserError(
hasteModuleName,
callExpressions,
callExpressions.length,
);
}
const callExpression = callExpressions[0];
const typeParameters = callExpression.typeParameters;
const methodName = callExpression.callee.property.name;
if (callExpression.arguments.length !== 1) {
throw new IncorrectModuleRegistryCallArityParserError(
hasteModuleName,
callExpression,
methodName,
callExpression.arguments.length,
);
}
if (callExpression.arguments[0].type !== 'StringLiteral') {
const type = callExpression.arguments[0].type;
throw new IncorrectModuleRegistryCallArgumentTypeParserError(
hasteModuleName,
callExpression.arguments[0],
methodName,
type,
);
}
const $moduleName = callExpression.arguments[0].value;
if (typeParameters == null) {
throw new UntypedModuleRegistryCallParserError(
hasteModuleName,
callExpression,
methodName,
$moduleName,
);
}
if (
typeParameters.type !== 'TSTypeParameterInstantiation' ||
typeParameters.params.length !== 1 ||
typeParameters.params[0].type !== 'TSTypeReference' ||
typeParameters.params[0].typeName.name !== 'Spec'
) {
throw new IncorrectModuleRegistryCallTypeParameterParserError(
hasteModuleName,
typeParameters,
methodName,
$moduleName,
);
}
return $moduleName;
});
const moduleNames = moduleName == null ? [] : [moduleName];
// Some module names use platform suffix to indicate platform-exclusive modules.
// Eventually this should be made explicit in the Flow type itself.
// Also check the hasteModuleName for platform suffix.
// Note: this shape is consistent with ComponentSchema.
let cxxOnly = false;
const excludedPlatforms = [];
const namesToValidate = [...moduleNames, hasteModuleName];
namesToValidate.forEach(name => {
if (name.endsWith('Android')) {
excludedPlatforms.push('iOS');
} else if (name.endsWith('IOS')) {
excludedPlatforms.push('android');
} else if (name.endsWith('Cxx')) {
cxxOnly = true;
excludedPlatforms.push('iOS', 'android');
}
});
// $FlowFixMe[missing-type-arg]
return moduleSpec.body.body
.filter(
property =>
property.type === 'TSMethodSignature' ||
property.type === 'TSPropertySignature',
)
.map(property => {
const aliasMap = {};
return tryParse(() => ({
aliasMap: aliasMap,
propertyShape: buildPropertySchema(
hasteModuleName,
property,
types,
aliasMap,
tryParse,
cxxOnly,
),
}));
})
.filter(Boolean)
.reduce(
(moduleSchema, {aliasMap, propertyShape}) => {
return {
type: 'NativeModule',
aliases: _objectSpread(
_objectSpread({}, moduleSchema.aliases),
aliasMap,
),
spec: {
properties: [...moduleSchema.spec.properties, propertyShape],
},
moduleNames: moduleSchema.moduleNames,
excludedPlatforms: moduleSchema.excludedPlatforms,
};
},
{
type: 'NativeModule',
aliases: {},
spec: {
properties: [],
},
moduleNames: moduleNames,
excludedPlatforms:
excludedPlatforms.length !== 0 ? [...excludedPlatforms] : undefined,
},
);
}
module.exports = {
buildModuleSchema,
};