Files
react-native/packages/react-native-codegen/src/parsers/flow/methods.js
T
Michał Osadnik b848af3b08 Add support for optional methods
Summary:
This diff adds an optional property.

#Facebook
Following this doc: https://our.intern.facebook.com/intern/wiki/React_Native/rctexport-interface-support/?vitals_event=wiki_click_navigation_link
I suppose it's vital to support this property as well. However i don't know if it's really useful, so thus I made a separated diff for it.

Reviewed By: TheSavior

Differential Revision: D16121408

fbshipit-source-id: 15491575e999ee4fcddfd176f52d927789458061
2019-07-08 06:24:19 -07:00

248 lines
6.2 KiB
JavaScript

/**
* Copyright (c) Facebook, Inc. and its 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 {
MethodTypeShape,
FunctionTypeAnnotationParam,
FunctionTypeAnnotationReturn,
PrimitiveTypeAnnotation,
FunctionTypeAnnotationParamTypeAnnotation,
} from '../../CodegenSchema.js';
function getValueFromTypes(value, types) {
if (value.type === 'GenericTypeAnnotation' && types[value.id.name]) {
return getValueFromTypes(types[value.id.name].right, types);
}
return value;
}
function wrapPrimitiveIntoTypeAnnotation(
methodName: string,
type:
| 'BooleanTypeAnnotation'
| 'NumberTypeAnnotation'
| 'StringTypeAnnotation',
paramName: string,
): PrimitiveTypeAnnotation {
switch (type) {
case 'BooleanTypeAnnotation':
case 'NumberTypeAnnotation':
case 'StringTypeAnnotation':
return {
type,
};
default:
(type: empty);
throw new Error(
`Unsupported param type for method "${methodName}", param "${paramName}". Found ${type}`,
);
}
}
function getElementTypeForArray(
name,
arrayParam,
paramName,
types: $ReadOnlyArray<TypesAST>,
): FunctionTypeAnnotationParamTypeAnnotation {
const typeAnnotation = getValueFromTypes(arrayParam, types);
if (
typeAnnotation.type === 'GenericTypeAnnotation' &&
typeAnnotation.id.name === 'Array'
) {
if (
typeAnnotation.typeParameters &&
typeAnnotation.typeParameters.params[0]
) {
return {
type: 'ArrayTypeAnnotation',
elementType: getElementTypeForArray(
name,
typeAnnotation.typeParameters.params[0],
'returning value',
types,
),
};
} else {
throw new Error(
`Unsupported type for ${name}, param: "${paramName}": expected to find annotation for type of nested array contents`,
);
}
}
const type = typeAnnotation.type;
if (type === 'AnyTypeAnnotation') {
return {
type,
};
}
return wrapPrimitiveIntoTypeAnnotation(name, type, paramName);
}
function getTypeAnnotationForParam(
name: string,
param,
types: $ReadOnlyArray<TypesAST>,
): FunctionTypeAnnotationParam {
const typeAnnotation = getValueFromTypes(param.typeAnnotation, types);
const paramName = param.name.name;
if (
typeAnnotation.type === 'GenericTypeAnnotation' &&
typeAnnotation.id.name === 'Array'
) {
if (
typeAnnotation.typeParameters &&
typeAnnotation.typeParameters.params[0]
) {
return {
name: paramName,
typeAnnotation: {
type: 'ArrayTypeAnnotation',
elementType: getElementTypeForArray(
name,
typeAnnotation.typeParameters.params[0],
paramName,
types,
),
},
};
} else {
throw new Error(
`Unsupported type for ${name}, param: "${paramName}": expected to find annotation for type of array contents`,
);
}
}
const type = typeAnnotation.type;
return {
name: paramName,
typeAnnotation: wrapPrimitiveIntoTypeAnnotation(name, type, paramName),
};
}
function getReturnTypeAnnotation(
methodName: string,
returnType,
types: $ReadOnlyArray<TypesAST>,
): FunctionTypeAnnotationReturn {
const typeAnnotation = getValueFromTypes(returnType, types);
if (
typeAnnotation.type === 'GenericTypeAnnotation' &&
typeAnnotation.id.name === 'Promise'
) {
if (
typeAnnotation.typeParameters &&
typeAnnotation.typeParameters.params[0]
) {
return {
type: 'PromiseTypeAnnotation',
resolvingType: getReturnTypeAnnotation(
methodName,
typeAnnotation.typeParameters.params[0],
types,
),
};
} else {
throw new Error(
`Unsupported return promise type for ${methodName}: expected to find annotation for type of promise content`,
);
}
}
if (
typeAnnotation.type === 'GenericTypeAnnotation' &&
typeAnnotation.id.name === 'Array'
) {
if (
typeAnnotation.typeParameters &&
typeAnnotation.typeParameters.params[0]
) {
return {
type: 'ArrayTypeAnnotation',
elementType: getElementTypeForArray(
methodName,
typeAnnotation.typeParameters.params[0],
'returning value',
types,
),
};
} else {
throw new Error(
`Unsupported return type for ${methodName}: expected to find annotation for type of array contents`,
);
}
}
const type = typeAnnotation.type;
switch (type) {
case 'BooleanTypeAnnotation':
case 'NumberTypeAnnotation':
case 'StringTypeAnnotation':
case 'VoidTypeAnnotation':
return {
type,
};
default:
(type: empty);
throw new Error(
`Unsupported return type for method "${methodName}", Found ${type}`,
);
}
}
function buildMethodSchema(
property: MethodAST,
types: $ReadOnlyArray<TypesAST>,
): MethodTypeShape {
const name: string = property.key.name;
const value = getValueFromTypes(property.value, types);
if (value.type !== 'FunctionTypeAnnotation') {
throw new Error(
`Only methods are supported as module properties. Found ${
value.type
} in ${property.key.name}`,
);
}
const params = value.params.map(param =>
getTypeAnnotationForParam(name, param, types),
);
const returnTypeAnnotation = getReturnTypeAnnotation(
name,
getValueFromTypes(value.returnType, types),
types,
);
return {
name,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation,
params,
optional: property.optional,
},
};
}
// $FlowFixMe there's no flowtype for ASTs
type MethodAST = Object;
// $FlowFixMe there's no flowtype for ASTs
type TypesAST = Object;
function getMethods(
typeDefinition: $ReadOnlyArray<MethodAST>,
types: $ReadOnlyArray<TypesAST>,
): $ReadOnlyArray<MethodTypeShape> {
return typeDefinition
.filter(property => property.type === 'ObjectTypeProperty')
.map(property => buildMethodSchema(property, types))
.filter(Boolean);
}
module.exports = {
getMethods,
};