Files
react-native/packages/react-native-codegen/src/parsers/errors.js
T
Eli White ad380c011f Add test for Object with indexers (#46726)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/46726

An object isn't allowed to have both an indexer and regular properties. Previously, the schema just wouldn't include the properties and would only include the indexer. Instead of failing silently and not generating the expected code, let's explicitly error out.

Changelog: [Internal]

Reviewed By: GijsWeterings

Differential Revision: D63615090

fbshipit-source-id: a4c881b7a809f0b467509f7f7e2131be59ee274e
2024-09-30 11:19:44 -07:00

477 lines
13 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
* @format
*/
'use strict';
import type {UnionTypeAnnotationMemberType} from '../CodegenSchema';
import type {Parser} from './parser';
export type ParserType = 'Flow' | 'TypeScript';
class ParserError extends Error {
nodes: $ReadOnlyArray<$FlowFixMe>;
constructor(
nativeModuleName: string,
astNodeOrNodes: $FlowFixMe,
message: string,
) {
super(`Module ${nativeModuleName}: ${message}`);
this.nodes = Array.isArray(astNodeOrNodes)
? astNodeOrNodes
: [astNodeOrNodes];
// assign the error class name in your custom error (as a shortcut)
this.name = this.constructor.name;
// capturing the stack trace keeps the reference to your error class
Error.captureStackTrace(this, this.constructor);
}
}
class MisnamedModuleInterfaceParserError extends ParserError {
constructor(nativeModuleName: string, id: $FlowFixMe, language: ParserType) {
super(
nativeModuleName,
id,
`All ${language} interfaces extending TurboModule must be called 'Spec'. Please rename ${language} interface '${id.name}' to 'Spec'.`,
);
}
}
class ModuleInterfaceNotFoundParserError extends ParserError {
constructor(nativeModuleName: string, ast: $FlowFixMe, language: ParserType) {
super(
nativeModuleName,
ast,
`No ${language} interfaces extending TurboModule were detected in this NativeModule spec.`,
);
}
}
class MoreThanOneModuleInterfaceParserError extends ParserError {
constructor(
nativeModuleName: string,
flowModuleInterfaces: $ReadOnlyArray<$FlowFixMe>,
names: $ReadOnlyArray<string>,
language: ParserType,
) {
const finalName = names[names.length - 1];
const allButLastName = names.slice(0, -1);
const quote = (x: string) => `'${x}'`;
const nameStr =
allButLastName.map(quote).join(', ') + ', and ' + quote(finalName);
super(
nativeModuleName,
flowModuleInterfaces,
`Every NativeModule spec file must declare exactly one NativeModule ${language} interface. This file declares ${names.length}: ${nameStr}. Please remove the extraneous ${language} interface declarations.`,
);
}
}
class UnsupportedModuleEventEmitterTypePropertyParserError extends ParserError {
constructor(
nativeModuleName: string,
propertyValue: $FlowFixMe,
propertyName: string,
language: ParserType,
nullable: boolean,
) {
super(
nativeModuleName,
propertyValue,
`Property '${propertyName}' is an EventEmitter and must have a non nullable eventType`,
);
}
}
class UnsupportedModuleEventEmitterPropertyParserError extends ParserError {
constructor(
nativeModuleName: string,
propertyValue: $FlowFixMe,
propertyName: string,
language: ParserType,
nullable: boolean,
untyped: boolean,
) {
let message = `${language} interfaces extending TurboModule must only contain 'FunctionTypeAnnotation's or non nullable 'EventEmitter's. Further the EventEmitter property `;
if (nullable) {
message += `'${propertyValue}' must non nullable.`;
} else if (untyped) {
message += `'${propertyValue}' must have a concrete or void eventType.`;
}
super(nativeModuleName, propertyValue, message);
}
}
class UnsupportedModulePropertyParserError extends ParserError {
constructor(
nativeModuleName: string,
propertyValue: $FlowFixMe,
propertyName: string,
invalidPropertyValueType: string,
language: ParserType,
) {
super(
nativeModuleName,
propertyValue,
`${language} interfaces extending TurboModule must only contain 'FunctionTypeAnnotation's or non nullable 'EventEmitter's. Property '${propertyName}' refers to a '${invalidPropertyValueType}'.`,
);
}
}
class UnsupportedTypeAnnotationParserError extends ParserError {
+typeAnnotationType: string;
constructor(
nativeModuleName: string,
typeAnnotation: $FlowFixMe,
language: ParserType,
) {
super(
nativeModuleName,
typeAnnotation,
`${language} type annotation '${typeAnnotation.type}' is unsupported in NativeModule specs.`,
);
this.typeAnnotationType = typeAnnotation.type;
}
}
class UnsupportedGenericParserError extends ParserError {
// +genericName: string;
constructor(
nativeModuleName: string,
genericTypeAnnotation: $FlowFixMe,
parser: Parser,
) {
const genericName = parser.getTypeAnnotationName(genericTypeAnnotation);
super(
nativeModuleName,
genericTypeAnnotation,
`Unrecognized generic type '${genericName}' in NativeModule spec.`,
);
// this.genericName = genericName;
}
}
class MissingTypeParameterGenericParserError extends ParserError {
constructor(
nativeModuleName: string,
genericTypeAnnotation: $FlowFixMe,
parser: Parser,
) {
const genericName = parser.getTypeAnnotationName(genericTypeAnnotation);
super(
nativeModuleName,
genericTypeAnnotation,
`Generic '${genericName}' must have type parameters.`,
);
}
}
class MoreThanOneTypeParameterGenericParserError extends ParserError {
constructor(
nativeModuleName: string,
genericTypeAnnotation: $FlowFixMe,
parser: Parser,
) {
const genericName = parser.getTypeAnnotationName(genericTypeAnnotation);
super(
nativeModuleName,
genericTypeAnnotation,
`Generic '${genericName}' must have exactly one type parameter.`,
);
}
}
/**
* Array parsing errors
*/
class UnsupportedArrayElementTypeAnnotationParserError extends ParserError {
constructor(
nativeModuleName: string,
arrayElementTypeAST: $FlowFixMe,
arrayType: 'Array' | '$ReadOnlyArray' | 'ReadonlyArray',
invalidArrayElementType: string,
) {
super(
nativeModuleName,
arrayElementTypeAST,
`${arrayType} element types cannot be '${invalidArrayElementType}'.`,
);
}
}
/**
* Object parsing errors
*/
class UnsupportedObjectPropertyTypeAnnotationParserError extends ParserError {
constructor(
nativeModuleName: string,
propertyAST: $FlowFixMe,
invalidPropertyType: string,
language: ParserType,
) {
let message = `'ObjectTypeAnnotation' cannot contain '${invalidPropertyType}'.`;
if (
invalidPropertyType === 'ObjectTypeSpreadProperty' &&
language !== 'TypeScript'
) {
message = "Object spread isn't supported in 'ObjectTypeAnnotation's.";
}
super(nativeModuleName, propertyAST, message);
}
}
class UnsupportedObjectPropertyWithIndexerTypeAnnotationParserError extends ParserError {
constructor(nativeModuleName: string, propertyAST: $FlowFixMe) {
let message =
"'ObjectTypeAnnotation' cannot contain both an indexer and properties.";
super(nativeModuleName, propertyAST, message);
}
}
class UnsupportedObjectPropertyValueTypeAnnotationParserError extends ParserError {
constructor(
nativeModuleName: string,
propertyValueAST: $FlowFixMe,
propertyName: string,
invalidPropertyValueType: string,
) {
super(
nativeModuleName,
propertyValueAST,
`Object property '${propertyName}' cannot have type '${invalidPropertyValueType}'.`,
);
}
}
class UnsupportedObjectDirectRecursivePropertyParserError extends ParserError {
constructor(
propertyName: string,
propertyValueAST: $FlowFixMe,
nativeModuleName: string,
) {
super(
nativeModuleName,
propertyValueAST,
`Object property '${propertyName}' is direct recursive and must be nullable.`,
);
}
}
/**
* Function parsing errors
*/
class UnnamedFunctionParamParserError extends ParserError {
constructor(functionParam: $FlowFixMe, nativeModuleName: string) {
super(
nativeModuleName,
functionParam,
'All function parameters must be named.',
);
}
}
class UnsupportedFunctionParamTypeAnnotationParserError extends ParserError {
constructor(
nativeModuleName: string,
flowParamTypeAnnotation: $FlowFixMe,
paramName: string,
invalidParamType: string,
) {
super(
nativeModuleName,
flowParamTypeAnnotation,
`Function parameter '${paramName}' cannot have type '${invalidParamType}'.`,
);
}
}
class UnsupportedFunctionReturnTypeAnnotationParserError extends ParserError {
constructor(
nativeModuleName: string,
flowReturnTypeAnnotation: $FlowFixMe,
invalidReturnType: string,
) {
super(
nativeModuleName,
flowReturnTypeAnnotation,
`Function return cannot have type '${invalidReturnType}'.`,
);
}
}
/**
* Enum parsing errors
*/
class UnsupportedEnumDeclarationParserError extends ParserError {
constructor(
nativeModuleName: string,
arrayElementTypeAST: $FlowFixMe,
memberType: string,
) {
super(
nativeModuleName,
arrayElementTypeAST,
`Unexpected enum member type ${memberType}. Only string and number enum members are supported`,
);
}
}
/**
* Union parsing errors
*/
class UnsupportedUnionTypeAnnotationParserError extends ParserError {
constructor(
nativeModuleName: string,
arrayElementTypeAST: $FlowFixMe,
types: UnionTypeAnnotationMemberType[],
) {
super(
nativeModuleName,
arrayElementTypeAST,
`Union members must be of the same type, but multiple types were found ${types.join(
', ',
)}'.`,
);
}
}
/**
* Module parsing errors
*/
class UnusedModuleInterfaceParserError extends ParserError {
constructor(nativeModuleName: string, flowInterface: $FlowFixMe) {
super(
nativeModuleName,
flowInterface,
"Unused NativeModule spec. Please load the NativeModule by calling TurboModuleRegistry.get<Spec>('<moduleName>').",
);
}
}
class MoreThanOneModuleRegistryCallsParserError extends ParserError {
constructor(
nativeModuleName: string,
flowCallExpressions: $FlowFixMe,
numCalls: number,
) {
super(
nativeModuleName,
flowCallExpressions,
`Every NativeModule spec file must contain exactly one NativeModule load. This file contains ${numCalls}. Please simplify this spec file, splitting it as necessary, to remove the extraneous loads.`,
);
}
}
class UntypedModuleRegistryCallParserError extends ParserError {
constructor(
nativeModuleName: string,
flowCallExpression: $FlowFixMe,
methodName: string,
moduleName: string,
) {
super(
nativeModuleName,
flowCallExpression,
`Please type this NativeModule load: TurboModuleRegistry.${methodName}<Spec>('${moduleName}').`,
);
}
}
class IncorrectModuleRegistryCallTypeParameterParserError extends ParserError {
constructor(
nativeModuleName: string,
flowTypeArguments: $FlowFixMe,
methodName: string,
moduleName: string,
) {
super(
nativeModuleName,
flowTypeArguments,
`Please change these type arguments to reflect TurboModuleRegistry.${methodName}<Spec>('${moduleName}').`,
);
}
}
class IncorrectModuleRegistryCallArityParserError extends ParserError {
constructor(
nativeModuleName: string,
flowCallExpression: $FlowFixMe,
methodName: string,
incorrectArity: number,
) {
super(
nativeModuleName,
flowCallExpression,
`Please call TurboModuleRegistry.${methodName}<Spec>() with exactly one argument. Detected ${incorrectArity}.`,
);
}
}
class IncorrectModuleRegistryCallArgumentTypeParserError extends ParserError {
constructor(
nativeModuleName: string,
flowArgument: $FlowFixMe,
methodName: string,
type: string,
) {
const a = /[aeiouy]/.test(type.toLowerCase()) ? 'an' : 'a';
super(
nativeModuleName,
flowArgument,
`Please call TurboModuleRegistry.${methodName}<Spec>() with a string literal. Detected ${a} '${type}'`,
);
}
}
module.exports = {
ParserError,
MissingTypeParameterGenericParserError,
MoreThanOneTypeParameterGenericParserError,
MisnamedModuleInterfaceParserError,
ModuleInterfaceNotFoundParserError,
MoreThanOneModuleInterfaceParserError,
UnnamedFunctionParamParserError,
UnsupportedArrayElementTypeAnnotationParserError,
UnsupportedGenericParserError,
UnsupportedTypeAnnotationParserError,
UnsupportedFunctionParamTypeAnnotationParserError,
UnsupportedFunctionReturnTypeAnnotationParserError,
UnsupportedEnumDeclarationParserError,
UnsupportedUnionTypeAnnotationParserError,
UnsupportedModuleEventEmitterTypePropertyParserError,
UnsupportedModuleEventEmitterPropertyParserError,
UnsupportedModulePropertyParserError,
UnsupportedObjectPropertyTypeAnnotationParserError,
UnsupportedObjectPropertyWithIndexerTypeAnnotationParserError,
UnsupportedObjectPropertyValueTypeAnnotationParserError,
UnsupportedObjectDirectRecursivePropertyParserError,
UnusedModuleInterfaceParserError,
MoreThanOneModuleRegistryCallsParserError,
UntypedModuleRegistryCallParserError,
IncorrectModuleRegistryCallTypeParameterParserError,
IncorrectModuleRegistryCallArityParserError,
IncorrectModuleRegistryCallArgumentTypeParserError,
};