mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Summary: This PR is part of https://github.com/facebook/react-native/issues/34872. This PR extracts `IncorrectModuleRegistryCallTypeParameterParserError` exception to a separate function inside an `error-utils.js` file ## Changelog [Internal] [Changed] - Extract `IncorrectModuleRegistryCallTypeParameterParserError` to a seperate function inside `error-utils.js` Pull Request resolved: https://github.com/facebook/react-native/pull/34941 Test Plan: ```sh yarn jest react-native-codegen ``` Added unit case in `error-utils-test.js` file <img width="940" alt="Screenshot 2022-10-11 at 4 42 03 PM" src="https://user-images.githubusercontent.com/86605635/195076564-3b023c17-661c-4330-805c-0216c4391d59.png"> Reviewed By: dmytrorykun Differential Revision: D40296642 Pulled By: cipolleschi fbshipit-source-id: 7c7bba6a4f68e9b8fa4729a7651f22cce6d7ca6e
749 lines
21 KiB
JavaScript
749 lines
21 KiB
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.
|
|
*
|
|
* @flow strict-local
|
|
* @format
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
import type {
|
|
NamedShape,
|
|
NativeModuleAliasMap,
|
|
NativeModuleArrayTypeAnnotation,
|
|
NativeModuleBaseTypeAnnotation,
|
|
NativeModuleFunctionTypeAnnotation,
|
|
NativeModuleParamTypeAnnotation,
|
|
NativeModulePropertyShape,
|
|
NativeModuleSchema,
|
|
Nullable,
|
|
} from '../../../CodegenSchema.js';
|
|
|
|
import type {ParserErrorCapturer, TypeDeclarationMap} from '../../utils';
|
|
import type {NativeModuleTypeAnnotation} from '../../../CodegenSchema.js';
|
|
|
|
const {throwIfMoreThanOneModuleRegistryCalls} = require('../../error-utils');
|
|
const {
|
|
resolveTypeAnnotation,
|
|
getTypes,
|
|
visit,
|
|
isModuleRegistryCall,
|
|
} = require('../utils.js');
|
|
const {
|
|
unwrapNullable,
|
|
wrapNullable,
|
|
assertGenericTypeAnnotationHasExactlyOneTypeParameter,
|
|
} = require('../../parsers-commons');
|
|
const {
|
|
emitBoolean,
|
|
emitDouble,
|
|
emitNumber,
|
|
emitInt32,
|
|
emitObject,
|
|
emitPromise,
|
|
emitRootTag,
|
|
emitVoid,
|
|
emitStringish,
|
|
typeAliasResolution,
|
|
} = require('../../parsers-primitives');
|
|
const {
|
|
MisnamedModuleInterfaceParserError,
|
|
MoreThanOneModuleInterfaceParserError,
|
|
UnnamedFunctionParamParserError,
|
|
UnsupportedArrayElementTypeAnnotationParserError,
|
|
UnsupportedGenericParserError,
|
|
UnsupportedTypeAnnotationParserError,
|
|
UnsupportedFunctionParamTypeAnnotationParserError,
|
|
UnsupportedFunctionReturnTypeAnnotationParserError,
|
|
UnsupportedEnumDeclarationParserError,
|
|
UnsupportedUnionTypeAnnotationParserError,
|
|
UnsupportedModulePropertyParserError,
|
|
UnsupportedObjectPropertyTypeAnnotationParserError,
|
|
UnsupportedObjectPropertyValueTypeAnnotationParserError,
|
|
UntypedModuleRegistryCallParserError,
|
|
IncorrectModuleRegistryCallArgumentTypeParserError,
|
|
} = require('../../errors.js');
|
|
|
|
const {
|
|
throwIfModuleInterfaceNotFound,
|
|
throwIfUnusedModuleInterfaceParserError,
|
|
throwIfWrongNumberOfCallExpressionArgs,
|
|
throwIfIncorrectModuleRegistryCallTypeParameterParserError,
|
|
} = require('../../error-utils');
|
|
|
|
const language = 'Flow';
|
|
|
|
function nullGuard<T>(fn: () => T): ?T {
|
|
return fn();
|
|
}
|
|
|
|
function translateTypeAnnotation(
|
|
hasteModuleName: string,
|
|
/**
|
|
* TODO(T71778680): Flow-type this node.
|
|
*/
|
|
flowTypeAnnotation: $FlowFixMe,
|
|
types: TypeDeclarationMap,
|
|
aliasMap: {...NativeModuleAliasMap},
|
|
tryParse: ParserErrorCapturer,
|
|
cxxOnly: boolean,
|
|
): Nullable<NativeModuleTypeAnnotation> {
|
|
const {nullable, typeAnnotation, typeAliasResolutionStatus} =
|
|
resolveTypeAnnotation(flowTypeAnnotation, types);
|
|
|
|
switch (typeAnnotation.type) {
|
|
case 'GenericTypeAnnotation': {
|
|
switch (typeAnnotation.id.name) {
|
|
case 'RootTag': {
|
|
return emitRootTag(nullable);
|
|
}
|
|
case 'Promise': {
|
|
return emitPromise(
|
|
hasteModuleName,
|
|
typeAnnotation,
|
|
language,
|
|
nullable,
|
|
);
|
|
}
|
|
case 'Array':
|
|
case '$ReadOnlyArray': {
|
|
assertGenericTypeAnnotationHasExactlyOneTypeParameter(
|
|
hasteModuleName,
|
|
typeAnnotation,
|
|
language,
|
|
);
|
|
|
|
try {
|
|
/**
|
|
* TODO(T72031674): Migrate all our NativeModule specs to not use
|
|
* invalid Array ElementTypes. Then, make the elementType a required
|
|
* parameter.
|
|
*/
|
|
const [elementType, isElementTypeNullable] = unwrapNullable(
|
|
translateTypeAnnotation(
|
|
hasteModuleName,
|
|
typeAnnotation.typeParameters.params[0],
|
|
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,
|
|
),
|
|
);
|
|
|
|
if (elementType.type === 'VoidTypeAnnotation') {
|
|
throw new UnsupportedArrayElementTypeAnnotationParserError(
|
|
hasteModuleName,
|
|
typeAnnotation.typeParameters.params[0],
|
|
typeAnnotation.type,
|
|
'void',
|
|
language,
|
|
);
|
|
}
|
|
|
|
if (elementType.type === 'PromiseTypeAnnotation') {
|
|
throw new UnsupportedArrayElementTypeAnnotationParserError(
|
|
hasteModuleName,
|
|
typeAnnotation.typeParameters.params[0],
|
|
typeAnnotation.type,
|
|
'Promise',
|
|
language,
|
|
);
|
|
}
|
|
|
|
if (elementType.type === 'FunctionTypeAnnotation') {
|
|
throw new UnsupportedArrayElementTypeAnnotationParserError(
|
|
hasteModuleName,
|
|
typeAnnotation.typeParameters.params[0],
|
|
typeAnnotation.type,
|
|
'FunctionTypeAnnotation',
|
|
language,
|
|
);
|
|
}
|
|
|
|
const finalTypeAnnotation: NativeModuleArrayTypeAnnotation<
|
|
Nullable<NativeModuleBaseTypeAnnotation>,
|
|
> = {
|
|
type: 'ArrayTypeAnnotation',
|
|
elementType: wrapNullable(isElementTypeNullable, elementType),
|
|
};
|
|
|
|
return wrapNullable(nullable, finalTypeAnnotation);
|
|
} catch (ex) {
|
|
return wrapNullable(nullable, {
|
|
type: 'ArrayTypeAnnotation',
|
|
});
|
|
}
|
|
}
|
|
case '$ReadOnly': {
|
|
assertGenericTypeAnnotationHasExactlyOneTypeParameter(
|
|
hasteModuleName,
|
|
typeAnnotation,
|
|
language,
|
|
);
|
|
|
|
const [paramType, isParamNullable] = unwrapNullable(
|
|
translateTypeAnnotation(
|
|
hasteModuleName,
|
|
typeAnnotation.typeParameters.params[0],
|
|
types,
|
|
aliasMap,
|
|
tryParse,
|
|
cxxOnly,
|
|
),
|
|
);
|
|
|
|
return wrapNullable(nullable || isParamNullable, paramType);
|
|
}
|
|
case 'Stringish': {
|
|
return emitStringish(nullable);
|
|
}
|
|
case 'Int32': {
|
|
return emitInt32(nullable);
|
|
}
|
|
case 'Double': {
|
|
return emitDouble(nullable);
|
|
}
|
|
case 'Float': {
|
|
return wrapNullable(nullable, {
|
|
type: 'FloatTypeAnnotation',
|
|
});
|
|
}
|
|
case 'UnsafeObject':
|
|
case 'Object': {
|
|
return emitObject(nullable);
|
|
}
|
|
default: {
|
|
const maybeEumDeclaration = types[typeAnnotation.id.name];
|
|
if (
|
|
cxxOnly &&
|
|
maybeEumDeclaration &&
|
|
maybeEumDeclaration.type === 'EnumDeclaration'
|
|
) {
|
|
const memberType = maybeEumDeclaration.body.type
|
|
.replace('EnumNumberBody', 'NumberTypeAnnotation')
|
|
.replace('EnumStringBody', 'StringTypeAnnotation');
|
|
if (
|
|
memberType === 'NumberTypeAnnotation' ||
|
|
memberType === 'StringTypeAnnotation'
|
|
) {
|
|
return wrapNullable(nullable, {
|
|
type: 'EnumDeclaration',
|
|
memberType: memberType,
|
|
});
|
|
} else {
|
|
throw new UnsupportedEnumDeclarationParserError(
|
|
hasteModuleName,
|
|
typeAnnotation,
|
|
memberType,
|
|
language,
|
|
);
|
|
}
|
|
}
|
|
throw new UnsupportedGenericParserError(
|
|
hasteModuleName,
|
|
typeAnnotation,
|
|
language,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
case 'ObjectTypeAnnotation': {
|
|
const objectTypeAnnotation = {
|
|
type: 'ObjectTypeAnnotation',
|
|
// $FlowFixMe[missing-type-arg]
|
|
properties: (typeAnnotation.properties: Array<$FlowFixMe>)
|
|
.map<?NamedShape<Nullable<NativeModuleBaseTypeAnnotation>>>(
|
|
property => {
|
|
return tryParse(() => {
|
|
if (property.type !== 'ObjectTypeProperty') {
|
|
throw new UnsupportedObjectPropertyTypeAnnotationParserError(
|
|
hasteModuleName,
|
|
property,
|
|
property.type,
|
|
language,
|
|
);
|
|
}
|
|
|
|
const {optional, key} = property;
|
|
|
|
const [propertyTypeAnnotation, isPropertyNullable] =
|
|
unwrapNullable(
|
|
translateTypeAnnotation(
|
|
hasteModuleName,
|
|
property.value,
|
|
types,
|
|
aliasMap,
|
|
tryParse,
|
|
cxxOnly,
|
|
),
|
|
);
|
|
|
|
if (propertyTypeAnnotation.type === 'FunctionTypeAnnotation') {
|
|
throw new UnsupportedObjectPropertyValueTypeAnnotationParserError(
|
|
hasteModuleName,
|
|
property.value,
|
|
property.key,
|
|
propertyTypeAnnotation.type,
|
|
language,
|
|
);
|
|
}
|
|
|
|
if (propertyTypeAnnotation.type === 'VoidTypeAnnotation') {
|
|
throw new UnsupportedObjectPropertyValueTypeAnnotationParserError(
|
|
hasteModuleName,
|
|
property.value,
|
|
property.key,
|
|
'void',
|
|
language,
|
|
);
|
|
}
|
|
|
|
if (propertyTypeAnnotation.type === 'PromiseTypeAnnotation') {
|
|
throw new UnsupportedObjectPropertyValueTypeAnnotationParserError(
|
|
hasteModuleName,
|
|
property.value,
|
|
property.key,
|
|
'Promise',
|
|
language,
|
|
);
|
|
}
|
|
|
|
return {
|
|
name: key.name,
|
|
optional,
|
|
typeAnnotation: wrapNullable(
|
|
isPropertyNullable,
|
|
propertyTypeAnnotation,
|
|
),
|
|
};
|
|
});
|
|
},
|
|
)
|
|
.filter(Boolean),
|
|
};
|
|
|
|
return typeAliasResolution(
|
|
typeAliasResolutionStatus,
|
|
objectTypeAnnotation,
|
|
aliasMap,
|
|
nullable,
|
|
);
|
|
}
|
|
case 'BooleanTypeAnnotation': {
|
|
return emitBoolean(nullable);
|
|
}
|
|
case 'NumberTypeAnnotation': {
|
|
return emitNumber(nullable);
|
|
}
|
|
case 'VoidTypeAnnotation': {
|
|
return emitVoid(nullable);
|
|
}
|
|
case 'StringTypeAnnotation': {
|
|
return wrapNullable(nullable, {
|
|
type: 'StringTypeAnnotation',
|
|
});
|
|
}
|
|
case 'FunctionTypeAnnotation': {
|
|
return wrapNullable(
|
|
nullable,
|
|
translateFunctionTypeAnnotation(
|
|
hasteModuleName,
|
|
typeAnnotation,
|
|
types,
|
|
aliasMap,
|
|
tryParse,
|
|
cxxOnly,
|
|
),
|
|
);
|
|
}
|
|
case 'UnionTypeAnnotation': {
|
|
if (cxxOnly) {
|
|
// Remap literal names
|
|
const unionTypes = typeAnnotation.types
|
|
.map(
|
|
item =>
|
|
item.type
|
|
.replace('NumberLiteralTypeAnnotation', 'NumberTypeAnnotation')
|
|
.replace('StringLiteralTypeAnnotation', 'StringTypeAnnotation'),
|
|
// ObjectAnnotation is already 'correct'
|
|
)
|
|
.filter((value, index, self) => self.indexOf(value) === index);
|
|
// Only support unionTypes of the same kind
|
|
if (unionTypes.length > 1) {
|
|
throw new UnsupportedUnionTypeAnnotationParserError(
|
|
hasteModuleName,
|
|
typeAnnotation,
|
|
unionTypes,
|
|
language,
|
|
);
|
|
}
|
|
return wrapNullable(nullable, {
|
|
type: 'UnionTypeAnnotation',
|
|
memberType: unionTypes[0],
|
|
});
|
|
}
|
|
// Fallthrough
|
|
}
|
|
case 'MixedTypeAnnotation': {
|
|
if (cxxOnly) {
|
|
return wrapNullable(nullable, {
|
|
type: 'MixedTypeAnnotation',
|
|
});
|
|
}
|
|
// Fallthrough
|
|
}
|
|
default: {
|
|
throw new UnsupportedTypeAnnotationParserError(
|
|
hasteModuleName,
|
|
typeAnnotation,
|
|
language,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
function translateFunctionTypeAnnotation(
|
|
hasteModuleName: string,
|
|
// TODO(T71778680): This is a FunctionTypeAnnotation. Type this.
|
|
flowFunctionTypeAnnotation: $FlowFixMe,
|
|
types: TypeDeclarationMap,
|
|
aliasMap: {...NativeModuleAliasMap},
|
|
tryParse: ParserErrorCapturer,
|
|
cxxOnly: boolean,
|
|
): NativeModuleFunctionTypeAnnotation {
|
|
type Param = NamedShape<Nullable<NativeModuleParamTypeAnnotation>>;
|
|
const params: Array<Param> = [];
|
|
|
|
for (const flowParam of (flowFunctionTypeAnnotation.params: $ReadOnlyArray<$FlowFixMe>)) {
|
|
const parsedParam = tryParse(() => {
|
|
if (flowParam.name == null) {
|
|
throw new UnnamedFunctionParamParserError(
|
|
flowParam,
|
|
hasteModuleName,
|
|
language,
|
|
);
|
|
}
|
|
|
|
const paramName = flowParam.name.name;
|
|
const [paramTypeAnnotation, isParamTypeAnnotationNullable] =
|
|
unwrapNullable(
|
|
translateTypeAnnotation(
|
|
hasteModuleName,
|
|
flowParam.typeAnnotation,
|
|
types,
|
|
aliasMap,
|
|
tryParse,
|
|
cxxOnly,
|
|
),
|
|
);
|
|
|
|
if (paramTypeAnnotation.type === 'VoidTypeAnnotation') {
|
|
throw new UnsupportedFunctionParamTypeAnnotationParserError(
|
|
hasteModuleName,
|
|
flowParam.typeAnnotation,
|
|
paramName,
|
|
'void',
|
|
language,
|
|
);
|
|
}
|
|
|
|
if (paramTypeAnnotation.type === 'PromiseTypeAnnotation') {
|
|
throw new UnsupportedFunctionParamTypeAnnotationParserError(
|
|
hasteModuleName,
|
|
flowParam.typeAnnotation,
|
|
paramName,
|
|
'Promise',
|
|
language,
|
|
);
|
|
}
|
|
|
|
return {
|
|
name: flowParam.name.name,
|
|
optional: flowParam.optional,
|
|
typeAnnotation: wrapNullable(
|
|
isParamTypeAnnotationNullable,
|
|
paramTypeAnnotation,
|
|
),
|
|
};
|
|
});
|
|
|
|
if (parsedParam != null) {
|
|
params.push(parsedParam);
|
|
}
|
|
}
|
|
|
|
const [returnTypeAnnotation, isReturnTypeAnnotationNullable] = unwrapNullable(
|
|
translateTypeAnnotation(
|
|
hasteModuleName,
|
|
flowFunctionTypeAnnotation.returnType,
|
|
types,
|
|
aliasMap,
|
|
tryParse,
|
|
cxxOnly,
|
|
),
|
|
);
|
|
|
|
if (!cxxOnly && returnTypeAnnotation.type === 'FunctionTypeAnnotation') {
|
|
throw new UnsupportedFunctionReturnTypeAnnotationParserError(
|
|
hasteModuleName,
|
|
flowFunctionTypeAnnotation.returnType,
|
|
'FunctionTypeAnnotation',
|
|
language,
|
|
);
|
|
}
|
|
|
|
return {
|
|
type: 'FunctionTypeAnnotation',
|
|
returnTypeAnnotation: wrapNullable(
|
|
isReturnTypeAnnotationNullable,
|
|
returnTypeAnnotation,
|
|
),
|
|
params,
|
|
};
|
|
}
|
|
|
|
function buildPropertySchema(
|
|
hasteModuleName: string,
|
|
// TODO(T71778680): This is an ObjectTypeProperty containing either:
|
|
// - a FunctionTypeAnnotation or GenericTypeAnnotation
|
|
// - a NullableTypeAnnoation containing a FunctionTypeAnnotation or GenericTypeAnnotation
|
|
// Flow type this node
|
|
property: $FlowFixMe,
|
|
types: TypeDeclarationMap,
|
|
aliasMap: {...NativeModuleAliasMap},
|
|
tryParse: ParserErrorCapturer,
|
|
cxxOnly: boolean,
|
|
): NativeModulePropertyShape {
|
|
let nullable = false;
|
|
let {key, value} = property;
|
|
|
|
const methodName: string = key.name;
|
|
|
|
({nullable, typeAnnotation: value} = resolveTypeAnnotation(value, types));
|
|
|
|
if (value.type !== 'FunctionTypeAnnotation') {
|
|
throw new UnsupportedModulePropertyParserError(
|
|
hasteModuleName,
|
|
property.value,
|
|
property.key.name,
|
|
value.type,
|
|
language,
|
|
);
|
|
}
|
|
|
|
return {
|
|
name: methodName,
|
|
optional: property.optional,
|
|
typeAnnotation: wrapNullable(
|
|
nullable,
|
|
translateFunctionTypeAnnotation(
|
|
hasteModuleName,
|
|
value,
|
|
types,
|
|
aliasMap,
|
|
tryParse,
|
|
cxxOnly,
|
|
),
|
|
),
|
|
};
|
|
}
|
|
|
|
function isModuleInterface(node: $FlowFixMe) {
|
|
return (
|
|
node.type === 'InterfaceDeclaration' &&
|
|
node.extends.length === 1 &&
|
|
node.extends[0].type === 'InterfaceExtends' &&
|
|
node.extends[0].id.name === 'TurboModule'
|
|
);
|
|
}
|
|
|
|
function buildModuleSchema(
|
|
hasteModuleName: string,
|
|
/**
|
|
* TODO(T71778680): Flow-type this node.
|
|
*/
|
|
ast: $FlowFixMe,
|
|
tryParse: ParserErrorCapturer,
|
|
): NativeModuleSchema {
|
|
const types = getTypes(ast);
|
|
const moduleSpecs = (Object.values(types): $ReadOnlyArray<$FlowFixMe>).filter(
|
|
isModuleInterface,
|
|
);
|
|
|
|
throwIfModuleInterfaceNotFound(
|
|
moduleSpecs.length,
|
|
hasteModuleName,
|
|
ast,
|
|
language,
|
|
);
|
|
|
|
if (moduleSpecs.length > 1) {
|
|
throw new MoreThanOneModuleInterfaceParserError(
|
|
hasteModuleName,
|
|
moduleSpecs,
|
|
moduleSpecs.map(node => node.id.name),
|
|
language,
|
|
);
|
|
}
|
|
|
|
const [moduleSpec] = moduleSpecs;
|
|
|
|
if (moduleSpec.id.name !== 'Spec') {
|
|
throw new MisnamedModuleInterfaceParserError(
|
|
hasteModuleName,
|
|
moduleSpec.id,
|
|
language,
|
|
);
|
|
}
|
|
|
|
// Parse Module Names
|
|
const moduleName = tryParse((): string => {
|
|
const callExpressions = [];
|
|
visit(ast, {
|
|
CallExpression(node) {
|
|
if (isModuleRegistryCall(node)) {
|
|
callExpressions.push(node);
|
|
}
|
|
},
|
|
});
|
|
|
|
throwIfUnusedModuleInterfaceParserError(
|
|
hasteModuleName,
|
|
moduleSpec,
|
|
callExpressions,
|
|
language,
|
|
);
|
|
|
|
throwIfMoreThanOneModuleRegistryCalls(
|
|
hasteModuleName,
|
|
callExpressions,
|
|
callExpressions.length,
|
|
language,
|
|
);
|
|
|
|
const [callExpression] = callExpressions;
|
|
const {typeArguments} = callExpression;
|
|
const methodName = callExpression.callee.property.name;
|
|
|
|
throwIfWrongNumberOfCallExpressionArgs(
|
|
hasteModuleName,
|
|
callExpression,
|
|
methodName,
|
|
callExpression.arguments.length,
|
|
language,
|
|
);
|
|
|
|
if (callExpression.arguments[0].type !== 'Literal') {
|
|
const {type} = callExpression.arguments[0];
|
|
throw new IncorrectModuleRegistryCallArgumentTypeParserError(
|
|
hasteModuleName,
|
|
callExpression.arguments[0],
|
|
methodName,
|
|
type,
|
|
language,
|
|
);
|
|
}
|
|
|
|
const $moduleName = callExpression.arguments[0].value;
|
|
|
|
if (typeArguments == null) {
|
|
throw new UntypedModuleRegistryCallParserError(
|
|
hasteModuleName,
|
|
callExpression,
|
|
methodName,
|
|
$moduleName,
|
|
language,
|
|
);
|
|
}
|
|
|
|
throwIfIncorrectModuleRegistryCallTypeParameterParserError(
|
|
hasteModuleName,
|
|
typeArguments,
|
|
methodName,
|
|
$moduleName,
|
|
language,
|
|
);
|
|
|
|
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.properties: $ReadOnlyArray<$FlowFixMe>)
|
|
.filter(property => property.type === 'ObjectTypeProperty')
|
|
.map<?{
|
|
aliasMap: NativeModuleAliasMap,
|
|
propertyShape: NativeModulePropertyShape,
|
|
}>(property => {
|
|
const aliasMap: {...NativeModuleAliasMap} = {};
|
|
|
|
return tryParse(() => ({
|
|
aliasMap: aliasMap,
|
|
propertyShape: buildPropertySchema(
|
|
hasteModuleName,
|
|
property,
|
|
types,
|
|
aliasMap,
|
|
tryParse,
|
|
cxxOnly,
|
|
),
|
|
}));
|
|
})
|
|
.filter(Boolean)
|
|
.reduce(
|
|
(moduleSchema: NativeModuleSchema, {aliasMap, propertyShape}) => {
|
|
return {
|
|
type: 'NativeModule',
|
|
aliases: {...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,
|
|
};
|