feat(smart-app): implement complete mobile app MVP

- App.tsx: full navigation (Auth stack + Main tabs with 5 screens)
- Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen
- HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors
- MapScreen: interactive map with layer toggles (6 layers)
- MarketplaceScreen: categories (6), products (5), search
- ChatScreen: AI chat with quick prompts (4), bot responses
- ProfileScreen: user info, stats, menu (9 items), logout
- AlertsScreen: alert list with severity, acknowledge
- SensorsScreen: sensor list with type filters (6 types), search
- ZonesScreen: zone cards with stats
- SettingsScreen: language picker (FR/EN/ES/DE), privacy, about
- Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n
- Hooks: useSensors, useAlerts, useNotifications, useLocation
- Components: Card, Button, LoadingSpinner, ErrorBoundary, Header
- Services: iotService, notificationService (with axios API client)
- Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN)
- Theme: colors.ts with full design system (Blue Ocean palette)
- Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
This commit is contained in:
Eric FELIXINE
2026-06-01 18:00:35 -04:00
parent 08ca495bde
commit e30ae8ed09
35578 changed files with 3703534 additions and 43 deletions

View File

@@ -0,0 +1,84 @@
/**
* 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
* @oncall react_native
*/
'use strict';
function compareSnaps(
flowFixtures,
flowSnaps,
flowExtraCases,
tsFixtures,
tsSnaps,
tsExtraCases,
ignoredCases,
) {
const flowCases = Object.keys(flowFixtures).sort();
const tsCases = Object.keys(tsFixtures).sort();
const commonCases = flowCases.filter(name => tsCases.indexOf(name) !== -1);
describe('RN Codegen Parsers', () => {
it('should not unintentionally contains test case for Flow but not for TypeScript', () => {
expect(
flowCases.filter(name => commonCases.indexOf(name) === -1),
).toEqual(flowExtraCases);
});
it('should not unintentionally contains test case for TypeScript but not for Flow', () => {
expect(tsCases.filter(name => commonCases.indexOf(name) === -1)).toEqual(
tsExtraCases,
);
});
for (const commonCase of commonCases) {
const flowSnap =
flowSnaps[
`RN Codegen Flow Parser can generate fixture ${commonCase} 1`
];
const tsSnap =
tsSnaps[
`RN Codegen TypeScript Parser can generate fixture ${commonCase} 1`
];
it(`should be able to find the snapshot for Flow for case ${commonCase}`, () => {
expect(typeof flowSnap).toEqual('string');
});
it(`should be able to find the snapshot for TypeScript for case ${commonCase}`, () => {
expect(typeof tsSnap).toEqual('string');
});
if (ignoredCases.indexOf(commonCase) === -1) {
it(`should generate the same snapshot from Flow and TypeScript for fixture ${commonCase}`, () => {
expect(flowSnap).toEqual(tsSnap);
});
} else {
it(`should generate the different snapshot from Flow and TypeScript for fixture ${commonCase}`, () => {
expect(flowSnap).not.toEqual(tsSnap);
});
}
}
});
}
function compareTsArraySnaps(tsSnaps, tsExtraCases) {
for (const array2Case of tsExtraCases.filter(
name => name.indexOf('ARRAY2') !== -1,
)) {
const arrayCase = array2Case.replace('ARRAY2', 'ARRAY');
it(`should generate the same snap from fixture ${arrayCase} and ${array2Case}`, () => {
expect(
tsSnaps[
`RN Codegen TypeScript Parser can generate fixture ${arrayCase}`
],
).toEqual(
tsSnaps[
`RN Codegen TypeScript Parser can generate fixture ${array2Case}`
],
);
});
}
}
module.exports = {
compareSnaps,
compareTsArraySnaps,
};

View File

@@ -0,0 +1,345 @@
/**
* 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';
const _require = require('./errors'),
IncorrectModuleRegistryCallArgumentTypeParserError =
_require.IncorrectModuleRegistryCallArgumentTypeParserError,
IncorrectModuleRegistryCallArityParserError =
_require.IncorrectModuleRegistryCallArityParserError,
IncorrectModuleRegistryCallTypeParameterParserError =
_require.IncorrectModuleRegistryCallTypeParameterParserError,
MisnamedModuleInterfaceParserError =
_require.MisnamedModuleInterfaceParserError,
ModuleInterfaceNotFoundParserError =
_require.ModuleInterfaceNotFoundParserError,
MoreThanOneModuleInterfaceParserError =
_require.MoreThanOneModuleInterfaceParserError,
MoreThanOneModuleRegistryCallsParserError =
_require.MoreThanOneModuleRegistryCallsParserError,
UnsupportedArrayElementTypeAnnotationParserError =
_require.UnsupportedArrayElementTypeAnnotationParserError,
UnsupportedFunctionParamTypeAnnotationParserError =
_require.UnsupportedFunctionParamTypeAnnotationParserError,
UnsupportedFunctionReturnTypeAnnotationParserError =
_require.UnsupportedFunctionReturnTypeAnnotationParserError,
UnsupportedModulePropertyParserError =
_require.UnsupportedModulePropertyParserError,
UnsupportedObjectPropertyValueTypeAnnotationParserError =
_require.UnsupportedObjectPropertyValueTypeAnnotationParserError,
UntypedModuleRegistryCallParserError =
_require.UntypedModuleRegistryCallParserError,
UnusedModuleInterfaceParserError = _require.UnusedModuleInterfaceParserError;
function throwIfModuleInterfaceIsMisnamed(
nativeModuleName,
moduleSpecId,
parserType,
) {
if (moduleSpecId.name !== 'Spec') {
throw new MisnamedModuleInterfaceParserError(
nativeModuleName,
moduleSpecId,
parserType,
);
}
}
function throwIfModuleInterfaceNotFound(
numberOfModuleSpecs,
nativeModuleName,
ast,
parserType,
) {
if (numberOfModuleSpecs === 0) {
throw new ModuleInterfaceNotFoundParserError(
nativeModuleName,
ast,
parserType,
);
}
}
function throwIfMoreThanOneModuleRegistryCalls(
hasteModuleName,
callExpressions,
callExpressionsLength,
) {
if (callExpressions.length > 1) {
throw new MoreThanOneModuleRegistryCallsParserError(
hasteModuleName,
callExpressions,
callExpressionsLength,
);
}
}
function throwIfUnusedModuleInterfaceParserError(
nativeModuleName,
moduleSpec,
callExpressions,
) {
if (callExpressions.length === 0) {
throw new UnusedModuleInterfaceParserError(nativeModuleName, moduleSpec);
}
}
function throwIfWrongNumberOfCallExpressionArgs(
nativeModuleName,
flowCallExpression,
methodName,
numberOfCallExpressionArgs,
) {
if (numberOfCallExpressionArgs !== 1) {
throw new IncorrectModuleRegistryCallArityParserError(
nativeModuleName,
flowCallExpression,
methodName,
numberOfCallExpressionArgs,
);
}
}
function throwIfIncorrectModuleRegistryCallTypeParameterParserError(
nativeModuleName,
typeArguments,
methodName,
moduleName,
parser,
) {
function throwError() {
throw new IncorrectModuleRegistryCallTypeParameterParserError(
nativeModuleName,
typeArguments,
methodName,
moduleName,
);
}
if (parser.checkIfInvalidModule(typeArguments)) {
throwError();
}
}
function throwIfUnsupportedFunctionReturnTypeAnnotationParserError(
nativeModuleName,
returnTypeAnnotation,
invalidReturnType,
cxxOnly,
returnType,
) {
if (!cxxOnly && returnType === 'FunctionTypeAnnotation') {
throw new UnsupportedFunctionReturnTypeAnnotationParserError(
nativeModuleName,
returnTypeAnnotation.returnType,
'FunctionTypeAnnotation',
);
}
}
function throwIfUntypedModule(
typeArguments,
hasteModuleName,
callExpression,
methodName,
moduleName,
) {
if (typeArguments == null) {
throw new UntypedModuleRegistryCallParserError(
hasteModuleName,
callExpression,
methodName,
moduleName,
);
}
}
function throwIfModuleTypeIsUnsupported(
nativeModuleName,
propertyValue,
propertyName,
propertyValueType,
parser,
) {
if (!parser.functionTypeAnnotation(propertyValueType)) {
throw new UnsupportedModulePropertyParserError(
nativeModuleName,
propertyValue,
propertyName,
propertyValueType,
parser.language(),
);
}
}
const UnsupportedObjectPropertyTypeToInvalidPropertyValueTypeMap = {
FunctionTypeAnnotation: 'FunctionTypeAnnotation',
VoidTypeAnnotation: 'void',
PromiseTypeAnnotation: 'Promise',
};
function throwIfPropertyValueTypeIsUnsupported(
moduleName,
propertyValue,
propertyKey,
type,
) {
const invalidPropertyValueType =
UnsupportedObjectPropertyTypeToInvalidPropertyValueTypeMap[type];
throw new UnsupportedObjectPropertyValueTypeAnnotationParserError(
moduleName,
propertyValue,
propertyKey,
invalidPropertyValueType,
);
}
function throwIfMoreThanOneModuleInterfaceParserError(
nativeModuleName,
moduleSpecs,
parserType,
) {
if (moduleSpecs.length > 1) {
throw new MoreThanOneModuleInterfaceParserError(
nativeModuleName,
moduleSpecs,
moduleSpecs.map(node => node.id.name),
parserType,
);
}
}
function throwIfUnsupportedFunctionParamTypeAnnotationParserError(
nativeModuleName,
languageParamTypeAnnotation,
paramName,
paramTypeAnnotationType,
) {
throw new UnsupportedFunctionParamTypeAnnotationParserError(
nativeModuleName,
languageParamTypeAnnotation,
paramName,
paramTypeAnnotationType,
);
}
function throwIfArrayElementTypeAnnotationIsUnsupported(
hasteModuleName,
flowElementType,
flowArrayType,
type,
) {
const TypeMap = {
FunctionTypeAnnotation: 'FunctionTypeAnnotation',
VoidTypeAnnotation: 'void',
PromiseTypeAnnotation: 'Promise',
// TODO: Added as a work-around for now until TupleTypeAnnotation are fully supported in both flow and TS
// Right now they are partially treated as UnionTypeAnnotation
UnionTypeAnnotation: 'UnionTypeAnnotation',
};
if (type in TypeMap) {
throw new UnsupportedArrayElementTypeAnnotationParserError(
hasteModuleName,
flowElementType,
flowArrayType,
TypeMap[type],
);
}
}
function throwIfIncorrectModuleRegistryCallArgument(
nativeModuleName,
callExpressionArg,
methodName,
) {
if (
callExpressionArg.type !== 'StringLiteral' &&
callExpressionArg.type !== 'Literal'
) {
const type = callExpressionArg.type;
throw new IncorrectModuleRegistryCallArgumentTypeParserError(
nativeModuleName,
callExpressionArg,
methodName,
type,
);
}
}
function throwIfPartialNotAnnotatingTypeParameter(
typeAnnotation,
types,
parser,
) {
const annotatedElement = parser.extractAnnotatedElement(
typeAnnotation,
types,
);
if (!annotatedElement) {
throw new Error('Partials only support annotating a type parameter.');
}
}
function throwIfPartialWithMoreParameter(typeAnnotation) {
if (typeAnnotation.typeParameters.params.length !== 1) {
throw new Error('Partials only support annotating exactly one parameter.');
}
}
function throwIfMoreThanOneCodegenNativecommands(commandsTypeNames) {
if (commandsTypeNames.length > 1) {
throw new Error('codegenNativeCommands may only be called once in a file');
}
}
function throwIfConfigNotfound(foundConfigs) {
if (foundConfigs.length === 0) {
throw new Error('Could not find component config for native component');
}
}
function throwIfMoreThanOneConfig(foundConfigs) {
if (foundConfigs.length > 1) {
throw new Error('Only one component is supported per file');
}
}
function throwIfEventHasNoName(typeAnnotation, parser) {
const name =
parser.language() === 'Flow' ? typeAnnotation.id : typeAnnotation.typeName;
if (!name) {
throw new Error("typeAnnotation of event doesn't have a name");
}
}
function throwIfBubblingTypeIsNull(bubblingType, eventName) {
if (!bubblingType) {
throw new Error(
`Unable to determine event bubbling type for "${eventName}"`,
);
}
return bubblingType;
}
function throwIfArgumentPropsAreNull(argumentProps, eventName) {
if (!argumentProps) {
throw new Error(`Unable to determine event arguments for "${eventName}"`);
}
return argumentProps;
}
function throwIfTypeAliasIsNotInterface(typeAlias, parser) {
if (typeAlias.type !== parser.interfaceDeclaration) {
throw new Error(
`The type argument for codegenNativeCommands must be an interface, received ${typeAlias.type}`,
);
}
}
module.exports = {
throwIfModuleInterfaceIsMisnamed,
throwIfUnsupportedFunctionReturnTypeAnnotationParserError,
throwIfModuleInterfaceNotFound,
throwIfMoreThanOneModuleRegistryCalls,
throwIfPropertyValueTypeIsUnsupported,
throwIfUnusedModuleInterfaceParserError,
throwIfWrongNumberOfCallExpressionArgs,
throwIfIncorrectModuleRegistryCallTypeParameterParserError,
throwIfUntypedModule,
throwIfModuleTypeIsUnsupported,
throwIfMoreThanOneModuleInterfaceParserError,
throwIfUnsupportedFunctionParamTypeAnnotationParserError,
throwIfArrayElementTypeAnnotationIsUnsupported,
throwIfIncorrectModuleRegistryCallArgument,
throwIfPartialNotAnnotatingTypeParameter,
throwIfPartialWithMoreParameter,
throwIfMoreThanOneCodegenNativecommands,
throwIfConfigNotfound,
throwIfMoreThanOneConfig,
throwIfEventHasNoName,
throwIfBubblingTypeIsNull,
throwIfArgumentPropsAreNull,
throwIfTypeAliasIsNotInterface,
};

View File

@@ -0,0 +1,378 @@
/**
* 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 {NativeModuleTypeAnnotation} from '../CodegenSchema';
import type {TypeDeclarationMap} from '../parsers/utils';
import type {ParserType} from './errors';
import type {Parser} from './parser';
const {
IncorrectModuleRegistryCallArgumentTypeParserError,
IncorrectModuleRegistryCallArityParserError,
IncorrectModuleRegistryCallTypeParameterParserError,
MisnamedModuleInterfaceParserError,
ModuleInterfaceNotFoundParserError,
MoreThanOneModuleInterfaceParserError,
MoreThanOneModuleRegistryCallsParserError,
UnsupportedArrayElementTypeAnnotationParserError,
UnsupportedFunctionParamTypeAnnotationParserError,
UnsupportedFunctionReturnTypeAnnotationParserError,
UnsupportedModulePropertyParserError,
UnsupportedObjectPropertyValueTypeAnnotationParserError,
UntypedModuleRegistryCallParserError,
UnusedModuleInterfaceParserError,
} = require('./errors');
function throwIfModuleInterfaceIsMisnamed(
nativeModuleName: string,
moduleSpecId: $FlowFixMe,
parserType: ParserType,
) {
if (moduleSpecId.name !== 'Spec') {
throw new MisnamedModuleInterfaceParserError(
nativeModuleName,
moduleSpecId,
parserType,
);
}
}
function throwIfModuleInterfaceNotFound(
numberOfModuleSpecs: number,
nativeModuleName: string,
ast: $FlowFixMe,
parserType: ParserType,
) {
if (numberOfModuleSpecs === 0) {
throw new ModuleInterfaceNotFoundParserError(
nativeModuleName,
ast,
parserType,
);
}
}
function throwIfMoreThanOneModuleRegistryCalls(
hasteModuleName: string,
callExpressions: $FlowFixMe,
callExpressionsLength: number,
) {
if (callExpressions.length > 1) {
throw new MoreThanOneModuleRegistryCallsParserError(
hasteModuleName,
callExpressions,
callExpressionsLength,
);
}
}
function throwIfUnusedModuleInterfaceParserError(
nativeModuleName: string,
moduleSpec: $FlowFixMe,
callExpressions: $FlowFixMe,
) {
if (callExpressions.length === 0) {
throw new UnusedModuleInterfaceParserError(nativeModuleName, moduleSpec);
}
}
function throwIfWrongNumberOfCallExpressionArgs(
nativeModuleName: string,
flowCallExpression: $FlowFixMe,
methodName: string,
numberOfCallExpressionArgs: number,
) {
if (numberOfCallExpressionArgs !== 1) {
throw new IncorrectModuleRegistryCallArityParserError(
nativeModuleName,
flowCallExpression,
methodName,
numberOfCallExpressionArgs,
);
}
}
function throwIfIncorrectModuleRegistryCallTypeParameterParserError(
nativeModuleName: string,
typeArguments: $FlowFixMe,
methodName: string,
moduleName: string,
parser: Parser,
) {
function throwError() {
throw new IncorrectModuleRegistryCallTypeParameterParserError(
nativeModuleName,
typeArguments,
methodName,
moduleName,
);
}
if (parser.checkIfInvalidModule(typeArguments)) {
throwError();
}
}
function throwIfUnsupportedFunctionReturnTypeAnnotationParserError(
nativeModuleName: string,
returnTypeAnnotation: $FlowFixMe,
invalidReturnType: string,
cxxOnly: boolean,
returnType: string,
) {
if (!cxxOnly && returnType === 'FunctionTypeAnnotation') {
throw new UnsupportedFunctionReturnTypeAnnotationParserError(
nativeModuleName,
returnTypeAnnotation.returnType,
'FunctionTypeAnnotation',
);
}
}
function throwIfUntypedModule(
typeArguments: $FlowFixMe,
hasteModuleName: string,
callExpression: $FlowFixMe,
methodName: string,
moduleName: string,
) {
if (typeArguments == null) {
throw new UntypedModuleRegistryCallParserError(
hasteModuleName,
callExpression,
methodName,
moduleName,
);
}
}
function throwIfModuleTypeIsUnsupported(
nativeModuleName: string,
propertyValue: $FlowFixMe,
propertyName: string,
propertyValueType: string,
parser: Parser,
) {
if (!parser.functionTypeAnnotation(propertyValueType)) {
throw new UnsupportedModulePropertyParserError(
nativeModuleName,
propertyValue,
propertyName,
propertyValueType,
parser.language(),
);
}
}
const UnsupportedObjectPropertyTypeToInvalidPropertyValueTypeMap = {
FunctionTypeAnnotation: 'FunctionTypeAnnotation',
VoidTypeAnnotation: 'void',
PromiseTypeAnnotation: 'Promise',
};
function throwIfPropertyValueTypeIsUnsupported(
moduleName: string,
propertyValue: $FlowFixMe,
propertyKey: string,
type: string,
) {
const invalidPropertyValueType =
UnsupportedObjectPropertyTypeToInvalidPropertyValueTypeMap[type];
throw new UnsupportedObjectPropertyValueTypeAnnotationParserError(
moduleName,
propertyValue,
propertyKey,
invalidPropertyValueType,
);
}
function throwIfMoreThanOneModuleInterfaceParserError(
nativeModuleName: string,
moduleSpecs: $ReadOnlyArray<$FlowFixMe>,
parserType: ParserType,
) {
if (moduleSpecs.length > 1) {
throw new MoreThanOneModuleInterfaceParserError(
nativeModuleName,
moduleSpecs,
moduleSpecs.map(node => node.id.name),
parserType,
);
}
}
function throwIfUnsupportedFunctionParamTypeAnnotationParserError(
nativeModuleName: string,
languageParamTypeAnnotation: $FlowFixMe,
paramName: string,
paramTypeAnnotationType: NativeModuleTypeAnnotation['type'],
) {
throw new UnsupportedFunctionParamTypeAnnotationParserError(
nativeModuleName,
languageParamTypeAnnotation,
paramName,
paramTypeAnnotationType,
);
}
function throwIfArrayElementTypeAnnotationIsUnsupported(
hasteModuleName: string,
flowElementType: $FlowFixMe,
flowArrayType: 'Array' | '$ReadOnlyArray' | 'ReadonlyArray',
type: string,
) {
const TypeMap = {
FunctionTypeAnnotation: 'FunctionTypeAnnotation',
VoidTypeAnnotation: 'void',
PromiseTypeAnnotation: 'Promise',
// TODO: Added as a work-around for now until TupleTypeAnnotation are fully supported in both flow and TS
// Right now they are partially treated as UnionTypeAnnotation
UnionTypeAnnotation: 'UnionTypeAnnotation',
};
if (type in TypeMap) {
throw new UnsupportedArrayElementTypeAnnotationParserError(
hasteModuleName,
flowElementType,
flowArrayType,
TypeMap[type],
);
}
}
function throwIfIncorrectModuleRegistryCallArgument(
nativeModuleName: string,
callExpressionArg: $FlowFixMe,
methodName: string,
) {
if (
callExpressionArg.type !== 'StringLiteral' &&
callExpressionArg.type !== 'Literal'
) {
const {type} = callExpressionArg;
throw new IncorrectModuleRegistryCallArgumentTypeParserError(
nativeModuleName,
callExpressionArg,
methodName,
type,
);
}
}
function throwIfPartialNotAnnotatingTypeParameter(
typeAnnotation: $FlowFixMe,
types: TypeDeclarationMap,
parser: Parser,
) {
const annotatedElement = parser.extractAnnotatedElement(
typeAnnotation,
types,
);
if (!annotatedElement) {
throw new Error('Partials only support annotating a type parameter.');
}
}
function throwIfPartialWithMoreParameter(typeAnnotation: $FlowFixMe) {
if (typeAnnotation.typeParameters.params.length !== 1) {
throw new Error('Partials only support annotating exactly one parameter.');
}
}
function throwIfMoreThanOneCodegenNativecommands(
commandsTypeNames: $ReadOnlyArray<$FlowFixMe>,
) {
if (commandsTypeNames.length > 1) {
throw new Error('codegenNativeCommands may only be called once in a file');
}
}
function throwIfConfigNotfound(foundConfigs: Array<{[string]: string}>) {
if (foundConfigs.length === 0) {
throw new Error('Could not find component config for native component');
}
}
function throwIfMoreThanOneConfig(foundConfigs: Array<{[string]: string}>) {
if (foundConfigs.length > 1) {
throw new Error('Only one component is supported per file');
}
}
function throwIfEventHasNoName(typeAnnotation: $FlowFixMe, parser: Parser) {
const name =
parser.language() === 'Flow' ? typeAnnotation.id : typeAnnotation.typeName;
if (!name) {
throw new Error("typeAnnotation of event doesn't have a name");
}
}
function throwIfBubblingTypeIsNull(
bubblingType: ?('direct' | 'bubble'),
eventName: string,
): 'direct' | 'bubble' {
if (!bubblingType) {
throw new Error(
`Unable to determine event bubbling type for "${eventName}"`,
);
}
return bubblingType;
}
function throwIfArgumentPropsAreNull(
argumentProps: ?$ReadOnlyArray<$FlowFixMe>,
eventName: string,
): $ReadOnlyArray<$FlowFixMe> {
if (!argumentProps) {
throw new Error(`Unable to determine event arguments for "${eventName}"`);
}
return argumentProps;
}
function throwIfTypeAliasIsNotInterface(typeAlias: $FlowFixMe, parser: Parser) {
if (typeAlias.type !== parser.interfaceDeclaration) {
throw new Error(
`The type argument for codegenNativeCommands must be an interface, received ${typeAlias.type}`,
);
}
}
module.exports = {
throwIfModuleInterfaceIsMisnamed,
throwIfUnsupportedFunctionReturnTypeAnnotationParserError,
throwIfModuleInterfaceNotFound,
throwIfMoreThanOneModuleRegistryCalls,
throwIfPropertyValueTypeIsUnsupported,
throwIfUnusedModuleInterfaceParserError,
throwIfWrongNumberOfCallExpressionArgs,
throwIfIncorrectModuleRegistryCallTypeParameterParserError,
throwIfUntypedModule,
throwIfModuleTypeIsUnsupported,
throwIfMoreThanOneModuleInterfaceParserError,
throwIfUnsupportedFunctionParamTypeAnnotationParserError,
throwIfArrayElementTypeAnnotationIsUnsupported,
throwIfIncorrectModuleRegistryCallArgument,
throwIfPartialNotAnnotatingTypeParameter,
throwIfPartialWithMoreParameter,
throwIfMoreThanOneCodegenNativecommands,
throwIfConfigNotfound,
throwIfMoreThanOneConfig,
throwIfEventHasNoName,
throwIfBubblingTypeIsNull,
throwIfArgumentPropsAreNull,
throwIfTypeAliasIsNotInterface,
};

View File

@@ -0,0 +1,10 @@
/**
* 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.
*/
export type ParserType = 'Flow' | 'TypeScript';
export declare class ParserError extends Error {}

View File

@@ -0,0 +1,334 @@
/**
* 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';
class ParserError extends Error {
constructor(nativeModuleName, astNodeOrNodes, message) {
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, id, language) {
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, ast, language) {
super(
nativeModuleName,
ast,
`No ${language} interfaces extending TurboModule were detected in this NativeModule spec.`,
);
}
}
class MoreThanOneModuleInterfaceParserError extends ParserError {
constructor(nativeModuleName, flowModuleInterfaces, names, language) {
const finalName = names[names.length - 1];
const allButLastName = names.slice(0, -1);
const quote = x => `'${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 UnsupportedModulePropertyParserError extends ParserError {
constructor(
nativeModuleName,
propertyValue,
propertyName,
invalidPropertyValueType,
language,
) {
super(
nativeModuleName,
propertyValue,
`${language} interfaces extending TurboModule must only contain 'FunctionTypeAnnotation's. Property '${propertyName}' refers to a '${invalidPropertyValueType}'.`,
);
}
}
class UnsupportedTypeAnnotationParserError extends ParserError {
constructor(nativeModuleName, typeAnnotation, language) {
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, genericTypeAnnotation, 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, genericTypeAnnotation, parser) {
const genericName = parser.getTypeAnnotationName(genericTypeAnnotation);
super(
nativeModuleName,
genericTypeAnnotation,
`Generic '${genericName}' must have type parameters.`,
);
}
}
class MoreThanOneTypeParameterGenericParserError extends ParserError {
constructor(nativeModuleName, genericTypeAnnotation, 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,
arrayElementTypeAST,
arrayType,
invalidArrayElementType,
) {
super(
nativeModuleName,
arrayElementTypeAST,
`${arrayType} element types cannot be '${invalidArrayElementType}'.`,
);
}
}
/**
* Object parsing errors
*/
class UnsupportedObjectPropertyTypeAnnotationParserError extends ParserError {
constructor(nativeModuleName, propertyAST, invalidPropertyType, language) {
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 UnsupportedObjectPropertyValueTypeAnnotationParserError extends ParserError {
constructor(
nativeModuleName,
propertyValueAST,
propertyName,
invalidPropertyValueType,
) {
super(
nativeModuleName,
propertyValueAST,
`Object property '${propertyName}' cannot have type '${invalidPropertyValueType}'.`,
);
}
}
class UnsupportedObjectDirectRecursivePropertyParserError extends ParserError {
constructor(propertyName, propertyValueAST, nativeModuleName) {
super(
nativeModuleName,
propertyValueAST,
`Object property '${propertyName}' is direct recursive and must be nullable.`,
);
}
}
/**
* Function parsing errors
*/
class UnnamedFunctionParamParserError extends ParserError {
constructor(functionParam, nativeModuleName) {
super(
nativeModuleName,
functionParam,
'All function parameters must be named.',
);
}
}
class UnsupportedFunctionParamTypeAnnotationParserError extends ParserError {
constructor(
nativeModuleName,
flowParamTypeAnnotation,
paramName,
invalidParamType,
) {
super(
nativeModuleName,
flowParamTypeAnnotation,
`Function parameter '${paramName}' cannot have type '${invalidParamType}'.`,
);
}
}
class UnsupportedFunctionReturnTypeAnnotationParserError extends ParserError {
constructor(nativeModuleName, flowReturnTypeAnnotation, invalidReturnType) {
super(
nativeModuleName,
flowReturnTypeAnnotation,
`Function return cannot have type '${invalidReturnType}'.`,
);
}
}
/**
* Enum parsing errors
*/
class UnsupportedEnumDeclarationParserError extends ParserError {
constructor(nativeModuleName, arrayElementTypeAST, memberType) {
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, arrayElementTypeAST, types) {
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, flowInterface) {
super(
nativeModuleName,
flowInterface,
"Unused NativeModule spec. Please load the NativeModule by calling TurboModuleRegistry.get<Spec>('<moduleName>').",
);
}
}
class MoreThanOneModuleRegistryCallsParserError extends ParserError {
constructor(nativeModuleName, flowCallExpressions, numCalls) {
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, flowCallExpression, methodName, moduleName) {
super(
nativeModuleName,
flowCallExpression,
`Please type this NativeModule load: TurboModuleRegistry.${methodName}<Spec>('${moduleName}').`,
);
}
}
class IncorrectModuleRegistryCallTypeParameterParserError extends ParserError {
constructor(nativeModuleName, flowTypeArguments, methodName, moduleName) {
super(
nativeModuleName,
flowTypeArguments,
`Please change these type arguments to reflect TurboModuleRegistry.${methodName}<Spec>('${moduleName}').`,
);
}
}
class IncorrectModuleRegistryCallArityParserError extends ParserError {
constructor(
nativeModuleName,
flowCallExpression,
methodName,
incorrectArity,
) {
super(
nativeModuleName,
flowCallExpression,
`Please call TurboModuleRegistry.${methodName}<Spec>() with exactly one argument. Detected ${incorrectArity}.`,
);
}
}
class IncorrectModuleRegistryCallArgumentTypeParserError extends ParserError {
constructor(nativeModuleName, flowArgument, methodName, type) {
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,
UnsupportedModulePropertyParserError,
UnsupportedObjectPropertyTypeAnnotationParserError,
UnsupportedObjectPropertyValueTypeAnnotationParserError,
UnsupportedObjectDirectRecursivePropertyParserError,
UnusedModuleInterfaceParserError,
MoreThanOneModuleRegistryCallsParserError,
UntypedModuleRegistryCallParserError,
IncorrectModuleRegistryCallTypeParameterParserError,
IncorrectModuleRegistryCallArityParserError,
IncorrectModuleRegistryCallArgumentTypeParserError,
};

View File

@@ -0,0 +1,429 @@
/**
* 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 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. 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 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,
UnsupportedModulePropertyParserError,
UnsupportedObjectPropertyTypeAnnotationParserError,
UnsupportedObjectPropertyValueTypeAnnotationParserError,
UnsupportedObjectDirectRecursivePropertyParserError,
UnusedModuleInterfaceParserError,
MoreThanOneModuleRegistryCallsParserError,
UntypedModuleRegistryCallParserError,
IncorrectModuleRegistryCallTypeParameterParserError,
IncorrectModuleRegistryCallArityParserError,
IncorrectModuleRegistryCallArgumentTypeParserError,
};

View File

@@ -0,0 +1,583 @@
/**
* 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
*/
// @licenselint-loose-mode
'use strict';
const COMMANDS_DEFINED_INLINE = `
/**
* 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
* @flow strict-local
*/
'use strict';
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props
|}>;
export const Commands = codegenNativeCommands<{
+hotspotUpdate: (ref: React.Ref<'RCTView'>, x: Int32, y: Int32) => void,
}>({
supportedCommands: ['hotspotUpdate'],
});
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const COMMANDS_DEFINED_MULTIPLE_TIMES = `
/**
* 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
* @flow strict-local
*/
'use strict';
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
+hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void;
}
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate'],
});
export const Commands2 = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate'],
});
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const COMMANDS_DEFINED_WITHOUT_REF = `
/**
* 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
* @flow strict-local
*/
'use strict';
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
+hotspotUpdate: (x: Int32, y: Int32) => void;
}
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate'],
});
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const COMMANDS_DEFINED_WITH_NULLABLE_REF = `
/**
* 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
* @flow strict-local
*/
'use strict';
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
+hotspotUpdate: (viewRef: ?React.Ref<'RCTView'>, x: Int32, y: Int32) => void;
}
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate'],
});
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const COMMANDS_DEFINED_WITH_MISMATCHED_METHOD_NAMES = `
/**
* 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
* @flow strict-local
*/
'use strict';
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
+hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void;
+scrollTo: (
viewRef: React.Ref<'RCTView'>,
y: Int32,
animated: boolean,
) => void;
}
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['scrollTo'],
});
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const COMMANDS_DEFINED_WITHOUT_METHOD_NAMES = `
/**
* 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
* @flow strict-local
*/
'use strict';
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
+hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void;
+scrollTo: (
viewRef: React.Ref<'RCTView'>,
y: Int32,
animated: boolean,
) => void;
}
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>();
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const NULLABLE_WITH_DEFAULT = `
/**
* 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
* @flow strict-local
*/
'use strict';
const codegenNativeComponent = require('codegenNativeComponent');
import type {WithDefault, Float} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
export type ModuleProps = $ReadOnly<{|
...ViewProps,
nullable_with_default: ?WithDefault<Float, 1.0>,
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const NON_OPTIONAL_KEY_WITH_DEFAULT_VALUE = `
/**
* 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
* @flow strict-local
*/
'use strict';
const codegenNativeComponent = require('codegenNativeComponent');
import type {WithDefault, Float} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
export type ModuleProps = $ReadOnly<{|
...ViewProps,
required_key_with_default: WithDefault<Float, 1.0>,
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROPS_CONFLICT_NAMES = `
/**
* 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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
isEnabled: string,
isEnabled: boolean,
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROPS_CONFLICT_WITH_SPREAD_PROPS = `
/**
* 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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
type PropsInFile = $ReadOnly<{|
isEnabled: boolean,
|}>;
export type ModuleProps = $ReadOnly<{|
...ViewProps,
...PropsInFile,
isEnabled: boolean,
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROPS_SPREAD_CONFLICTS_WITH_PROPS = `
/**
* 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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
type PropsInFile = $ReadOnly<{|
isEnabled: boolean,
|}>;
export type ModuleProps = $ReadOnly<{|
...ViewProps,
isEnabled: boolean,
...PropsInFile,
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROP_NUMBER_TYPE = `
/**
* 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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
someProp: number
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROP_MIXED_ENUM = `
/**
* 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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
someProp?: WithDefault<'foo' | 1, 1>
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROP_ENUM_BOOLEAN = `
/**
* 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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
someProp?: WithDefault<false | true, false>
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROP_ARRAY_MIXED_ENUM = `
/**
* 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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
someProp?: WithDefault<$ReadOnlyArray<'foo' | 1>, 1>
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROP_ARRAY_ENUM_BOOLEAN = `
/**
* 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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
someProp?: WithDefault<$ReadOnlyArray<false | true>, false>
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROP_ARRAY_ENUM_INT = `
/**
* 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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
someProp?: WithDefault<$ReadOnlyArray<0 | 1>, 0>
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
module.exports = {
COMMANDS_DEFINED_INLINE,
COMMANDS_DEFINED_MULTIPLE_TIMES,
COMMANDS_DEFINED_WITH_MISMATCHED_METHOD_NAMES,
COMMANDS_DEFINED_WITHOUT_METHOD_NAMES,
COMMANDS_DEFINED_WITHOUT_REF,
COMMANDS_DEFINED_WITH_NULLABLE_REF,
NULLABLE_WITH_DEFAULT,
NON_OPTIONAL_KEY_WITH_DEFAULT_VALUE,
PROPS_CONFLICT_NAMES,
PROPS_CONFLICT_WITH_SPREAD_PROPS,
PROPS_SPREAD_CONFLICTS_WITH_PROPS,
PROP_NUMBER_TYPE,
PROP_MIXED_ENUM,
PROP_ENUM_BOOLEAN,
PROP_ARRAY_MIXED_ENUM,
PROP_ARRAY_ENUM_BOOLEAN,
PROP_ARRAY_ENUM_INT,
};

View File

@@ -0,0 +1,600 @@
/**
* 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
*/
// @licenselint-loose-mode
'use strict';
const COMMANDS_DEFINED_INLINE = `
/**
* 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
* @flow strict-local
*/
'use strict';
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props
|}>;
export const Commands = codegenNativeCommands<{
+hotspotUpdate: (ref: React.Ref<'RCTView'>, x: Int32, y: Int32) => void,
}>({
supportedCommands: ['hotspotUpdate'],
});
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const COMMANDS_DEFINED_MULTIPLE_TIMES = `
/**
* 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
* @flow strict-local
*/
'use strict';
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
+hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void;
}
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate'],
});
export const Commands2 = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate'],
});
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const COMMANDS_DEFINED_WITHOUT_REF = `
/**
* 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
* @flow strict-local
*/
'use strict';
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
+hotspotUpdate: (x: Int32, y: Int32) => void;
}
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate'],
});
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const COMMANDS_DEFINED_WITH_NULLABLE_REF = `
/**
* 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
* @flow strict-local
*/
'use strict';
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
+hotspotUpdate: (viewRef: ?React.Ref<'RCTView'>, x: Int32, y: Int32) => void;
}
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate'],
});
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const COMMANDS_DEFINED_WITH_MISMATCHED_METHOD_NAMES = `
/**
* 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
* @flow strict-local
*/
'use strict';
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
+hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void;
+scrollTo: (
viewRef: React.Ref<'RCTView'>,
y: Int32,
animated: boolean,
) => void;
}
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['scrollTo'],
});
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const COMMANDS_DEFINED_WITHOUT_METHOD_NAMES = `
/**
* 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
* @flow strict-local
*/
'use strict';
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
+hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void;
+scrollTo: (
viewRef: React.Ref<'RCTView'>,
y: Int32,
animated: boolean,
) => void;
}
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>();
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const NULLABLE_WITH_DEFAULT = `
/**
* 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
* @flow strict-local
*/
'use strict';
const codegenNativeComponent = require('codegenNativeComponent');
import type {WithDefault, Float} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
export type ModuleProps = $ReadOnly<{|
...ViewProps,
nullable_with_default: ?WithDefault<Float, 1.0>,
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const NON_OPTIONAL_KEY_WITH_DEFAULT_VALUE = `
/**
* 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
* @flow strict-local
*/
'use strict';
const codegenNativeComponent = require('codegenNativeComponent');
import type {WithDefault, Float} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
export type ModuleProps = $ReadOnly<{|
...ViewProps,
required_key_with_default: WithDefault<Float, 1.0>,
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROPS_CONFLICT_NAMES = `
/**
* 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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
isEnabled: string,
isEnabled: boolean,
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROPS_CONFLICT_WITH_SPREAD_PROPS = `
/**
* 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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
type PropsInFile = $ReadOnly<{|
isEnabled: boolean,
|}>;
export type ModuleProps = $ReadOnly<{|
...ViewProps,
...PropsInFile,
isEnabled: boolean,
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROPS_SPREAD_CONFLICTS_WITH_PROPS = `
/**
* 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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
type PropsInFile = $ReadOnly<{|
isEnabled: boolean,
|}>;
export type ModuleProps = $ReadOnly<{|
...ViewProps,
isEnabled: boolean,
...PropsInFile,
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROP_NUMBER_TYPE = `
/**
* 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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
someProp: number
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROP_MIXED_ENUM = `
/**
* 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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
someProp?: WithDefault<'foo' | 1, 1>
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROP_ENUM_BOOLEAN = `
/**
* 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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
someProp?: WithDefault<false | true, false>
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROP_ARRAY_MIXED_ENUM = `
/**
* 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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
someProp?: WithDefault<$ReadOnlyArray<'foo' | 1>, 1>
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROP_ARRAY_ENUM_BOOLEAN = `
/**
* 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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
someProp?: WithDefault<$ReadOnlyArray<false | true>, false>
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROP_ARRAY_ENUM_INT = `
/**
* 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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
someProp?: WithDefault<$ReadOnlyArray<0 | 1>, 0>
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
module.exports = {
COMMANDS_DEFINED_INLINE,
COMMANDS_DEFINED_MULTIPLE_TIMES,
COMMANDS_DEFINED_WITH_MISMATCHED_METHOD_NAMES,
COMMANDS_DEFINED_WITHOUT_METHOD_NAMES,
COMMANDS_DEFINED_WITHOUT_REF,
COMMANDS_DEFINED_WITH_NULLABLE_REF,
NULLABLE_WITH_DEFAULT,
NON_OPTIONAL_KEY_WITH_DEFAULT_VALUE,
PROPS_CONFLICT_NAMES,
PROPS_CONFLICT_WITH_SPREAD_PROPS,
PROPS_SPREAD_CONFLICTS_WITH_PROPS,
PROP_NUMBER_TYPE,
PROP_MIXED_ENUM,
PROP_ENUM_BOOLEAN,
PROP_ARRAY_MIXED_ENUM,
PROP_ARRAY_ENUM_BOOLEAN,
PROP_ARRAY_ENUM_INT,
};

View File

@@ -0,0 +1,131 @@
/**
* 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';
const _require = require('../utils.js'),
getValueFromTypes = _require.getValueFromTypes;
// $FlowFixMe[unclear-type] there's no flowtype for ASTs
function buildCommandSchema(property, types) {
const name = property.key.name;
const optional = property.optional;
const value = getValueFromTypes(property.value, types);
const firstParam = value.params[0].typeAnnotation;
if (
!(
firstParam.id != null &&
firstParam.id.type === 'QualifiedTypeIdentifier' &&
firstParam.id.qualification.name === 'React' &&
firstParam.id.id.name === 'ElementRef'
)
) {
throw new Error(
`The first argument of method ${name} must be of type React.ElementRef<>`,
);
}
const params = value.params.slice(1).map(param => {
const paramName = param.name.name;
const paramValue = getValueFromTypes(param.typeAnnotation, types);
const type =
paramValue.type === 'GenericTypeAnnotation'
? paramValue.id.name
: paramValue.type;
let returnType;
switch (type) {
case 'RootTag':
returnType = {
type: 'ReservedTypeAnnotation',
name: 'RootTag',
};
break;
case 'BooleanTypeAnnotation':
returnType = {
type: 'BooleanTypeAnnotation',
};
break;
case 'Int32':
returnType = {
type: 'Int32TypeAnnotation',
};
break;
case 'Double':
returnType = {
type: 'DoubleTypeAnnotation',
};
break;
case 'Float':
returnType = {
type: 'FloatTypeAnnotation',
};
break;
case 'StringTypeAnnotation':
returnType = {
type: 'StringTypeAnnotation',
};
break;
case 'Array':
case '$ReadOnlyArray':
if (!paramValue.type === 'GenericTypeAnnotation') {
throw new Error(
'Array and $ReadOnlyArray are GenericTypeAnnotation for array',
);
}
returnType = {
type: 'ArrayTypeAnnotation',
elementType: {
// TODO: T172453752 support complex type annotation for array element
type: paramValue.typeParameters.params[0].type,
},
};
break;
case 'ArrayTypeAnnotation':
returnType = {
type: 'ArrayTypeAnnotation',
elementType: {
// TODO: T172453752 support complex type annotation for array element
type: paramValue.elementType.type,
},
};
break;
default:
type;
throw new Error(
`Unsupported param type for method "${name}", param "${paramName}". Found ${type}`,
);
}
return {
name: paramName,
optional: false,
typeAnnotation: returnType,
};
});
return {
name,
optional,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
params,
returnTypeAnnotation: {
type: 'VoidTypeAnnotation',
},
},
};
}
function getCommands(commandTypeAST, types) {
return commandTypeAST
.filter(property => property.type === 'ObjectTypeProperty')
.map(property => buildCommandSchema(property, types))
.filter(Boolean);
}
module.exports = {
getCommands,
};

View File

@@ -0,0 +1,166 @@
/**
* 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 {
CommandParamTypeAnnotation,
CommandTypeAnnotation,
NamedShape,
} from '../../../CodegenSchema.js';
import type {TypeDeclarationMap} from '../../utils';
const {getValueFromTypes} = require('../utils.js');
// $FlowFixMe[unclear-type] there's no flowtype for ASTs
type EventTypeAST = Object;
function buildCommandSchema(
property: EventTypeAST,
types: TypeDeclarationMap,
): $ReadOnly<{
name: string,
optional: boolean,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
params: $ReadOnlyArray<{
name: string,
optional: boolean,
typeAnnotation: CommandParamTypeAnnotation,
}>,
returnTypeAnnotation: {
type: 'VoidTypeAnnotation',
},
},
}> {
const name = property.key.name;
const optional = property.optional;
const value = getValueFromTypes(property.value, types);
const firstParam = value.params[0].typeAnnotation;
if (
!(
firstParam.id != null &&
firstParam.id.type === 'QualifiedTypeIdentifier' &&
firstParam.id.qualification.name === 'React' &&
firstParam.id.id.name === 'ElementRef'
)
) {
throw new Error(
`The first argument of method ${name} must be of type React.ElementRef<>`,
);
}
const params = value.params.slice(1).map(param => {
const paramName = param.name.name;
const paramValue = getValueFromTypes(param.typeAnnotation, types);
const type =
paramValue.type === 'GenericTypeAnnotation'
? paramValue.id.name
: paramValue.type;
let returnType: CommandParamTypeAnnotation;
switch (type) {
case 'RootTag':
returnType = {
type: 'ReservedTypeAnnotation',
name: 'RootTag',
};
break;
case 'BooleanTypeAnnotation':
returnType = {
type: 'BooleanTypeAnnotation',
};
break;
case 'Int32':
returnType = {
type: 'Int32TypeAnnotation',
};
break;
case 'Double':
returnType = {
type: 'DoubleTypeAnnotation',
};
break;
case 'Float':
returnType = {
type: 'FloatTypeAnnotation',
};
break;
case 'StringTypeAnnotation':
returnType = {
type: 'StringTypeAnnotation',
};
break;
case 'Array':
case '$ReadOnlyArray':
if (!paramValue.type === 'GenericTypeAnnotation') {
throw new Error(
'Array and $ReadOnlyArray are GenericTypeAnnotation for array',
);
}
returnType = {
type: 'ArrayTypeAnnotation',
elementType: {
// TODO: T172453752 support complex type annotation for array element
type: paramValue.typeParameters.params[0].type,
},
};
break;
case 'ArrayTypeAnnotation':
returnType = {
type: 'ArrayTypeAnnotation',
elementType: {
// TODO: T172453752 support complex type annotation for array element
type: paramValue.elementType.type,
},
};
break;
default:
(type: empty);
throw new Error(
`Unsupported param type for method "${name}", param "${paramName}". Found ${type}`,
);
}
return {
name: paramName,
optional: false,
typeAnnotation: returnType,
};
});
return {
name,
optional,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
params,
returnTypeAnnotation: {
type: 'VoidTypeAnnotation',
},
},
};
}
function getCommands(
commandTypeAST: $ReadOnlyArray<EventTypeAST>,
types: TypeDeclarationMap,
): $ReadOnlyArray<NamedShape<CommandTypeAnnotation>> {
return commandTypeAST
.filter(property => property.type === 'ObjectTypeProperty')
.map(property => buildCommandSchema(property, types))
.filter(Boolean);
}
module.exports = {
getCommands,
};

View File

@@ -0,0 +1,450 @@
/**
* 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';
const _require = require('../../parsers-commons'),
verifyPropNotAlreadyDefined = _require.verifyPropNotAlreadyDefined;
const _require2 = require('../utils.js'),
getValueFromTypes = _require2.getValueFromTypes;
// $FlowFixMe[unsupported-variance-annotation]
function getTypeAnnotationForArray(
name,
typeAnnotation,
defaultValue,
types,
parser,
buildSchema,
) {
const extractedTypeAnnotation = getValueFromTypes(typeAnnotation, types);
if (extractedTypeAnnotation.type === 'NullableTypeAnnotation') {
throw new Error(
'Nested optionals such as "$ReadOnlyArray<?boolean>" are not supported, please declare optionals at the top level of value definitions as in "?$ReadOnlyArray<boolean>"',
);
}
if (
extractedTypeAnnotation.type === 'GenericTypeAnnotation' &&
parser.getTypeAnnotationName(extractedTypeAnnotation) === 'WithDefault'
) {
throw new Error(
'Nested defaults such as "$ReadOnlyArray<WithDefault<boolean, false>>" are not supported, please declare defaults at the top level of value definitions as in "WithDefault<$ReadOnlyArray<boolean>, false>"',
);
}
if (extractedTypeAnnotation.type === 'GenericTypeAnnotation') {
// Resolve the type alias if it's not defined inline
const objectType = getValueFromTypes(extractedTypeAnnotation, types);
if (objectType.id.name === '$ReadOnly') {
return {
type: 'ObjectTypeAnnotation',
properties: flattenProperties(
objectType.typeParameters.params[0].properties,
types,
parser,
)
.map(prop => buildSchema(prop, types, parser))
.filter(Boolean),
};
}
if (objectType.id.name === '$ReadOnlyArray') {
// We need to go yet another level deeper to resolve
// types that may be defined in a type alias
const nestedObjectType = getValueFromTypes(
objectType.typeParameters.params[0],
types,
);
return {
type: 'ArrayTypeAnnotation',
elementType: {
type: 'ObjectTypeAnnotation',
properties: flattenProperties(
nestedObjectType.typeParameters.params[0].properties,
types,
parser,
)
.map(prop => buildSchema(prop, types, parser))
.filter(Boolean),
},
};
}
}
const type =
extractedTypeAnnotation.type === 'GenericTypeAnnotation'
? parser.getTypeAnnotationName(extractedTypeAnnotation)
: extractedTypeAnnotation.type;
switch (type) {
case 'ImageSource':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ImageSourcePrimitive',
};
case 'ImageRequest':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ImageRequestPrimitive',
};
case 'ColorValue':
case 'ProcessedColorValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ColorPrimitive',
};
case 'PointValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'PointPrimitive',
};
case 'EdgeInsetsValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'EdgeInsetsPrimitive',
};
case 'DimensionValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'DimensionPrimitive',
};
case 'Stringish':
return {
type: 'StringTypeAnnotation',
};
case 'Int32':
return {
type: 'Int32TypeAnnotation',
};
case 'Double':
return {
type: 'DoubleTypeAnnotation',
};
case 'Float':
return {
type: 'FloatTypeAnnotation',
};
case 'BooleanTypeAnnotation':
return {
type: 'BooleanTypeAnnotation',
};
case 'StringTypeAnnotation':
return {
type: 'StringTypeAnnotation',
};
case 'UnionTypeAnnotation':
typeAnnotation.types.reduce((lastType, currType) => {
if (lastType && currType.type !== lastType.type) {
throw new Error(`Mixed types are not supported (see "${name}")`);
}
return currType;
});
if (defaultValue === null) {
throw new Error(`A default enum value is required for "${name}"`);
}
const unionType = typeAnnotation.types[0].type;
if (unionType === 'StringLiteralTypeAnnotation') {
return {
type: 'StringEnumTypeAnnotation',
default: defaultValue,
options: typeAnnotation.types.map(option => option.value),
};
} else if (unionType === 'NumberLiteralTypeAnnotation') {
throw new Error(
`Arrays of int enums are not supported (see: "${name}")`,
);
} else {
throw new Error(
`Unsupported union type for "${name}", received "${unionType}"`,
);
}
default:
throw new Error(`Unknown property type for "${name}": ${type}`);
}
}
function flattenProperties(typeDefinition, types, parser) {
return typeDefinition
.map(property => {
if (property.type === 'ObjectTypeProperty') {
return property;
} else if (property.type === 'ObjectTypeSpreadProperty') {
return flattenProperties(
parser.getProperties(property.argument.id.name, types),
types,
parser,
);
}
})
.reduce((acc, item) => {
if (Array.isArray(item)) {
item.forEach(prop => {
verifyPropNotAlreadyDefined(acc, prop);
});
return acc.concat(item);
} else {
verifyPropNotAlreadyDefined(acc, item);
acc.push(item);
return acc;
}
}, [])
.filter(Boolean);
}
// $FlowFixMe[unsupported-variance-annotation]
function getTypeAnnotation(
name,
annotation,
defaultValue,
withNullDefault,
types,
parser,
buildSchema,
) {
const typeAnnotation = getValueFromTypes(annotation, types);
if (
typeAnnotation.type === 'GenericTypeAnnotation' &&
parser.getTypeAnnotationName(typeAnnotation) === '$ReadOnlyArray'
) {
return {
type: 'ArrayTypeAnnotation',
elementType: getTypeAnnotationForArray(
name,
typeAnnotation.typeParameters.params[0],
defaultValue,
types,
parser,
buildSchema,
),
};
}
if (
typeAnnotation.type === 'GenericTypeAnnotation' &&
parser.getTypeAnnotationName(typeAnnotation) === '$ReadOnly'
) {
return {
type: 'ObjectTypeAnnotation',
properties: flattenProperties(
typeAnnotation.typeParameters.params[0].properties,
types,
parser,
)
.map(prop => buildSchema(prop, types, parser))
.filter(Boolean),
};
}
const type =
typeAnnotation.type === 'GenericTypeAnnotation'
? parser.getTypeAnnotationName(typeAnnotation)
: typeAnnotation.type;
switch (type) {
case 'ImageSource':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ImageSourcePrimitive',
};
case 'ImageRequest':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ImageRequestPrimitive',
};
case 'ColorValue':
case 'ProcessedColorValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ColorPrimitive',
};
case 'ColorArrayValue':
return {
type: 'ArrayTypeAnnotation',
elementType: {
type: 'ReservedPropTypeAnnotation',
name: 'ColorPrimitive',
},
};
case 'PointValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'PointPrimitive',
};
case 'EdgeInsetsValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'EdgeInsetsPrimitive',
};
case 'DimensionValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'DimensionPrimitive',
};
case 'Int32':
return {
type: 'Int32TypeAnnotation',
default: defaultValue ? defaultValue : 0,
};
case 'Double':
return {
type: 'DoubleTypeAnnotation',
default: defaultValue ? defaultValue : 0,
};
case 'Float':
return {
type: 'FloatTypeAnnotation',
default: withNullDefault
? defaultValue
: defaultValue
? defaultValue
: 0,
};
case 'BooleanTypeAnnotation':
return {
type: 'BooleanTypeAnnotation',
default: withNullDefault
? defaultValue
: defaultValue == null
? false
: defaultValue,
};
case 'StringTypeAnnotation':
if (typeof defaultValue !== 'undefined') {
return {
type: 'StringTypeAnnotation',
default: defaultValue,
};
}
throw new Error(`A default string (or null) is required for "${name}"`);
case 'Stringish':
if (typeof defaultValue !== 'undefined') {
return {
type: 'StringTypeAnnotation',
default: defaultValue,
};
}
throw new Error(`A default string (or null) is required for "${name}"`);
case 'UnionTypeAnnotation':
typeAnnotation.types.reduce((lastType, currType) => {
if (lastType && currType.type !== lastType.type) {
throw new Error(`Mixed types are not supported (see "${name}").`);
}
return currType;
});
if (defaultValue === null) {
throw new Error(`A default enum value is required for "${name}"`);
}
const unionType = typeAnnotation.types[0].type;
if (unionType === 'StringLiteralTypeAnnotation') {
return {
type: 'StringEnumTypeAnnotation',
default: defaultValue,
options: typeAnnotation.types.map(option => option.value),
};
} else if (unionType === 'NumberLiteralTypeAnnotation') {
return {
type: 'Int32EnumTypeAnnotation',
default: defaultValue,
options: typeAnnotation.types.map(option => option.value),
};
} else {
throw new Error(
`Unsupported union type for "${name}", received "${unionType}"`,
);
}
case 'ObjectTypeAnnotation':
throw new Error(
`Cannot use "${type}" type annotation for "${name}": object types must be declared using $ReadOnly<>`,
);
case 'NumberTypeAnnotation':
throw new Error(
`Cannot use "${type}" type annotation for "${name}": must use a specific numeric type like Int32, Double, or Float`,
);
case 'UnsafeMixed':
return {
type: 'MixedTypeAnnotation',
};
default:
throw new Error(
`Unknown property type for "${name}": "${type}" in the State`,
);
}
}
function getSchemaInfo(property, types) {
const name = property.key.name;
const value = getValueFromTypes(property.value, types);
let typeAnnotation =
value.type === 'NullableTypeAnnotation' ? value.typeAnnotation : value;
const optional =
value.type === 'NullableTypeAnnotation' ||
property.optional ||
(value.type === 'GenericTypeAnnotation' &&
typeAnnotation.id.name === 'WithDefault');
if (
!property.optional &&
value.type === 'GenericTypeAnnotation' &&
typeAnnotation.id.name === 'WithDefault'
) {
throw new Error(
`key ${name} must be optional if used with WithDefault<> annotation`,
);
}
if (
value.type === 'NullableTypeAnnotation' &&
typeAnnotation.type === 'GenericTypeAnnotation' &&
typeAnnotation.id.name === 'WithDefault'
) {
throw new Error(
'WithDefault<> is optional and does not need to be marked as optional. Please remove the ? annotation in front of it.',
);
}
let type = typeAnnotation.type;
if (
type === 'GenericTypeAnnotation' &&
(typeAnnotation.id.name === 'DirectEventHandler' ||
typeAnnotation.id.name === 'BubblingEventHandler')
) {
return null;
}
if (
name === 'style' &&
type === 'GenericTypeAnnotation' &&
typeAnnotation.id.name === 'ViewStyleProp'
) {
return null;
}
let defaultValue = null;
let withNullDefault = false;
if (
type === 'GenericTypeAnnotation' &&
typeAnnotation.id.name === 'WithDefault'
) {
if (typeAnnotation.typeParameters.params.length === 1) {
throw new Error(
`WithDefault requires two parameters, did you forget to provide a default value for "${name}"?`,
);
}
defaultValue = typeAnnotation.typeParameters.params[1].value;
const defaultValueType = typeAnnotation.typeParameters.params[1].type;
typeAnnotation = typeAnnotation.typeParameters.params[0];
type =
typeAnnotation.type === 'GenericTypeAnnotation'
? typeAnnotation.id.name
: typeAnnotation.type;
if (defaultValueType === 'NullLiteralTypeAnnotation') {
defaultValue = null;
withNullDefault = true;
}
}
return {
name,
optional,
typeAnnotation,
defaultValue,
withNullDefault,
};
}
module.exports = {
getSchemaInfo,
getTypeAnnotation,
flattenProperties,
};

View File

@@ -0,0 +1,490 @@
/**
* 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 {BuildSchemaFN, Parser} from '../../parser';
import type {ASTNode, PropAST, TypeDeclarationMap} from '../../utils';
const {verifyPropNotAlreadyDefined} = require('../../parsers-commons');
const {getValueFromTypes} = require('../utils.js');
// $FlowFixMe[unsupported-variance-annotation]
function getTypeAnnotationForArray<+T>(
name: string,
typeAnnotation: $FlowFixMe,
defaultValue: $FlowFixMe | null,
types: TypeDeclarationMap,
parser: Parser,
buildSchema: BuildSchemaFN<T>,
): $FlowFixMe {
const extractedTypeAnnotation = getValueFromTypes(typeAnnotation, types);
if (extractedTypeAnnotation.type === 'NullableTypeAnnotation') {
throw new Error(
'Nested optionals such as "$ReadOnlyArray<?boolean>" are not supported, please declare optionals at the top level of value definitions as in "?$ReadOnlyArray<boolean>"',
);
}
if (
extractedTypeAnnotation.type === 'GenericTypeAnnotation' &&
parser.getTypeAnnotationName(extractedTypeAnnotation) === 'WithDefault'
) {
throw new Error(
'Nested defaults such as "$ReadOnlyArray<WithDefault<boolean, false>>" are not supported, please declare defaults at the top level of value definitions as in "WithDefault<$ReadOnlyArray<boolean>, false>"',
);
}
if (extractedTypeAnnotation.type === 'GenericTypeAnnotation') {
// Resolve the type alias if it's not defined inline
const objectType = getValueFromTypes(extractedTypeAnnotation, types);
if (objectType.id.name === '$ReadOnly') {
return {
type: 'ObjectTypeAnnotation',
properties: flattenProperties(
objectType.typeParameters.params[0].properties,
types,
parser,
)
.map(prop => buildSchema(prop, types, parser))
.filter(Boolean),
};
}
if (objectType.id.name === '$ReadOnlyArray') {
// We need to go yet another level deeper to resolve
// types that may be defined in a type alias
const nestedObjectType = getValueFromTypes(
objectType.typeParameters.params[0],
types,
);
return {
type: 'ArrayTypeAnnotation',
elementType: {
type: 'ObjectTypeAnnotation',
properties: flattenProperties(
nestedObjectType.typeParameters.params[0].properties,
types,
parser,
)
.map(prop => buildSchema(prop, types, parser))
.filter(Boolean),
},
};
}
}
const type =
extractedTypeAnnotation.type === 'GenericTypeAnnotation'
? parser.getTypeAnnotationName(extractedTypeAnnotation)
: extractedTypeAnnotation.type;
switch (type) {
case 'ImageSource':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ImageSourcePrimitive',
};
case 'ImageRequest':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ImageRequestPrimitive',
};
case 'ColorValue':
case 'ProcessedColorValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ColorPrimitive',
};
case 'PointValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'PointPrimitive',
};
case 'EdgeInsetsValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'EdgeInsetsPrimitive',
};
case 'DimensionValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'DimensionPrimitive',
};
case 'Stringish':
return {
type: 'StringTypeAnnotation',
};
case 'Int32':
return {
type: 'Int32TypeAnnotation',
};
case 'Double':
return {
type: 'DoubleTypeAnnotation',
};
case 'Float':
return {
type: 'FloatTypeAnnotation',
};
case 'BooleanTypeAnnotation':
return {
type: 'BooleanTypeAnnotation',
};
case 'StringTypeAnnotation':
return {
type: 'StringTypeAnnotation',
};
case 'UnionTypeAnnotation':
typeAnnotation.types.reduce((lastType, currType) => {
if (lastType && currType.type !== lastType.type) {
throw new Error(`Mixed types are not supported (see "${name}")`);
}
return currType;
});
if (defaultValue === null) {
throw new Error(`A default enum value is required for "${name}"`);
}
const unionType = typeAnnotation.types[0].type;
if (unionType === 'StringLiteralTypeAnnotation') {
return {
type: 'StringEnumTypeAnnotation',
default: (defaultValue: string),
options: typeAnnotation.types.map(option => option.value),
};
} else if (unionType === 'NumberLiteralTypeAnnotation') {
throw new Error(
`Arrays of int enums are not supported (see: "${name}")`,
);
} else {
throw new Error(
`Unsupported union type for "${name}", received "${unionType}"`,
);
}
default:
throw new Error(`Unknown property type for "${name}": ${type}`);
}
}
function flattenProperties(
typeDefinition: $ReadOnlyArray<PropAST>,
types: TypeDeclarationMap,
parser: Parser,
): $ReadOnlyArray<PropAST> {
return typeDefinition
.map(property => {
if (property.type === 'ObjectTypeProperty') {
return property;
} else if (property.type === 'ObjectTypeSpreadProperty') {
return flattenProperties(
parser.getProperties(property.argument.id.name, types),
types,
parser,
);
}
})
.reduce((acc: Array<PropAST>, item) => {
if (Array.isArray(item)) {
item.forEach(prop => {
verifyPropNotAlreadyDefined(acc, prop);
});
return acc.concat(item);
} else {
verifyPropNotAlreadyDefined(acc, item);
acc.push(item);
return acc;
}
}, [])
.filter(Boolean);
}
// $FlowFixMe[unsupported-variance-annotation]
function getTypeAnnotation<+T>(
name: string,
annotation: $FlowFixMe | ASTNode,
defaultValue: $FlowFixMe | null,
withNullDefault: boolean,
types: TypeDeclarationMap,
parser: Parser,
buildSchema: BuildSchemaFN<T>,
): $FlowFixMe {
const typeAnnotation = getValueFromTypes(annotation, types);
if (
typeAnnotation.type === 'GenericTypeAnnotation' &&
parser.getTypeAnnotationName(typeAnnotation) === '$ReadOnlyArray'
) {
return {
type: 'ArrayTypeAnnotation',
elementType: getTypeAnnotationForArray(
name,
typeAnnotation.typeParameters.params[0],
defaultValue,
types,
parser,
buildSchema,
),
};
}
if (
typeAnnotation.type === 'GenericTypeAnnotation' &&
parser.getTypeAnnotationName(typeAnnotation) === '$ReadOnly'
) {
return {
type: 'ObjectTypeAnnotation',
properties: flattenProperties(
typeAnnotation.typeParameters.params[0].properties,
types,
parser,
)
.map(prop => buildSchema(prop, types, parser))
.filter(Boolean),
};
}
const type =
typeAnnotation.type === 'GenericTypeAnnotation'
? parser.getTypeAnnotationName(typeAnnotation)
: typeAnnotation.type;
switch (type) {
case 'ImageSource':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ImageSourcePrimitive',
};
case 'ImageRequest':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ImageRequestPrimitive',
};
case 'ColorValue':
case 'ProcessedColorValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ColorPrimitive',
};
case 'ColorArrayValue':
return {
type: 'ArrayTypeAnnotation',
elementType: {
type: 'ReservedPropTypeAnnotation',
name: 'ColorPrimitive',
},
};
case 'PointValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'PointPrimitive',
};
case 'EdgeInsetsValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'EdgeInsetsPrimitive',
};
case 'DimensionValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'DimensionPrimitive',
};
case 'Int32':
return {
type: 'Int32TypeAnnotation',
default: ((defaultValue ? defaultValue : 0): number),
};
case 'Double':
return {
type: 'DoubleTypeAnnotation',
default: ((defaultValue ? defaultValue : 0): number),
};
case 'Float':
return {
type: 'FloatTypeAnnotation',
default: withNullDefault
? (defaultValue: number | null)
: ((defaultValue ? defaultValue : 0): number),
};
case 'BooleanTypeAnnotation':
return {
type: 'BooleanTypeAnnotation',
default: withNullDefault
? (defaultValue: boolean | null)
: ((defaultValue == null ? false : defaultValue): boolean),
};
case 'StringTypeAnnotation':
if (typeof defaultValue !== 'undefined') {
return {
type: 'StringTypeAnnotation',
default: (defaultValue: string | null),
};
}
throw new Error(`A default string (or null) is required for "${name}"`);
case 'Stringish':
if (typeof defaultValue !== 'undefined') {
return {
type: 'StringTypeAnnotation',
default: (defaultValue: string | null),
};
}
throw new Error(`A default string (or null) is required for "${name}"`);
case 'UnionTypeAnnotation':
typeAnnotation.types.reduce((lastType, currType) => {
if (lastType && currType.type !== lastType.type) {
throw new Error(`Mixed types are not supported (see "${name}").`);
}
return currType;
});
if (defaultValue === null) {
throw new Error(`A default enum value is required for "${name}"`);
}
const unionType = typeAnnotation.types[0].type;
if (unionType === 'StringLiteralTypeAnnotation') {
return {
type: 'StringEnumTypeAnnotation',
default: (defaultValue: string),
options: typeAnnotation.types.map(option => option.value),
};
} else if (unionType === 'NumberLiteralTypeAnnotation') {
return {
type: 'Int32EnumTypeAnnotation',
default: (defaultValue: number),
options: typeAnnotation.types.map(option => option.value),
};
} else {
throw new Error(
`Unsupported union type for "${name}", received "${unionType}"`,
);
}
case 'ObjectTypeAnnotation':
throw new Error(
`Cannot use "${type}" type annotation for "${name}": object types must be declared using $ReadOnly<>`,
);
case 'NumberTypeAnnotation':
throw new Error(
`Cannot use "${type}" type annotation for "${name}": must use a specific numeric type like Int32, Double, or Float`,
);
case 'UnsafeMixed':
return {
type: 'MixedTypeAnnotation',
};
default:
throw new Error(
`Unknown property type for "${name}": "${type}" in the State`,
);
}
}
type SchemaInfo = {
name: string,
optional: boolean,
typeAnnotation: $FlowFixMe,
defaultValue: $FlowFixMe,
withNullDefault: boolean,
};
function getSchemaInfo(
property: PropAST,
types: TypeDeclarationMap,
): ?SchemaInfo {
const name = property.key.name;
const value = getValueFromTypes(property.value, types);
let typeAnnotation =
value.type === 'NullableTypeAnnotation' ? value.typeAnnotation : value;
const optional =
value.type === 'NullableTypeAnnotation' ||
property.optional ||
(value.type === 'GenericTypeAnnotation' &&
typeAnnotation.id.name === 'WithDefault');
if (
!property.optional &&
value.type === 'GenericTypeAnnotation' &&
typeAnnotation.id.name === 'WithDefault'
) {
throw new Error(
`key ${name} must be optional if used with WithDefault<> annotation`,
);
}
if (
value.type === 'NullableTypeAnnotation' &&
typeAnnotation.type === 'GenericTypeAnnotation' &&
typeAnnotation.id.name === 'WithDefault'
) {
throw new Error(
'WithDefault<> is optional and does not need to be marked as optional. Please remove the ? annotation in front of it.',
);
}
let type = typeAnnotation.type;
if (
type === 'GenericTypeAnnotation' &&
(typeAnnotation.id.name === 'DirectEventHandler' ||
typeAnnotation.id.name === 'BubblingEventHandler')
) {
return null;
}
if (
name === 'style' &&
type === 'GenericTypeAnnotation' &&
typeAnnotation.id.name === 'ViewStyleProp'
) {
return null;
}
let defaultValue = null;
let withNullDefault = false;
if (
type === 'GenericTypeAnnotation' &&
typeAnnotation.id.name === 'WithDefault'
) {
if (typeAnnotation.typeParameters.params.length === 1) {
throw new Error(
`WithDefault requires two parameters, did you forget to provide a default value for "${name}"?`,
);
}
defaultValue = typeAnnotation.typeParameters.params[1].value;
const defaultValueType = typeAnnotation.typeParameters.params[1].type;
typeAnnotation = typeAnnotation.typeParameters.params[0];
type =
typeAnnotation.type === 'GenericTypeAnnotation'
? typeAnnotation.id.name
: typeAnnotation.type;
if (defaultValueType === 'NullLiteralTypeAnnotation') {
defaultValue = null;
withNullDefault = true;
}
}
return {
name,
optional,
typeAnnotation,
defaultValue,
withNullDefault,
};
}
module.exports = {
getSchemaInfo,
getTypeAnnotation,
flattenProperties,
};

View File

@@ -0,0 +1,256 @@
/**
* 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';
const _require = require('../../error-utils'),
throwIfArgumentPropsAreNull = _require.throwIfArgumentPropsAreNull,
throwIfBubblingTypeIsNull = _require.throwIfBubblingTypeIsNull,
throwIfEventHasNoName = _require.throwIfEventHasNoName;
const _require2 = require('../../parsers-commons'),
buildPropertiesForEvent = _require2.buildPropertiesForEvent,
emitBuildEventSchema = _require2.emitBuildEventSchema,
getEventArgument = _require2.getEventArgument,
handleEventHandler = _require2.handleEventHandler;
const _require3 = require('../../parsers-primitives'),
emitBoolProp = _require3.emitBoolProp,
emitDoubleProp = _require3.emitDoubleProp,
emitFloatProp = _require3.emitFloatProp,
emitInt32Prop = _require3.emitInt32Prop,
emitMixedProp = _require3.emitMixedProp,
emitObjectProp = _require3.emitObjectProp,
emitStringProp = _require3.emitStringProp,
emitUnionProp = _require3.emitUnionProp;
function getPropertyType(
/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's
* LTI update could not be added via codemod */
name,
optional,
typeAnnotation,
parser,
) {
const type = extractTypeFromTypeAnnotation(typeAnnotation, parser);
switch (type) {
case 'BooleanTypeAnnotation':
return emitBoolProp(name, optional);
case 'StringTypeAnnotation':
return emitStringProp(name, optional);
case 'Int32':
return emitInt32Prop(name, optional);
case 'Double':
return emitDoubleProp(name, optional);
case 'Float':
return emitFloatProp(name, optional);
case '$ReadOnly':
return getPropertyType(
name,
optional,
typeAnnotation.typeParameters.params[0],
parser,
);
case 'ObjectTypeAnnotation':
return emitObjectProp(
name,
optional,
parser,
typeAnnotation,
extractArrayElementType,
);
case 'UnionTypeAnnotation':
return emitUnionProp(name, optional, parser, typeAnnotation);
case 'UnsafeMixed':
return emitMixedProp(name, optional);
case 'ArrayTypeAnnotation':
case '$ReadOnlyArray':
return {
name,
optional,
typeAnnotation: extractArrayElementType(typeAnnotation, name, parser),
};
default:
throw new Error(`Unable to determine event type for "${name}": ${type}`);
}
}
function extractArrayElementType(typeAnnotation, name, parser) {
const type = extractTypeFromTypeAnnotation(typeAnnotation, parser);
switch (type) {
case 'BooleanTypeAnnotation':
return {
type: 'BooleanTypeAnnotation',
};
case 'StringTypeAnnotation':
return {
type: 'StringTypeAnnotation',
};
case 'Int32':
return {
type: 'Int32TypeAnnotation',
};
case 'Float':
return {
type: 'FloatTypeAnnotation',
};
case 'NumberTypeAnnotation':
case 'Double':
return {
type: 'DoubleTypeAnnotation',
};
case 'UnionTypeAnnotation':
return {
type: 'StringEnumTypeAnnotation',
options: typeAnnotation.types.map(option =>
parser.getLiteralValue(option),
),
};
case 'UnsafeMixed':
return {
type: 'MixedTypeAnnotation',
};
case 'ObjectTypeAnnotation':
return {
type: 'ObjectTypeAnnotation',
properties: parser
.getObjectProperties(typeAnnotation)
.map(member =>
buildPropertiesForEvent(member, parser, getPropertyType),
),
};
case 'ArrayTypeAnnotation':
return {
type: 'ArrayTypeAnnotation',
elementType: extractArrayElementType(
typeAnnotation.elementType,
name,
parser,
),
};
case '$ReadOnlyArray':
const genericParams = typeAnnotation.typeParameters.params;
if (genericParams.length !== 1) {
throw new Error(
`Events only supports arrays with 1 Generic type. Found ${
genericParams.length
} types:\n${prettify(genericParams)}`,
);
}
return {
type: 'ArrayTypeAnnotation',
elementType: extractArrayElementType(genericParams[0], name, parser),
};
default:
throw new Error(
`Unrecognized ${type} for Array ${name} in events.\n${prettify(
typeAnnotation,
)}`,
);
}
}
function prettify(jsonObject) {
return JSON.stringify(jsonObject, null, 2);
}
function extractTypeFromTypeAnnotation(typeAnnotation, parser) {
return typeAnnotation.type === 'GenericTypeAnnotation'
? parser.getTypeAnnotationName(typeAnnotation)
: typeAnnotation.type;
}
function findEventArgumentsAndType(
parser,
typeAnnotation,
types,
bubblingType,
paperName,
) {
throwIfEventHasNoName(typeAnnotation, parser);
const name = parser.getTypeAnnotationName(typeAnnotation);
if (name === '$ReadOnly') {
return {
argumentProps: typeAnnotation.typeParameters.params[0].properties,
paperTopLevelNameDeprecated: paperName,
bubblingType,
};
} else if (name === 'BubblingEventHandler' || name === 'DirectEventHandler') {
return handleEventHandler(
name,
typeAnnotation,
parser,
types,
findEventArgumentsAndType,
);
} else if (types[name]) {
return findEventArgumentsAndType(
parser,
types[name].right,
types,
bubblingType,
paperName,
);
} else {
return {
argumentProps: null,
bubblingType: null,
paperTopLevelNameDeprecated: null,
};
}
}
function buildEventSchema(types, property, parser) {
const name = property.key.name;
const optional =
property.optional || property.value.type === 'NullableTypeAnnotation';
let typeAnnotation =
property.value.type === 'NullableTypeAnnotation'
? property.value.typeAnnotation
: property.value;
if (
typeAnnotation.type !== 'GenericTypeAnnotation' ||
(parser.getTypeAnnotationName(typeAnnotation) !== 'BubblingEventHandler' &&
parser.getTypeAnnotationName(typeAnnotation) !== 'DirectEventHandler')
) {
return null;
}
const _findEventArgumentsAn = findEventArgumentsAndType(
parser,
typeAnnotation,
types,
),
argumentProps = _findEventArgumentsAn.argumentProps,
bubblingType = _findEventArgumentsAn.bubblingType,
paperTopLevelNameDeprecated =
_findEventArgumentsAn.paperTopLevelNameDeprecated;
const nonNullableArgumentProps = throwIfArgumentPropsAreNull(
argumentProps,
name,
);
const nonNullableBubblingType = throwIfBubblingTypeIsNull(bubblingType, name);
const argument = getEventArgument(
nonNullableArgumentProps,
parser,
getPropertyType,
);
return emitBuildEventSchema(
paperTopLevelNameDeprecated,
name,
optional,
nonNullableBubblingType,
argument,
);
}
// $FlowFixMe[unclear-type] there's no flowtype for ASTs
function getEvents(eventTypeAST, types, parser) {
return eventTypeAST
.filter(property => property.type === 'ObjectTypeProperty')
.map(property => buildEventSchema(types, property, parser))
.filter(Boolean);
}
module.exports = {
getEvents,
extractArrayElementType,
};

View File

@@ -0,0 +1,287 @@
/**
* 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 {
EventTypeAnnotation,
EventTypeShape,
NamedShape,
} from '../../../CodegenSchema.js';
import type {Parser} from '../../parser';
import type {EventArgumentReturnType} from '../../parsers-commons';
const {
throwIfArgumentPropsAreNull,
throwIfBubblingTypeIsNull,
throwIfEventHasNoName,
} = require('../../error-utils');
const {
buildPropertiesForEvent,
emitBuildEventSchema,
getEventArgument,
handleEventHandler,
} = require('../../parsers-commons');
const {
emitBoolProp,
emitDoubleProp,
emitFloatProp,
emitInt32Prop,
emitMixedProp,
emitObjectProp,
emitStringProp,
emitUnionProp,
} = require('../../parsers-primitives');
function getPropertyType(
/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's
* LTI update could not be added via codemod */
name: string,
optional: boolean,
typeAnnotation: $FlowFixMe,
parser: Parser,
): NamedShape<EventTypeAnnotation> {
const type = extractTypeFromTypeAnnotation(typeAnnotation, parser);
switch (type) {
case 'BooleanTypeAnnotation':
return emitBoolProp(name, optional);
case 'StringTypeAnnotation':
return emitStringProp(name, optional);
case 'Int32':
return emitInt32Prop(name, optional);
case 'Double':
return emitDoubleProp(name, optional);
case 'Float':
return emitFloatProp(name, optional);
case '$ReadOnly':
return getPropertyType(
name,
optional,
typeAnnotation.typeParameters.params[0],
parser,
);
case 'ObjectTypeAnnotation':
return emitObjectProp(
name,
optional,
parser,
typeAnnotation,
extractArrayElementType,
);
case 'UnionTypeAnnotation':
return emitUnionProp(name, optional, parser, typeAnnotation);
case 'UnsafeMixed':
return emitMixedProp(name, optional);
case 'ArrayTypeAnnotation':
case '$ReadOnlyArray':
return {
name,
optional,
typeAnnotation: extractArrayElementType(typeAnnotation, name, parser),
};
default:
throw new Error(`Unable to determine event type for "${name}": ${type}`);
}
}
function extractArrayElementType(
typeAnnotation: $FlowFixMe,
name: string,
parser: Parser,
): EventTypeAnnotation {
const type = extractTypeFromTypeAnnotation(typeAnnotation, parser);
switch (type) {
case 'BooleanTypeAnnotation':
return {type: 'BooleanTypeAnnotation'};
case 'StringTypeAnnotation':
return {type: 'StringTypeAnnotation'};
case 'Int32':
return {type: 'Int32TypeAnnotation'};
case 'Float':
return {type: 'FloatTypeAnnotation'};
case 'NumberTypeAnnotation':
case 'Double':
return {
type: 'DoubleTypeAnnotation',
};
case 'UnionTypeAnnotation':
return {
type: 'StringEnumTypeAnnotation',
options: typeAnnotation.types.map(option =>
parser.getLiteralValue(option),
),
};
case 'UnsafeMixed':
return {type: 'MixedTypeAnnotation'};
case 'ObjectTypeAnnotation':
return {
type: 'ObjectTypeAnnotation',
properties: parser
.getObjectProperties(typeAnnotation)
.map(member =>
buildPropertiesForEvent(member, parser, getPropertyType),
),
};
case 'ArrayTypeAnnotation':
return {
type: 'ArrayTypeAnnotation',
elementType: extractArrayElementType(
typeAnnotation.elementType,
name,
parser,
),
};
case '$ReadOnlyArray':
const genericParams = typeAnnotation.typeParameters.params;
if (genericParams.length !== 1) {
throw new Error(
`Events only supports arrays with 1 Generic type. Found ${
genericParams.length
} types:\n${prettify(genericParams)}`,
);
}
return {
type: 'ArrayTypeAnnotation',
elementType: extractArrayElementType(genericParams[0], name, parser),
};
default:
throw new Error(
`Unrecognized ${type} for Array ${name} in events.\n${prettify(
typeAnnotation,
)}`,
);
}
}
function prettify(jsonObject: $FlowFixMe): string {
return JSON.stringify(jsonObject, null, 2);
}
function extractTypeFromTypeAnnotation(
typeAnnotation: $FlowFixMe,
parser: Parser,
): string {
return typeAnnotation.type === 'GenericTypeAnnotation'
? parser.getTypeAnnotationName(typeAnnotation)
: typeAnnotation.type;
}
function findEventArgumentsAndType(
parser: Parser,
typeAnnotation: $FlowFixMe,
types: TypeMap,
bubblingType: void | 'direct' | 'bubble',
paperName: ?$FlowFixMe,
): EventArgumentReturnType {
throwIfEventHasNoName(typeAnnotation, parser);
const name = parser.getTypeAnnotationName(typeAnnotation);
if (name === '$ReadOnly') {
return {
argumentProps: typeAnnotation.typeParameters.params[0].properties,
paperTopLevelNameDeprecated: paperName,
bubblingType,
};
} else if (name === 'BubblingEventHandler' || name === 'DirectEventHandler') {
return handleEventHandler(
name,
typeAnnotation,
parser,
types,
findEventArgumentsAndType,
);
} else if (types[name]) {
return findEventArgumentsAndType(
parser,
types[name].right,
types,
bubblingType,
paperName,
);
} else {
return {
argumentProps: null,
bubblingType: null,
paperTopLevelNameDeprecated: null,
};
}
}
function buildEventSchema(
types: TypeMap,
property: EventTypeAST,
parser: Parser,
): ?EventTypeShape {
const name = property.key.name;
const optional =
property.optional || property.value.type === 'NullableTypeAnnotation';
let typeAnnotation =
property.value.type === 'NullableTypeAnnotation'
? property.value.typeAnnotation
: property.value;
if (
typeAnnotation.type !== 'GenericTypeAnnotation' ||
(parser.getTypeAnnotationName(typeAnnotation) !== 'BubblingEventHandler' &&
parser.getTypeAnnotationName(typeAnnotation) !== 'DirectEventHandler')
) {
return null;
}
const {argumentProps, bubblingType, paperTopLevelNameDeprecated} =
findEventArgumentsAndType(parser, typeAnnotation, types);
const nonNullableArgumentProps = throwIfArgumentPropsAreNull(
argumentProps,
name,
);
const nonNullableBubblingType = throwIfBubblingTypeIsNull(bubblingType, name);
const argument = getEventArgument(
nonNullableArgumentProps,
parser,
getPropertyType,
);
return emitBuildEventSchema(
paperTopLevelNameDeprecated,
name,
optional,
nonNullableBubblingType,
argument,
);
}
// $FlowFixMe[unclear-type] there's no flowtype for ASTs
type EventTypeAST = Object;
type TypeMap = {
// $FlowFixMe[unclear-type] there's no flowtype for ASTs
[string]: Object,
...
};
function getEvents(
eventTypeAST: $ReadOnlyArray<EventTypeAST>,
types: TypeMap,
parser: Parser,
): $ReadOnlyArray<EventTypeShape> {
return eventTypeAST
.filter(property => property.type === 'ObjectTypeProperty')
.map(property => buildEventSchema(types, property, parser))
.filter(Boolean);
}
module.exports = {
getEvents,
extractArrayElementType,
};

View File

@@ -0,0 +1,49 @@
/**
* 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';
const _require = require('../../parsers-commons'),
findComponentConfig = _require.findComponentConfig,
getCommandProperties = _require.getCommandProperties,
getOptions = _require.getOptions;
const _require2 = require('./commands'),
getCommands = _require2.getCommands;
const _require3 = require('./events'),
getEvents = _require3.getEvents;
// $FlowFixMe[signature-verification-failure] there's no flowtype for AST
function buildComponentSchema(ast, parser) {
const _findComponentConfig = findComponentConfig(ast, parser),
componentName = _findComponentConfig.componentName,
propsTypeName = _findComponentConfig.propsTypeName,
optionsExpression = _findComponentConfig.optionsExpression;
const types = parser.getTypes(ast);
const propProperties = parser.getProperties(propsTypeName, types);
const commandProperties = getCommandProperties(ast, parser);
const _parser$getProps = parser.getProps(propProperties, types),
extendsProps = _parser$getProps.extendsProps,
props = _parser$getProps.props;
const options = getOptions(optionsExpression);
const events = getEvents(propProperties, types, parser);
const commands = getCommands(commandProperties, types);
return {
filename: componentName,
componentName,
options,
extendsProps,
events,
props,
commands,
};
}
module.exports = {
buildComponentSchema,
};

View File

@@ -0,0 +1,56 @@
/**
* 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 {Parser} from '../../parser';
import type {ComponentSchemaBuilderConfig} from '../../schema.js';
const {
findComponentConfig,
getCommandProperties,
getOptions,
} = require('../../parsers-commons');
const {getCommands} = require('./commands');
const {getEvents} = require('./events');
// $FlowFixMe[signature-verification-failure] there's no flowtype for AST
function buildComponentSchema(
ast: $FlowFixMe,
parser: Parser,
): ComponentSchemaBuilderConfig {
const {componentName, propsTypeName, optionsExpression} = findComponentConfig(
ast,
parser,
);
const types = parser.getTypes(ast);
const propProperties = parser.getProperties(propsTypeName, types);
const commandProperties = getCommandProperties(ast, parser);
const {extendsProps, props} = parser.getProps(propProperties, types);
const options = getOptions(optionsExpression);
const events = getEvents(propProperties, types, parser);
const commands = getCommands(commandProperties, types);
return {
filename: componentName,
componentName,
options,
extendsProps,
events,
props,
commands,
};
}
module.exports = {
buildComponentSchema,
};

View File

@@ -0,0 +1,269 @@
/**
* 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
*/
// @licenselint-loose-mode
'use strict';
const NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
getString: (arg: string) => Array;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT_AS_PARAM = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
getString: (arg : Array) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_READ_ONLY_OBJECT_NO_TYPE_FOR_CONTENT = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
getString: (arg : $ReadOnly<>) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_NOT_ONLY_METHODS = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getBool: (arg: boolean) => boolean;
+getNumber: (arg: number) => number;
+getString: (arg: string) => string;
sampleBool: boolean,
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_UNNAMED_PARAMS = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getBool: (boolean) => boolean;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_PROMISE_WITHOUT_TYPE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getBool: (arg: boolean) => Promise;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export default TurboModuleRegistry.getEnforcing<Spec1>('SampleTurboModule1');
export default TurboModuleRegistry.getEnforcing<Spec2>('SampleTurboModule2');
`;
const TWO_NATIVE_EXTENDING_TURBO_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getSth: (a : ?number) => void
}
export interface Spec2 extends TurboModule {
+getSth: (a : ?number) => void
}
`;
const EMPTY_ENUM_NATIVE_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export enum SomeEnum {
}
export interface Spec extends TurboModule {
+getEnums: (a: SomeEnum) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('EmptyEnumNativeModule');
`;
const MIXED_VALUES_ENUM_NATIVE_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export enum SomeEnum {
NUM = 1,
STR = 'str',
}
export interface Spec extends TurboModule {
+getEnums: (a: SomeEnum) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('MixedValuesEnumNativeModule');
`;
module.exports = {
NATIVE_MODULES_WITH_READ_ONLY_OBJECT_NO_TYPE_FOR_CONTENT,
NATIVE_MODULES_WITH_UNNAMED_PARAMS,
NATIVE_MODULES_WITH_PROMISE_WITHOUT_TYPE,
NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT_AS_PARAM,
NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT,
TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT,
NATIVE_MODULES_WITH_NOT_ONLY_METHODS,
TWO_NATIVE_EXTENDING_TURBO_MODULE,
EMPTY_ENUM_NATIVE_MODULE,
MIXED_VALUES_ENUM_NATIVE_MODULE,
};

View File

@@ -0,0 +1,279 @@
/**
* 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
*/
// @licenselint-loose-mode
'use strict';
const NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
getString: (arg: string) => Array;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT_AS_PARAM = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
getString: (arg : Array) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_READ_ONLY_OBJECT_NO_TYPE_FOR_CONTENT = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
getString: (arg : $ReadOnly<>) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_NOT_ONLY_METHODS = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getBool: (arg: boolean) => boolean;
+getNumber: (arg: number) => number;
+getString: (arg: string) => string;
sampleBool: boolean,
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_UNNAMED_PARAMS = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getBool: (boolean) => boolean;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_PROMISE_WITHOUT_TYPE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getBool: (arg: boolean) => Promise;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export default TurboModuleRegistry.getEnforcing<Spec1>('SampleTurboModule1');
export default TurboModuleRegistry.getEnforcing<Spec2>('SampleTurboModule2');
`;
const TWO_NATIVE_EXTENDING_TURBO_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getSth: (a : ?number) => void
}
export interface Spec2 extends TurboModule {
+getSth: (a : ?number) => void
}
`;
const EMPTY_ENUM_NATIVE_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export enum SomeEnum {
}
export interface Spec extends TurboModule {
+getEnums: (a: SomeEnum) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('EmptyEnumNativeModule');
`;
const MIXED_VALUES_ENUM_NATIVE_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export enum SomeEnum {
NUM = 1,
STR = 'str',
}
export interface Spec extends TurboModule {
+getEnums: (a: SomeEnum) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('MixedValuesEnumNativeModule');
`;
module.exports = {
NATIVE_MODULES_WITH_READ_ONLY_OBJECT_NO_TYPE_FOR_CONTENT,
NATIVE_MODULES_WITH_UNNAMED_PARAMS,
NATIVE_MODULES_WITH_PROMISE_WITHOUT_TYPE,
NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT_AS_PARAM,
NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT,
TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT,
NATIVE_MODULES_WITH_NOT_ONLY_METHODS,
TWO_NATIVE_EXTENDING_TURBO_MODULE,
EMPTY_ENUM_NATIVE_MODULE,
MIXED_VALUES_ENUM_NATIVE_MODULE,
};

View File

@@ -0,0 +1,800 @@
/**
* 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
*/
// @licenselint-loose-mode
'use strict';
const EMPTY_NATIVE_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
// no methods
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_COMPLEX_OBJECTS = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type String = string;
export interface Spec extends TurboModule {
// Exported methods.
+getObject: (arg: {|const1: {|const1: boolean|}|}) => {|
const1: {|const1: boolean|},
|};
+getReadOnlyObject: (arg: $ReadOnly<{|const1: $ReadOnly<{|const1: boolean|}>|}>) => $ReadOnly<{|
const1: {|const1: boolean|},
|}>;
+getObject2: (arg: { a: String }) => Object;
+getObjectInArray: (arg: {const1: {|const1: boolean|}}) => Array<{|
const1: {const1: boolean},
|}>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_COMPLEX_OBJECTS_WITH_NULLABLE_KEY = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getConstants: () => {|
isTesting: boolean,
reactNativeVersion: {|
major: number,
minor: number,
patch?: number,
prerelease: ?number,
|},
forceTouchAvailable: boolean,
osVersion: string,
systemName: string,
interfaceIdiom: string,
|};
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_BASIC_PARAM_TYPES = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+passBool?: (arg: boolean) => void;
+passNumber: (arg: number) => void;
+passString: (arg: string) => void;
+passStringish: (arg: Stringish) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ALIASES = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
type NumNum = number;
export type Num = (arg: NumNum) => void;
type Num2 = Num;
export type Void = void;
export type A = number;
export type B = number;
export type ObjectAlias = {|
x: number,
y: number,
label: string,
truthy: boolean,
|};
export type ReadOnlyAlias = $ReadOnly<ObjectAlias>;
export interface Spec extends TurboModule {
// Exported methods.
+getNumber: Num2;
+getVoid: () => Void;
+getArray: (a: Array<A>) => {| a: B |};
+getStringFromAlias: (a: ObjectAlias) => string;
+getStringFromNullableAlias: (a: ?ObjectAlias) => string;
+getStringFromReadOnlyAlias: (a: ReadOnlyAlias) => string;
+getStringFromNullableReadOnlyAlias: (a: ?ReadOnlyAlias) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_NESTED_ALIASES = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
type Bar = {|
z: number
|};
type Foo = {|
bar1: Bar,
bar2: Bar,
|};
export interface Spec extends TurboModule {
// Exported methods.
foo1: (x: Foo) => Foo;
foo2: (x: Foo) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_FLOAT_AND_INT32 = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
import type {Int32, Float} from 'react-native/Libraries/Types/CodegenTypes';
export interface Spec extends TurboModule {
+getInt: (arg: Int32) => Int32;
+getFloat: (arg: Float) => Float;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_SIMPLE_OBJECT = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getObject: (o: Object) => Object,
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_UNSAFE_OBJECT = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
import type {UnsafeObject} from 'react-native/Libraries/Types/CodegenTypes';
export interface Spec extends TurboModule {
+getUnsafeObject: (o: UnsafeObject) => UnsafeObject,
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_PARTIALS = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type SomeObj = {|
a: string,
b?: boolean,
|};
export interface Spec extends TurboModule {
+getSomeObj: () => SomeObj;
+getPartialSomeObj: () => Partial<SomeObj>;
+getSomeObjFromPartialSomeObj: (value: Partial<SomeObj>) => SomeObj;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_PARTIALS_COMPLEX = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type SomeObj = {|
a: string,
b?: boolean,
|};
export type PartialSomeObj = Partial<SomeObj>;
export interface Spec extends TurboModule {
+getPartialPartial: (value1: Partial<SomeObj>, value2: PartialSomeObj) => SomeObj
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ROOT_TAG = `
/**
* 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 {RootTag, TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getRootTag: (rootTag: RootTag) => RootTag,
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_NULLABLE_PARAM = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
// Exported methods.
+voidFunc: (arg: ?string) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_BASIC_ARRAY = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getArray: (arg: Array<string>) => Array<string>;
+getArray: (arg: $ReadOnlyArray<string>) => $ReadOnlyArray<string>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_OBJECT_WITH_OBJECT_DEFINED_IN_FILE_AS_PROPERTY = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
type DisplayMetricsAndroid = {|
width: number,
|};
export interface Spec extends TurboModule {
+getConstants: () => {|
+Dimensions: {
windowPhysicalPixels: DisplayMetricsAndroid,
},
|};
+getConstants2: () => $ReadOnly<{|
+Dimensions: {
windowPhysicalPixels: DisplayMetricsAndroid,
},
|}>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ARRAY_WITH_UNION_AND_TOUPLE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getArray: (arg: Array<[string, string]>) => Array<string | number | boolean>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ARRAY_WITH_ALIAS = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type SomeString = string;
export interface Spec extends TurboModule {
+getArray: (arg: Array<SomeString>) => Array<string>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_COMPLEX_ARRAY = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getArray: (arg: Array<Array<Array<Array<Array<string>>>>>) => Array<Array<Array<string>>>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_PROMISE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type String = string;
export type SomeObj = {| a: string |};
export interface Spec extends TurboModule {
+getValueWithPromise: () => Promise<string>;
+getValueWithPromiseDefinedSomewhereElse: () => Promise<String>;
+getValueWithPromiseObjDefinedSomewhereElse: () => Promise<SomeObj>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_CALLBACK = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
// Exported methods.
+getValueWithCallback: (
callback: (value: string, arr: Array<Array<string>>) => void,
) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_UNION = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type ChooseInt = 1 | 2 | 3;
export type ChooseFloat = 1.44 | 2.88 | 5.76;
export type ChooseObject = {} | {low: string};
export type ChooseString = 'One' | 'Two' | 'Three';
export interface Spec extends TurboModule {
+getUnion: (chooseInt: ChooseInt, chooseFloat: ChooseFloat, chooseObject: ChooseObject, chooseString: ChooseString) => ChooseObject;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const ANDROID_ONLY_NATIVE_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
// no methods
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModuleAndroid');
`;
const IOS_ONLY_NATIVE_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export enum Quality {
SD,
HD,
}
export enum Resolution {
Low = 720,
High = 1080,
}
export enum Floppy {
LowDensity = 0.72,
HighDensity = 1.44,
}
export enum StringOptions {
One = 'one',
Two = 'two',
Three = 'three',
}
export interface Spec extends TurboModule {
getEnums(quality: Quality, resolution?: Resolution, floppy: Floppy, stringOptions: StringOptions): string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModuleIOS');
`;
const CXX_ONLY_NATIVE_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type ChooseInt = 1 | 2 | 3;
export type ChooseFloat = 1.44 | 2.88 | 5.76;
export type ChooseObject = {} | {low: string};
export type ChooseString = 'One' | 'Two' | 'Three';
export enum Quality {
SD,
HD,
}
export enum Resolution {
Low = 720,
High = 1080,
}
export enum Floppy {
LowDensity = 0.72,
HighDensity = 1.44,
}
export enum StringOptions {
One = 'one',
Two = 'two',
Three = 'three',
}
export type BinaryTreeNode = {
left?: BinaryTreeNode,
value: number,
right?: BinaryTreeNode,
};
export type GraphNode = {
label: string,
neighbors?: Array<GraphNode>,
};
export interface Spec extends TurboModule {
+getCallback: () => () => void;
+getMixed: (arg: mixed) => mixed;
+getEnums: (quality: Quality, resolution?: Resolution, floppy: Floppy, stringOptions: StringOptions) => string;
+getBinaryTreeNode: (arg: BinaryTreeNode) => BinaryTreeNode;
+getGraphNode: (arg: GraphNode) => GraphNode;
+getMap: (arg: {[a: string]: ?number}) => {[b: string]: ?number};
+getAnotherMap: (arg: {[string]: string}) => {[string]: string};
+getUnion: (chooseInt: ChooseInt, chooseFloat: ChooseFloat, chooseObject: ChooseObject, chooseString: ChooseString) => ChooseObject;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModuleCxx');
`;
const PROMISE_WITH_COMMONLY_USED_TYPES = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type Season = 'Spring' | 'Summer' | 'Autumn' | 'Winter';
export type CustomObject = {|
field1: Array<Object>,
field2: boolean,
field3: string,
type: 'A_String_Literal',
|};
export interface Spec extends TurboModule {
returnStringArray(): Promise<Array<string>>;
returnObjectArray(): Promise<Array<Object>>;
returnNullableNumber(): Promise<number | null>;
returnEmpty(): Promise<empty>;
returnUnsupportedIndex(): Promise<{ [string]: 'authorized' | 'denied' | 'undetermined' | true | false }>;
returnSupportedIndex(): Promise<{ [string]: CustomObject }>;
returnEnum() : Promise<Season>;
returnObject() : Promise<CustomObject>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
module.exports = {
NATIVE_MODULE_WITH_OBJECT_WITH_OBJECT_DEFINED_IN_FILE_AS_PROPERTY,
NATIVE_MODULE_WITH_ARRAY_WITH_UNION_AND_TOUPLE,
NATIVE_MODULE_WITH_FLOAT_AND_INT32,
NATIVE_MODULE_WITH_ALIASES,
NATIVE_MODULE_WITH_NESTED_ALIASES,
NATIVE_MODULE_WITH_PROMISE,
NATIVE_MODULE_WITH_COMPLEX_OBJECTS,
NATIVE_MODULE_WITH_COMPLEX_OBJECTS_WITH_NULLABLE_KEY,
NATIVE_MODULE_WITH_SIMPLE_OBJECT,
NATIVE_MODULE_WITH_UNSAFE_OBJECT,
NATIVE_MODULE_WITH_PARTIALS,
NATIVE_MODULE_WITH_PARTIALS_COMPLEX,
NATIVE_MODULE_WITH_ROOT_TAG,
NATIVE_MODULE_WITH_NULLABLE_PARAM,
NATIVE_MODULE_WITH_BASIC_ARRAY,
NATIVE_MODULE_WITH_COMPLEX_ARRAY,
NATIVE_MODULE_WITH_ARRAY_WITH_ALIAS,
NATIVE_MODULE_WITH_BASIC_PARAM_TYPES,
NATIVE_MODULE_WITH_CALLBACK,
NATIVE_MODULE_WITH_UNION,
EMPTY_NATIVE_MODULE,
ANDROID_ONLY_NATIVE_MODULE,
IOS_ONLY_NATIVE_MODULE,
CXX_ONLY_NATIVE_MODULE,
PROMISE_WITH_COMMONLY_USED_TYPES,
};

View File

@@ -0,0 +1,825 @@
/**
* 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
*/
// @licenselint-loose-mode
'use strict';
const EMPTY_NATIVE_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
// no methods
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_COMPLEX_OBJECTS = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type String = string;
export interface Spec extends TurboModule {
// Exported methods.
+getObject: (arg: {|const1: {|const1: boolean|}|}) => {|
const1: {|const1: boolean|},
|};
+getReadOnlyObject: (arg: $ReadOnly<{|const1: $ReadOnly<{|const1: boolean|}>|}>) => $ReadOnly<{|
const1: {|const1: boolean|},
|}>;
+getObject2: (arg: { a: String }) => Object;
+getObjectInArray: (arg: {const1: {|const1: boolean|}}) => Array<{|
const1: {const1: boolean},
|}>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_COMPLEX_OBJECTS_WITH_NULLABLE_KEY = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getConstants: () => {|
isTesting: boolean,
reactNativeVersion: {|
major: number,
minor: number,
patch?: number,
prerelease: ?number,
|},
forceTouchAvailable: boolean,
osVersion: string,
systemName: string,
interfaceIdiom: string,
|};
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_BASIC_PARAM_TYPES = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+passBool?: (arg: boolean) => void;
+passNumber: (arg: number) => void;
+passString: (arg: string) => void;
+passStringish: (arg: Stringish) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ALIASES = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
type NumNum = number;
export type Num = (arg: NumNum) => void;
type Num2 = Num;
export type Void = void;
export type A = number;
export type B = number;
export type ObjectAlias = {|
x: number,
y: number,
label: string,
truthy: boolean,
|};
export type ReadOnlyAlias = $ReadOnly<ObjectAlias>;
export interface Spec extends TurboModule {
// Exported methods.
+getNumber: Num2;
+getVoid: () => Void;
+getArray: (a: Array<A>) => {| a: B |};
+getStringFromAlias: (a: ObjectAlias) => string;
+getStringFromNullableAlias: (a: ?ObjectAlias) => string;
+getStringFromReadOnlyAlias: (a: ReadOnlyAlias) => string;
+getStringFromNullableReadOnlyAlias: (a: ?ReadOnlyAlias) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_NESTED_ALIASES = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
type Bar = {|
z: number
|};
type Foo = {|
bar1: Bar,
bar2: Bar,
|};
export interface Spec extends TurboModule {
// Exported methods.
foo1: (x: Foo) => Foo;
foo2: (x: Foo) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_FLOAT_AND_INT32 = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
import type {Int32, Float} from 'react-native/Libraries/Types/CodegenTypes';
export interface Spec extends TurboModule {
+getInt: (arg: Int32) => Int32;
+getFloat: (arg: Float) => Float;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_SIMPLE_OBJECT = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getObject: (o: Object) => Object,
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_UNSAFE_OBJECT = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
import type {UnsafeObject} from 'react-native/Libraries/Types/CodegenTypes';
export interface Spec extends TurboModule {
+getUnsafeObject: (o: UnsafeObject) => UnsafeObject,
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_PARTIALS = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type SomeObj = {|
a: string,
b?: boolean,
|};
export interface Spec extends TurboModule {
+getSomeObj: () => SomeObj;
+getPartialSomeObj: () => Partial<SomeObj>;
+getSomeObjFromPartialSomeObj: (value: Partial<SomeObj>) => SomeObj;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_PARTIALS_COMPLEX = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type SomeObj = {|
a: string,
b?: boolean,
|};
export type PartialSomeObj = Partial<SomeObj>;
export interface Spec extends TurboModule {
+getPartialPartial: (value1: Partial<SomeObj>, value2: PartialSomeObj) => SomeObj
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ROOT_TAG = `
/**
* 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 {RootTag, TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getRootTag: (rootTag: RootTag) => RootTag,
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_NULLABLE_PARAM = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
// Exported methods.
+voidFunc: (arg: ?string) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_BASIC_ARRAY = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getArray: (arg: Array<string>) => Array<string>;
+getArray: (arg: $ReadOnlyArray<string>) => $ReadOnlyArray<string>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_OBJECT_WITH_OBJECT_DEFINED_IN_FILE_AS_PROPERTY = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
type DisplayMetricsAndroid = {|
width: number,
|};
export interface Spec extends TurboModule {
+getConstants: () => {|
+Dimensions: {
windowPhysicalPixels: DisplayMetricsAndroid,
},
|};
+getConstants2: () => $ReadOnly<{|
+Dimensions: {
windowPhysicalPixels: DisplayMetricsAndroid,
},
|}>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ARRAY_WITH_UNION_AND_TOUPLE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getArray: (arg: Array<[string, string]>) => Array<string | number | boolean>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ARRAY_WITH_ALIAS = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type SomeString = string;
export interface Spec extends TurboModule {
+getArray: (arg: Array<SomeString>) => Array<string>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_COMPLEX_ARRAY = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getArray: (arg: Array<Array<Array<Array<Array<string>>>>>) => Array<Array<Array<string>>>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_PROMISE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type String = string;
export type SomeObj = {| a: string |};
export interface Spec extends TurboModule {
+getValueWithPromise: () => Promise<string>;
+getValueWithPromiseDefinedSomewhereElse: () => Promise<String>;
+getValueWithPromiseObjDefinedSomewhereElse: () => Promise<SomeObj>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_CALLBACK = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
// Exported methods.
+getValueWithCallback: (
callback: (value: string, arr: Array<Array<string>>) => void,
) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_UNION = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type ChooseInt = 1 | 2 | 3;
export type ChooseFloat = 1.44 | 2.88 | 5.76;
export type ChooseObject = {} | {low: string};
export type ChooseString = 'One' | 'Two' | 'Three';
export interface Spec extends TurboModule {
+getUnion: (chooseInt: ChooseInt, chooseFloat: ChooseFloat, chooseObject: ChooseObject, chooseString: ChooseString) => ChooseObject;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const ANDROID_ONLY_NATIVE_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
// no methods
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModuleAndroid');
`;
const IOS_ONLY_NATIVE_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export enum Quality {
SD,
HD,
}
export enum Resolution {
Low = 720,
High = 1080,
}
export enum Floppy {
LowDensity = 0.72,
HighDensity = 1.44,
}
export enum StringOptions {
One = 'one',
Two = 'two',
Three = 'three',
}
export interface Spec extends TurboModule {
getEnums(quality: Quality, resolution?: Resolution, floppy: Floppy, stringOptions: StringOptions): string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModuleIOS');
`;
const CXX_ONLY_NATIVE_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type ChooseInt = 1 | 2 | 3;
export type ChooseFloat = 1.44 | 2.88 | 5.76;
export type ChooseObject = {} | {low: string};
export type ChooseString = 'One' | 'Two' | 'Three';
export enum Quality {
SD,
HD,
}
export enum Resolution {
Low = 720,
High = 1080,
}
export enum Floppy {
LowDensity = 0.72,
HighDensity = 1.44,
}
export enum StringOptions {
One = 'one',
Two = 'two',
Three = 'three',
}
export type BinaryTreeNode = {
left?: BinaryTreeNode,
value: number,
right?: BinaryTreeNode,
};
export type GraphNode = {
label: string,
neighbors?: Array<GraphNode>,
};
export interface Spec extends TurboModule {
+getCallback: () => () => void;
+getMixed: (arg: mixed) => mixed;
+getEnums: (quality: Quality, resolution?: Resolution, floppy: Floppy, stringOptions: StringOptions) => string;
+getBinaryTreeNode: (arg: BinaryTreeNode) => BinaryTreeNode;
+getGraphNode: (arg: GraphNode) => GraphNode;
+getMap: (arg: {[a: string]: ?number}) => {[b: string]: ?number};
+getAnotherMap: (arg: {[string]: string}) => {[string]: string};
+getUnion: (chooseInt: ChooseInt, chooseFloat: ChooseFloat, chooseObject: ChooseObject, chooseString: ChooseString) => ChooseObject;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModuleCxx');
`;
const PROMISE_WITH_COMMONLY_USED_TYPES = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type Season = 'Spring' | 'Summer' | 'Autumn' | 'Winter';
export type CustomObject = {|
field1: Array<Object>,
field2: boolean,
field3: string,
type: 'A_String_Literal',
|};
export interface Spec extends TurboModule {
returnStringArray(): Promise<Array<string>>;
returnObjectArray(): Promise<Array<Object>>;
returnNullableNumber(): Promise<number | null>;
returnEmpty(): Promise<empty>;
returnUnsupportedIndex(): Promise<{ [string]: 'authorized' | 'denied' | 'undetermined' | true | false }>;
returnSupportedIndex(): Promise<{ [string]: CustomObject }>;
returnEnum() : Promise<Season>;
returnObject() : Promise<CustomObject>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
module.exports = {
NATIVE_MODULE_WITH_OBJECT_WITH_OBJECT_DEFINED_IN_FILE_AS_PROPERTY,
NATIVE_MODULE_WITH_ARRAY_WITH_UNION_AND_TOUPLE,
NATIVE_MODULE_WITH_FLOAT_AND_INT32,
NATIVE_MODULE_WITH_ALIASES,
NATIVE_MODULE_WITH_NESTED_ALIASES,
NATIVE_MODULE_WITH_PROMISE,
NATIVE_MODULE_WITH_COMPLEX_OBJECTS,
NATIVE_MODULE_WITH_COMPLEX_OBJECTS_WITH_NULLABLE_KEY,
NATIVE_MODULE_WITH_SIMPLE_OBJECT,
NATIVE_MODULE_WITH_UNSAFE_OBJECT,
NATIVE_MODULE_WITH_PARTIALS,
NATIVE_MODULE_WITH_PARTIALS_COMPLEX,
NATIVE_MODULE_WITH_ROOT_TAG,
NATIVE_MODULE_WITH_NULLABLE_PARAM,
NATIVE_MODULE_WITH_BASIC_ARRAY,
NATIVE_MODULE_WITH_COMPLEX_ARRAY,
NATIVE_MODULE_WITH_ARRAY_WITH_ALIAS,
NATIVE_MODULE_WITH_BASIC_PARAM_TYPES,
NATIVE_MODULE_WITH_CALLBACK,
NATIVE_MODULE_WITH_UNION,
EMPTY_NATIVE_MODULE,
ANDROID_ONLY_NATIVE_MODULE,
IOS_ONLY_NATIVE_MODULE,
CXX_ONLY_NATIVE_MODULE,
PROMISE_WITH_COMMONLY_USED_TYPES,
};

View File

@@ -0,0 +1,317 @@
/**
* 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 _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(arr, i) {
var _i =
null == arr
? null
: ('undefined' != typeof Symbol && arr[Symbol.iterator]) ||
arr['@@iterator'];
if (null != _i) {
var _s,
_e,
_x,
_r,
_arr = [],
_n = !0,
_d = !1;
try {
if (((_x = (_i = _i.call(arr)).next), 0 === i)) {
if (Object(_i) !== _i) return;
_n = !1;
} else
for (
;
!(_n = (_s = _x.call(_i)).done) &&
(_arr.push(_s.value), _arr.length !== i);
_n = !0
);
} catch (err) {
(_d = !0), (_e = err);
} finally {
try {
if (!_n && null != _i.return && ((_r = _i.return()), Object(_r) !== _r))
return;
} finally {
if (_d) throw _e;
}
}
return _arr;
}
}
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
const _require = require('../../errors'),
UnsupportedGenericParserError = _require.UnsupportedGenericParserError,
UnsupportedTypeAnnotationParserError =
_require.UnsupportedTypeAnnotationParserError;
const _require2 = require('../../parsers-commons'),
assertGenericTypeAnnotationHasExactlyOneTypeParameter =
_require2.assertGenericTypeAnnotationHasExactlyOneTypeParameter,
parseObjectProperty = _require2.parseObjectProperty,
unwrapNullable = _require2.unwrapNullable,
wrapNullable = _require2.wrapNullable;
const _require3 = require('../../parsers-primitives'),
emitArrayType = _require3.emitArrayType,
emitCommonTypes = _require3.emitCommonTypes,
emitDictionary = _require3.emitDictionary,
emitFunction = _require3.emitFunction,
emitPromise = _require3.emitPromise,
emitRootTag = _require3.emitRootTag,
emitUnion = _require3.emitUnion,
typeAliasResolution = _require3.typeAliasResolution,
typeEnumResolution = _require3.typeEnumResolution;
function translateTypeAnnotation(
hasteModuleName,
/**
* TODO(T71778680): Flow-type this node.
*/
flowTypeAnnotation,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
) {
const resolveTypeAnnotationFN = parser.getResolveTypeAnnotationFN();
const _resolveTypeAnnotatio = resolveTypeAnnotationFN(
flowTypeAnnotation,
types,
parser,
),
nullable = _resolveTypeAnnotatio.nullable,
typeAnnotation = _resolveTypeAnnotatio.typeAnnotation,
typeResolutionStatus = _resolveTypeAnnotatio.typeResolutionStatus;
switch (typeAnnotation.type) {
case 'GenericTypeAnnotation': {
switch (parser.getTypeAnnotationName(typeAnnotation)) {
case 'RootTag': {
return emitRootTag(nullable);
}
case 'Promise': {
return emitPromise(
hasteModuleName,
typeAnnotation,
parser,
nullable,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
translateTypeAnnotation,
);
}
case 'Array':
case '$ReadOnlyArray': {
return emitArrayType(
hasteModuleName,
typeAnnotation,
parser,
types,
aliasMap,
enumMap,
cxxOnly,
nullable,
translateTypeAnnotation,
);
}
case '$ReadOnly': {
assertGenericTypeAnnotationHasExactlyOneTypeParameter(
hasteModuleName,
typeAnnotation,
parser,
);
const _unwrapNullable = unwrapNullable(
translateTypeAnnotation(
hasteModuleName,
typeAnnotation.typeParameters.params[0],
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
),
),
_unwrapNullable2 = _slicedToArray(_unwrapNullable, 2),
paramType = _unwrapNullable2[0],
isParamNullable = _unwrapNullable2[1];
return wrapNullable(nullable || isParamNullable, paramType);
}
default: {
const commonType = emitCommonTypes(
hasteModuleName,
types,
typeAnnotation,
aliasMap,
enumMap,
tryParse,
cxxOnly,
nullable,
parser,
);
if (!commonType) {
throw new UnsupportedGenericParserError(
hasteModuleName,
typeAnnotation,
parser,
);
}
return commonType;
}
}
}
case 'ObjectTypeAnnotation': {
// if there is any indexer, then it is a dictionary
if (typeAnnotation.indexers) {
const indexers = typeAnnotation.indexers.filter(
member => member.type === 'ObjectTypeIndexer',
);
if (indexers.length > 0) {
// check the property type to prevent developers from using unsupported types
// the return value from `translateTypeAnnotation` is unused
const propertyType = indexers[0].value;
const valueType = translateTypeAnnotation(
hasteModuleName,
propertyType,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
);
// no need to do further checking
return emitDictionary(nullable, valueType);
}
}
const objectTypeAnnotation = {
type: 'ObjectTypeAnnotation',
// $FlowFixMe[missing-type-arg]
properties: [...typeAnnotation.properties, ...typeAnnotation.indexers]
.map(property => {
return tryParse(() => {
return parseObjectProperty(
flowTypeAnnotation,
property,
hasteModuleName,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
nullable,
translateTypeAnnotation,
parser,
);
});
})
.filter(Boolean),
};
return typeAliasResolution(
typeResolutionStatus,
objectTypeAnnotation,
aliasMap,
nullable,
);
}
case 'FunctionTypeAnnotation': {
return emitFunction(
nullable,
hasteModuleName,
typeAnnotation,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
translateTypeAnnotation,
parser,
);
}
case 'UnionTypeAnnotation': {
return emitUnion(nullable, hasteModuleName, typeAnnotation, parser);
}
case 'StringLiteralTypeAnnotation': {
// 'a' is a special case for 'a' | 'b' but the type name is different
return wrapNullable(nullable, {
type: 'UnionTypeAnnotation',
memberType: 'StringTypeAnnotation',
});
}
case 'EnumStringBody':
case 'EnumNumberBody': {
return typeEnumResolution(
typeAnnotation,
typeResolutionStatus,
nullable,
hasteModuleName,
enumMap,
parser,
);
}
default: {
const commonType = emitCommonTypes(
hasteModuleName,
types,
typeAnnotation,
aliasMap,
enumMap,
tryParse,
cxxOnly,
nullable,
parser,
);
if (!commonType) {
throw new UnsupportedTypeAnnotationParserError(
hasteModuleName,
typeAnnotation,
parser.language(),
);
}
return commonType;
}
}
}
module.exports = {
flowTranslateTypeAnnotation: translateTypeAnnotation,
};

View File

@@ -0,0 +1,266 @@
/**
* 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 {
NamedShape,
NativeModuleAliasMap,
NativeModuleBaseTypeAnnotation,
NativeModuleEnumMap,
NativeModuleTypeAnnotation,
Nullable,
} from '../../../CodegenSchema';
import type {Parser} from '../../parser';
import type {ParserErrorCapturer, TypeDeclarationMap} from '../../utils';
const {
UnsupportedGenericParserError,
UnsupportedTypeAnnotationParserError,
} = require('../../errors');
const {
assertGenericTypeAnnotationHasExactlyOneTypeParameter,
parseObjectProperty,
unwrapNullable,
wrapNullable,
} = require('../../parsers-commons');
const {
emitArrayType,
emitCommonTypes,
emitDictionary,
emitFunction,
emitPromise,
emitRootTag,
emitUnion,
typeAliasResolution,
typeEnumResolution,
} = require('../../parsers-primitives');
function translateTypeAnnotation(
hasteModuleName: string,
/**
* TODO(T71778680): Flow-type this node.
*/
flowTypeAnnotation: $FlowFixMe,
types: TypeDeclarationMap,
aliasMap: {...NativeModuleAliasMap},
enumMap: {...NativeModuleEnumMap},
tryParse: ParserErrorCapturer,
cxxOnly: boolean,
parser: Parser,
): Nullable<NativeModuleTypeAnnotation> {
const resolveTypeAnnotationFN = parser.getResolveTypeAnnotationFN();
const {nullable, typeAnnotation, typeResolutionStatus} =
resolveTypeAnnotationFN(flowTypeAnnotation, types, parser);
switch (typeAnnotation.type) {
case 'GenericTypeAnnotation': {
switch (parser.getTypeAnnotationName(typeAnnotation)) {
case 'RootTag': {
return emitRootTag(nullable);
}
case 'Promise': {
return emitPromise(
hasteModuleName,
typeAnnotation,
parser,
nullable,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
translateTypeAnnotation,
);
}
case 'Array':
case '$ReadOnlyArray': {
return emitArrayType(
hasteModuleName,
typeAnnotation,
parser,
types,
aliasMap,
enumMap,
cxxOnly,
nullable,
translateTypeAnnotation,
);
}
case '$ReadOnly': {
assertGenericTypeAnnotationHasExactlyOneTypeParameter(
hasteModuleName,
typeAnnotation,
parser,
);
const [paramType, isParamNullable] = unwrapNullable(
translateTypeAnnotation(
hasteModuleName,
typeAnnotation.typeParameters.params[0],
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
),
);
return wrapNullable(nullable || isParamNullable, paramType);
}
default: {
const commonType = emitCommonTypes(
hasteModuleName,
types,
typeAnnotation,
aliasMap,
enumMap,
tryParse,
cxxOnly,
nullable,
parser,
);
if (!commonType) {
throw new UnsupportedGenericParserError(
hasteModuleName,
typeAnnotation,
parser,
);
}
return commonType;
}
}
}
case 'ObjectTypeAnnotation': {
// if there is any indexer, then it is a dictionary
if (typeAnnotation.indexers) {
const indexers = typeAnnotation.indexers.filter(
member => member.type === 'ObjectTypeIndexer',
);
if (indexers.length > 0) {
// check the property type to prevent developers from using unsupported types
// the return value from `translateTypeAnnotation` is unused
const propertyType = indexers[0].value;
const valueType = translateTypeAnnotation(
hasteModuleName,
propertyType,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
);
// no need to do further checking
return emitDictionary(nullable, valueType);
}
}
const objectTypeAnnotation = {
type: 'ObjectTypeAnnotation',
// $FlowFixMe[missing-type-arg]
properties: ([
...typeAnnotation.properties,
...typeAnnotation.indexers,
]: Array<$FlowFixMe>)
.map<?NamedShape<Nullable<NativeModuleBaseTypeAnnotation>>>(
property => {
return tryParse(() => {
return parseObjectProperty(
flowTypeAnnotation,
property,
hasteModuleName,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
nullable,
translateTypeAnnotation,
parser,
);
});
},
)
.filter(Boolean),
};
return typeAliasResolution(
typeResolutionStatus,
objectTypeAnnotation,
aliasMap,
nullable,
);
}
case 'FunctionTypeAnnotation': {
return emitFunction(
nullable,
hasteModuleName,
typeAnnotation,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
translateTypeAnnotation,
parser,
);
}
case 'UnionTypeAnnotation': {
return emitUnion(nullable, hasteModuleName, typeAnnotation, parser);
}
case 'StringLiteralTypeAnnotation': {
// 'a' is a special case for 'a' | 'b' but the type name is different
return wrapNullable(nullable, {
type: 'UnionTypeAnnotation',
memberType: 'StringTypeAnnotation',
});
}
case 'EnumStringBody':
case 'EnumNumberBody': {
return typeEnumResolution(
typeAnnotation,
typeResolutionStatus,
nullable,
hasteModuleName,
enumMap,
parser,
);
}
default: {
const commonType = emitCommonTypes(
hasteModuleName,
types,
typeAnnotation,
aliasMap,
enumMap,
tryParse,
cxxOnly,
nullable,
parser,
);
if (!commonType) {
throw new UnsupportedTypeAnnotationParserError(
hasteModuleName,
typeAnnotation,
parser.language(),
);
}
return commonType;
}
}
}
module.exports = {
flowTranslateTypeAnnotation: translateTypeAnnotation,
};

View File

@@ -0,0 +1,105 @@
/**
* 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(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
enumerableOnly &&
(symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
})),
keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread(target) {
for (var i = 1; i < arguments.length; i++) {
var source = null != arguments[i] ? arguments[i] : {};
i % 2
? ownKeys(Object(source), !0).forEach(function (key) {
_defineProperty(target, key, source[key]);
})
: Object.getOwnPropertyDescriptors
? Object.defineProperties(
target,
Object.getOwnPropertyDescriptors(source),
)
: ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(
target,
key,
Object.getOwnPropertyDescriptor(source, key),
);
});
}
return target;
}
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);
}
const hermesParser = require('hermes-parser');
function parseFlowAndThrowErrors(code, options = {}) {
let ast;
try {
ast = hermesParser.parse(
code,
_objectSpread(
{
// Produce an ESTree-compliant AST
babel: false,
// Parse Flow without a pragma
flow: 'all',
},
options.filename != null
? {
sourceFilename: options.filename,
}
: {},
),
);
} catch (e) {
if (options.filename != null) {
e.message = `Syntax error in ${options.filename}: ${e.message}`;
}
throw e;
}
return ast;
}
module.exports = {
parseFlowAndThrowErrors,
};

View File

@@ -0,0 +1,41 @@
/**
* 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 {Program as ESTreeProgram} from 'hermes-estree';
const hermesParser = require('hermes-parser');
function parseFlowAndThrowErrors(
code: string,
options: $ReadOnly<{filename?: ?string}> = {},
): ESTreeProgram {
let ast;
try {
ast = hermesParser.parse(code, {
// Produce an ESTree-compliant AST
babel: false,
// Parse Flow without a pragma
flow: 'all',
...(options.filename != null ? {sourceFilename: options.filename} : {}),
});
} catch (e) {
if (options.filename != null) {
e.message = `Syntax error in ${options.filename}: ${e.message}`;
}
throw e;
}
return ast;
}
module.exports = {
parseFlowAndThrowErrors,
};

View File

@@ -0,0 +1,17 @@
/**
* 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.
*/
import type { Parser } from '../parser';
import type { SchemaType } from '../../CodegenSchema';
import type { ParserType } from '../errors';
export declare class FlowParser implements Parser {
language(): ParserType;
parseFile(filename: string): SchemaType;
parseString(contents: string, filename?: string): SchemaType;
parseModuleFixture(filename: string): SchemaType;
}

View File

@@ -0,0 +1,481 @@
/**
* 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 _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);
}
const _require = require('../errors'),
UnsupportedObjectPropertyTypeAnnotationParserError =
_require.UnsupportedObjectPropertyTypeAnnotationParserError;
const _require2 = require('../parsers-commons'),
buildModuleSchema = _require2.buildModuleSchema,
buildPropSchema = _require2.buildPropSchema,
buildSchema = _require2.buildSchema,
handleGenericTypeAnnotation = _require2.handleGenericTypeAnnotation;
const _require3 = require('../parsers-primitives'),
Visitor = _require3.Visitor;
const _require4 = require('../schema.js'),
wrapComponentSchema = _require4.wrapComponentSchema;
const _require5 = require('./components'),
buildComponentSchema = _require5.buildComponentSchema;
const _require6 = require('./components/componentsUtils'),
flattenProperties = _require6.flattenProperties,
getSchemaInfo = _require6.getSchemaInfo,
getTypeAnnotation = _require6.getTypeAnnotation;
const _require7 = require('./modules'),
flowTranslateTypeAnnotation = _require7.flowTranslateTypeAnnotation;
const _require8 = require('./parseFlowAndThrowErrors'),
parseFlowAndThrowErrors = _require8.parseFlowAndThrowErrors;
const fs = require('fs');
const invariant = require('invariant');
class FlowParser {
constructor() {
_defineProperty(
this,
'typeParameterInstantiation',
'TypeParameterInstantiation',
);
_defineProperty(this, 'typeAlias', 'TypeAlias');
_defineProperty(this, 'enumDeclaration', 'EnumDeclaration');
_defineProperty(this, 'interfaceDeclaration', 'InterfaceDeclaration');
_defineProperty(
this,
'nullLiteralTypeAnnotation',
'NullLiteralTypeAnnotation',
);
_defineProperty(
this,
'undefinedLiteralTypeAnnotation',
'VoidLiteralTypeAnnotation',
);
}
isProperty(property) {
return property.type === 'ObjectTypeProperty';
}
getKeyName(property, hasteModuleName) {
if (!this.isProperty(property)) {
throw new UnsupportedObjectPropertyTypeAnnotationParserError(
hasteModuleName,
property,
property.type,
this.language(),
);
}
return property.key.name;
}
language() {
return 'Flow';
}
getTypeAnnotationName(typeAnnotation) {
var _typeAnnotation$id;
return typeAnnotation === null || typeAnnotation === void 0
? void 0
: (_typeAnnotation$id = typeAnnotation.id) === null ||
_typeAnnotation$id === void 0
? void 0
: _typeAnnotation$id.name;
}
checkIfInvalidModule(typeArguments) {
return (
typeArguments.type !== 'TypeParameterInstantiation' ||
typeArguments.params.length !== 1 ||
typeArguments.params[0].type !== 'GenericTypeAnnotation' ||
typeArguments.params[0].id.name !== 'Spec'
);
}
remapUnionTypeAnnotationMemberNames(membersTypes) {
const remapLiteral = item => {
return item.type
.replace('NumberLiteralTypeAnnotation', 'NumberTypeAnnotation')
.replace('StringLiteralTypeAnnotation', 'StringTypeAnnotation');
};
return [...new Set(membersTypes.map(remapLiteral))];
}
parseFile(filename) {
const contents = fs.readFileSync(filename, 'utf8');
return this.parseString(contents, filename);
}
parseString(contents, filename) {
return buildSchema(
contents,
filename,
wrapComponentSchema,
buildComponentSchema,
buildModuleSchema,
Visitor,
this,
flowTranslateTypeAnnotation,
);
}
parseModuleFixture(filename) {
const contents = fs.readFileSync(filename, 'utf8');
return this.parseString(contents, 'path/NativeSampleTurboModule.js');
}
getAst(contents, filename) {
return parseFlowAndThrowErrors(contents, {
filename,
});
}
getFunctionTypeAnnotationParameters(functionTypeAnnotation) {
return functionTypeAnnotation.params;
}
getFunctionNameFromParameter(parameter) {
return parameter.name;
}
getParameterName(parameter) {
return parameter.name.name;
}
getParameterTypeAnnotation(parameter) {
return parameter.typeAnnotation;
}
getFunctionTypeAnnotationReturnType(functionTypeAnnotation) {
return functionTypeAnnotation.returnType;
}
parseEnumMembersType(typeAnnotation) {
const enumMembersType =
typeAnnotation.type === 'EnumStringBody'
? 'StringTypeAnnotation'
: typeAnnotation.type === 'EnumNumberBody'
? 'NumberTypeAnnotation'
: null;
if (!enumMembersType) {
throw new Error(
`Unknown enum type annotation type. Got: ${typeAnnotation.type}. Expected: EnumStringBody or EnumNumberBody.`,
);
}
return enumMembersType;
}
validateEnumMembersSupported(typeAnnotation, enumMembersType) {
if (!typeAnnotation.members || typeAnnotation.members.length === 0) {
// passing mixed members to flow would result in a flow error
// if the tool is launched ignoring that error, the enum would appear like not having enums
throw new Error(
'Enums should have at least one member and member values can not be mixed- they all must be either blank, number, or string values.',
);
}
typeAnnotation.members.forEach(member => {
if (
enumMembersType === 'StringTypeAnnotation' &&
(!member.init || typeof member.init.value === 'string')
) {
return;
}
if (
enumMembersType === 'NumberTypeAnnotation' &&
member.init &&
typeof member.init.value === 'number'
) {
return;
}
throw new Error(
'Enums can not be mixed- they all must be either blank, number, or string values.',
);
});
}
parseEnumMembers(typeAnnotation) {
return typeAnnotation.members.map(member => {
var _member$init$value, _member$init;
return {
name: member.id.name,
value:
(_member$init$value =
(_member$init = member.init) === null || _member$init === void 0
? void 0
: _member$init.value) !== null && _member$init$value !== void 0
? _member$init$value
: member.id.name,
};
});
}
isModuleInterface(node) {
return (
node.type === 'InterfaceDeclaration' &&
node.extends.length === 1 &&
node.extends[0].type === 'InterfaceExtends' &&
node.extends[0].id.name === 'TurboModule'
);
}
isGenericTypeAnnotation(type) {
return type === 'GenericTypeAnnotation';
}
extractAnnotatedElement(typeAnnotation, types) {
return types[typeAnnotation.typeParameters.params[0].id.name];
}
/**
* This FlowFixMe is supposed to refer to an InterfaceDeclaration or TypeAlias
* declaration type. Unfortunately, we don't have those types, because flow-parser
* generates them, and flow-parser is not type-safe. In the future, we should find
* a way to get these types from our flow parser library.
*
* TODO(T71778680): Flow type AST Nodes
*/
getTypes(ast) {
return ast.body.reduce((types, node) => {
if (
node.type === 'ExportNamedDeclaration' &&
node.exportKind === 'type'
) {
if (
node.declaration != null &&
(node.declaration.type === 'TypeAlias' ||
node.declaration.type === 'InterfaceDeclaration')
) {
types[node.declaration.id.name] = node.declaration;
}
} else if (
node.type === 'ExportNamedDeclaration' &&
node.exportKind === 'value' &&
node.declaration &&
node.declaration.type === 'EnumDeclaration'
) {
types[node.declaration.id.name] = node.declaration;
} else if (
node.type === 'TypeAlias' ||
node.type === 'InterfaceDeclaration' ||
node.type === 'EnumDeclaration'
) {
types[node.id.name] = node;
}
return types;
}, {});
}
callExpressionTypeParameters(callExpression) {
return callExpression.typeArguments || null;
}
computePartialProperties(
properties,
hasteModuleName,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
) {
return properties.map(prop => {
return {
name: prop.key.name,
optional: true,
typeAnnotation: flowTranslateTypeAnnotation(
hasteModuleName,
prop.value,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
this,
),
};
});
}
functionTypeAnnotation(propertyValueType) {
return propertyValueType === 'FunctionTypeAnnotation';
}
getTypeArgumentParamsFromDeclaration(declaration) {
return declaration.typeArguments.params;
}
/**
* This FlowFixMe is supposed to refer to typeArgumentParams and
* funcArgumentParams of generated AST.
*/
getNativeComponentType(typeArgumentParams, funcArgumentParams) {
return {
propsTypeName: typeArgumentParams[0].id.name,
componentName: funcArgumentParams[0].value,
};
}
getAnnotatedElementProperties(annotatedElement) {
return annotatedElement.right.properties;
}
bodyProperties(typeAlias) {
return typeAlias.body.properties;
}
convertKeywordToTypeAnnotation(keyword) {
return keyword;
}
argumentForProp(prop) {
return prop.argument;
}
nameForArgument(prop) {
return prop.argument.id.name;
}
isOptionalProperty(property) {
return (
property.value.type === 'NullableTypeAnnotation' || property.optional
);
}
getGetSchemaInfoFN() {
return getSchemaInfo;
}
getTypeAnnotationFromProperty(property) {
return property.value.type === 'NullableTypeAnnotation'
? property.value.typeAnnotation
: property.value;
}
getGetTypeAnnotationFN() {
return getTypeAnnotation;
}
getResolvedTypeAnnotation(typeAnnotation, types, parser) {
invariant(
typeAnnotation != null,
'resolveTypeAnnotation(): typeAnnotation cannot be null',
);
let node = typeAnnotation;
let nullable = false;
let typeResolutionStatus = {
successful: false,
};
for (;;) {
if (node.type === 'NullableTypeAnnotation') {
nullable = true;
node = node.typeAnnotation;
continue;
}
if (node.type !== 'GenericTypeAnnotation') {
break;
}
const typeAnnotationName = this.getTypeAnnotationName(node);
const resolvedTypeAnnotation = types[typeAnnotationName];
if (resolvedTypeAnnotation == null) {
break;
}
const _handleGenericTypeAnn = handleGenericTypeAnnotation(
node,
resolvedTypeAnnotation,
this,
),
typeAnnotationNode = _handleGenericTypeAnn.typeAnnotation,
status = _handleGenericTypeAnn.typeResolutionStatus;
typeResolutionStatus = status;
node = typeAnnotationNode;
}
return {
nullable: nullable,
typeAnnotation: node,
typeResolutionStatus,
};
}
getResolveTypeAnnotationFN() {
return (typeAnnotation, types, parser) =>
this.getResolvedTypeAnnotation(typeAnnotation, types, parser);
}
extendsForProp(prop, types, parser) {
const argument = this.argumentForProp(prop);
if (!argument) {
console.log('null', prop);
}
const name = parser.nameForArgument(prop);
if (types[name] != null) {
// This type is locally defined in the file
return null;
}
switch (name) {
case 'ViewProps':
return {
type: 'ReactNativeBuiltInType',
knownTypeName: 'ReactNativeCoreViewProps',
};
default: {
throw new Error(`Unable to handle prop spread: ${name}`);
}
}
}
removeKnownExtends(typeDefinition, types) {
return typeDefinition.filter(
prop =>
prop.type !== 'ObjectTypeSpreadProperty' ||
this.extendsForProp(prop, types, this) === null,
);
}
getExtendsProps(typeDefinition, types) {
return typeDefinition
.filter(prop => prop.type === 'ObjectTypeSpreadProperty')
.map(prop => this.extendsForProp(prop, types, this))
.filter(Boolean);
}
getProps(typeDefinition, types) {
const nonExtendsProps = this.removeKnownExtends(typeDefinition, types);
const props = flattenProperties(nonExtendsProps, types, this)
.map(property => buildPropSchema(property, types, this))
.filter(Boolean);
return {
props,
extendsProps: this.getExtendsProps(typeDefinition, types),
};
}
getProperties(typeName, types) {
const typeAlias = types[typeName];
try {
return typeAlias.right.typeParameters.params[0].properties;
} catch (e) {
throw new Error(
`Failed to find type definition for "${typeName}", please check that you have a valid codegen flow file`,
);
}
}
nextNodeForTypeAlias(typeAnnotation) {
return typeAnnotation.right;
}
nextNodeForEnum(typeAnnotation) {
return typeAnnotation.body;
}
genericTypeAnnotationErrorMessage(typeAnnotation) {
return `A non GenericTypeAnnotation must be a type declaration ('${this.typeAlias}') or enum ('${this.enumDeclaration}'). Instead, got the unsupported ${typeAnnotation.type}.`;
}
extractTypeFromTypeAnnotation(typeAnnotation) {
return typeAnnotation.type === 'GenericTypeAnnotation'
? typeAnnotation.id.name
: typeAnnotation.type;
}
getObjectProperties(typeAnnotation) {
return typeAnnotation.properties;
}
getLiteralValue(option) {
return option.value;
}
getPaperTopLevelNameDeprecated(typeAnnotation) {
return typeAnnotation.typeParameters.params.length > 1
? typeAnnotation.typeParameters.params[1].value
: null;
}
}
module.exports = {
FlowParser,
};

View File

@@ -0,0 +1,551 @@
/**
* 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 {
ExtendsPropsShape,
NamedShape,
NativeModuleAliasMap,
NativeModuleEnumMap,
NativeModuleEnumMembers,
NativeModuleEnumMemberType,
NativeModuleParamTypeAnnotation,
Nullable,
PropTypeAnnotation,
SchemaType,
UnionTypeAnnotationMemberType,
} from '../../CodegenSchema';
import type {ParserType} from '../errors';
import type {
GetSchemaInfoFN,
GetTypeAnnotationFN,
Parser,
ResolveTypeAnnotationFN,
} from '../parser';
import type {
ParserErrorCapturer,
PropAST,
TypeDeclarationMap,
TypeResolutionStatus,
} from '../utils';
const {
UnsupportedObjectPropertyTypeAnnotationParserError,
} = require('../errors');
const {
buildModuleSchema,
buildPropSchema,
buildSchema,
handleGenericTypeAnnotation,
} = require('../parsers-commons');
const {Visitor} = require('../parsers-primitives');
const {wrapComponentSchema} = require('../schema.js');
const {buildComponentSchema} = require('./components');
const {
flattenProperties,
getSchemaInfo,
getTypeAnnotation,
} = require('./components/componentsUtils');
const {flowTranslateTypeAnnotation} = require('./modules');
const {parseFlowAndThrowErrors} = require('./parseFlowAndThrowErrors');
const fs = require('fs');
const invariant = require('invariant');
type ExtendsForProp = null | {
type: 'ReactNativeBuiltInType',
knownTypeName: 'ReactNativeCoreViewProps',
};
class FlowParser implements Parser {
typeParameterInstantiation: string = 'TypeParameterInstantiation';
typeAlias: string = 'TypeAlias';
enumDeclaration: string = 'EnumDeclaration';
interfaceDeclaration: string = 'InterfaceDeclaration';
nullLiteralTypeAnnotation: string = 'NullLiteralTypeAnnotation';
undefinedLiteralTypeAnnotation: string = 'VoidLiteralTypeAnnotation';
isProperty(property: $FlowFixMe): boolean {
return property.type === 'ObjectTypeProperty';
}
getKeyName(property: $FlowFixMe, hasteModuleName: string): string {
if (!this.isProperty(property)) {
throw new UnsupportedObjectPropertyTypeAnnotationParserError(
hasteModuleName,
property,
property.type,
this.language(),
);
}
return property.key.name;
}
language(): ParserType {
return 'Flow';
}
getTypeAnnotationName(typeAnnotation: $FlowFixMe): string {
return typeAnnotation?.id?.name;
}
checkIfInvalidModule(typeArguments: $FlowFixMe): boolean {
return (
typeArguments.type !== 'TypeParameterInstantiation' ||
typeArguments.params.length !== 1 ||
typeArguments.params[0].type !== 'GenericTypeAnnotation' ||
typeArguments.params[0].id.name !== 'Spec'
);
}
remapUnionTypeAnnotationMemberNames(
membersTypes: $FlowFixMe[],
): UnionTypeAnnotationMemberType[] {
const remapLiteral = (item: $FlowFixMe) => {
return item.type
.replace('NumberLiteralTypeAnnotation', 'NumberTypeAnnotation')
.replace('StringLiteralTypeAnnotation', 'StringTypeAnnotation');
};
return [...new Set(membersTypes.map(remapLiteral))];
}
parseFile(filename: string): SchemaType {
const contents = fs.readFileSync(filename, 'utf8');
return this.parseString(contents, filename);
}
parseString(contents: string, filename: ?string): SchemaType {
return buildSchema(
contents,
filename,
wrapComponentSchema,
buildComponentSchema,
buildModuleSchema,
Visitor,
this,
flowTranslateTypeAnnotation,
);
}
parseModuleFixture(filename: string): SchemaType {
const contents = fs.readFileSync(filename, 'utf8');
return this.parseString(contents, 'path/NativeSampleTurboModule.js');
}
getAst(contents: string, filename?: ?string): $FlowFixMe {
return parseFlowAndThrowErrors(contents, {filename});
}
getFunctionTypeAnnotationParameters(
functionTypeAnnotation: $FlowFixMe,
): $ReadOnlyArray<$FlowFixMe> {
return functionTypeAnnotation.params;
}
getFunctionNameFromParameter(
parameter: NamedShape<Nullable<NativeModuleParamTypeAnnotation>>,
): $FlowFixMe {
return parameter.name;
}
getParameterName(parameter: $FlowFixMe): string {
return parameter.name.name;
}
getParameterTypeAnnotation(parameter: $FlowFixMe): $FlowFixMe {
return parameter.typeAnnotation;
}
getFunctionTypeAnnotationReturnType(
functionTypeAnnotation: $FlowFixMe,
): $FlowFixMe {
return functionTypeAnnotation.returnType;
}
parseEnumMembersType(typeAnnotation: $FlowFixMe): NativeModuleEnumMemberType {
const enumMembersType: ?NativeModuleEnumMemberType =
typeAnnotation.type === 'EnumStringBody'
? 'StringTypeAnnotation'
: typeAnnotation.type === 'EnumNumberBody'
? 'NumberTypeAnnotation'
: null;
if (!enumMembersType) {
throw new Error(
`Unknown enum type annotation type. Got: ${typeAnnotation.type}. Expected: EnumStringBody or EnumNumberBody.`,
);
}
return enumMembersType;
}
validateEnumMembersSupported(
typeAnnotation: $FlowFixMe,
enumMembersType: NativeModuleEnumMemberType,
): void {
if (!typeAnnotation.members || typeAnnotation.members.length === 0) {
// passing mixed members to flow would result in a flow error
// if the tool is launched ignoring that error, the enum would appear like not having enums
throw new Error(
'Enums should have at least one member and member values can not be mixed- they all must be either blank, number, or string values.',
);
}
typeAnnotation.members.forEach(member => {
if (
enumMembersType === 'StringTypeAnnotation' &&
(!member.init || typeof member.init.value === 'string')
) {
return;
}
if (
enumMembersType === 'NumberTypeAnnotation' &&
member.init &&
typeof member.init.value === 'number'
) {
return;
}
throw new Error(
'Enums can not be mixed- they all must be either blank, number, or string values.',
);
});
}
parseEnumMembers(typeAnnotation: $FlowFixMe): NativeModuleEnumMembers {
return typeAnnotation.members.map(member => ({
name: member.id.name,
value: member.init?.value ?? member.id.name,
}));
}
isModuleInterface(node: $FlowFixMe): boolean {
return (
node.type === 'InterfaceDeclaration' &&
node.extends.length === 1 &&
node.extends[0].type === 'InterfaceExtends' &&
node.extends[0].id.name === 'TurboModule'
);
}
isGenericTypeAnnotation(type: $FlowFixMe): boolean {
return type === 'GenericTypeAnnotation';
}
extractAnnotatedElement(
typeAnnotation: $FlowFixMe,
types: TypeDeclarationMap,
): $FlowFixMe {
return types[typeAnnotation.typeParameters.params[0].id.name];
}
/**
* This FlowFixMe is supposed to refer to an InterfaceDeclaration or TypeAlias
* declaration type. Unfortunately, we don't have those types, because flow-parser
* generates them, and flow-parser is not type-safe. In the future, we should find
* a way to get these types from our flow parser library.
*
* TODO(T71778680): Flow type AST Nodes
*/
getTypes(ast: $FlowFixMe): TypeDeclarationMap {
return ast.body.reduce((types, node) => {
if (
node.type === 'ExportNamedDeclaration' &&
node.exportKind === 'type'
) {
if (
node.declaration != null &&
(node.declaration.type === 'TypeAlias' ||
node.declaration.type === 'InterfaceDeclaration')
) {
types[node.declaration.id.name] = node.declaration;
}
} else if (
node.type === 'ExportNamedDeclaration' &&
node.exportKind === 'value' &&
node.declaration &&
node.declaration.type === 'EnumDeclaration'
) {
types[node.declaration.id.name] = node.declaration;
} else if (
node.type === 'TypeAlias' ||
node.type === 'InterfaceDeclaration' ||
node.type === 'EnumDeclaration'
) {
types[node.id.name] = node;
}
return types;
}, {});
}
callExpressionTypeParameters(callExpression: $FlowFixMe): $FlowFixMe | null {
return callExpression.typeArguments || null;
}
computePartialProperties(
properties: Array<$FlowFixMe>,
hasteModuleName: string,
types: TypeDeclarationMap,
aliasMap: {...NativeModuleAliasMap},
enumMap: {...NativeModuleEnumMap},
tryParse: ParserErrorCapturer,
cxxOnly: boolean,
): Array<$FlowFixMe> {
return properties.map(prop => {
return {
name: prop.key.name,
optional: true,
typeAnnotation: flowTranslateTypeAnnotation(
hasteModuleName,
prop.value,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
this,
),
};
});
}
functionTypeAnnotation(propertyValueType: string): boolean {
return propertyValueType === 'FunctionTypeAnnotation';
}
getTypeArgumentParamsFromDeclaration(declaration: $FlowFixMe): $FlowFixMe {
return declaration.typeArguments.params;
}
/**
* This FlowFixMe is supposed to refer to typeArgumentParams and
* funcArgumentParams of generated AST.
*/
getNativeComponentType(
typeArgumentParams: $FlowFixMe,
funcArgumentParams: $FlowFixMe,
): {[string]: string} {
return {
propsTypeName: typeArgumentParams[0].id.name,
componentName: funcArgumentParams[0].value,
};
}
getAnnotatedElementProperties(annotatedElement: $FlowFixMe): $FlowFixMe {
return annotatedElement.right.properties;
}
bodyProperties(typeAlias: $FlowFixMe): $ReadOnlyArray<$FlowFixMe> {
return typeAlias.body.properties;
}
convertKeywordToTypeAnnotation(keyword: string): string {
return keyword;
}
argumentForProp(prop: PropAST): $FlowFixMe {
return prop.argument;
}
nameForArgument(prop: PropAST): $FlowFixMe {
return prop.argument.id.name;
}
isOptionalProperty(property: $FlowFixMe): boolean {
return (
property.value.type === 'NullableTypeAnnotation' || property.optional
);
}
getGetSchemaInfoFN(): GetSchemaInfoFN {
return getSchemaInfo;
}
getTypeAnnotationFromProperty(property: PropAST): $FlowFixMe {
return property.value.type === 'NullableTypeAnnotation'
? property.value.typeAnnotation
: property.value;
}
getGetTypeAnnotationFN(): GetTypeAnnotationFN {
return getTypeAnnotation;
}
getResolvedTypeAnnotation(
typeAnnotation: $FlowFixMe,
types: TypeDeclarationMap,
parser: Parser,
): {
nullable: boolean,
typeAnnotation: $FlowFixMe,
typeResolutionStatus: TypeResolutionStatus,
} {
invariant(
typeAnnotation != null,
'resolveTypeAnnotation(): typeAnnotation cannot be null',
);
let node = typeAnnotation;
let nullable = false;
let typeResolutionStatus: TypeResolutionStatus = {
successful: false,
};
for (;;) {
if (node.type === 'NullableTypeAnnotation') {
nullable = true;
node = node.typeAnnotation;
continue;
}
if (node.type !== 'GenericTypeAnnotation') {
break;
}
const typeAnnotationName = this.getTypeAnnotationName(node);
const resolvedTypeAnnotation = types[typeAnnotationName];
if (resolvedTypeAnnotation == null) {
break;
}
const {typeAnnotation: typeAnnotationNode, typeResolutionStatus: status} =
handleGenericTypeAnnotation(node, resolvedTypeAnnotation, this);
typeResolutionStatus = status;
node = typeAnnotationNode;
}
return {
nullable: nullable,
typeAnnotation: node,
typeResolutionStatus,
};
}
getResolveTypeAnnotationFN(): ResolveTypeAnnotationFN {
return (typeAnnotation, types, parser) =>
this.getResolvedTypeAnnotation(typeAnnotation, types, parser);
}
extendsForProp(
prop: PropAST,
types: TypeDeclarationMap,
parser: Parser,
): ExtendsForProp {
const argument = this.argumentForProp(prop);
if (!argument) {
console.log('null', prop);
}
const name = parser.nameForArgument(prop);
if (types[name] != null) {
// This type is locally defined in the file
return null;
}
switch (name) {
case 'ViewProps':
return {
type: 'ReactNativeBuiltInType',
knownTypeName: 'ReactNativeCoreViewProps',
};
default: {
throw new Error(`Unable to handle prop spread: ${name}`);
}
}
}
removeKnownExtends(
typeDefinition: $ReadOnlyArray<PropAST>,
types: TypeDeclarationMap,
): $ReadOnlyArray<PropAST> {
return typeDefinition.filter(
prop =>
prop.type !== 'ObjectTypeSpreadProperty' ||
this.extendsForProp(prop, types, this) === null,
);
}
getExtendsProps(
typeDefinition: $ReadOnlyArray<PropAST>,
types: TypeDeclarationMap,
): $ReadOnlyArray<ExtendsPropsShape> {
return typeDefinition
.filter(prop => prop.type === 'ObjectTypeSpreadProperty')
.map(prop => this.extendsForProp(prop, types, this))
.filter(Boolean);
}
getProps(
typeDefinition: $ReadOnlyArray<PropAST>,
types: TypeDeclarationMap,
): {
props: $ReadOnlyArray<NamedShape<PropTypeAnnotation>>,
extendsProps: $ReadOnlyArray<ExtendsPropsShape>,
} {
const nonExtendsProps = this.removeKnownExtends(typeDefinition, types);
const props = flattenProperties(nonExtendsProps, types, this)
.map(property => buildPropSchema(property, types, this))
.filter(Boolean);
return {
props,
extendsProps: this.getExtendsProps(typeDefinition, types),
};
}
getProperties(typeName: string, types: TypeDeclarationMap): $FlowFixMe {
const typeAlias = types[typeName];
try {
return typeAlias.right.typeParameters.params[0].properties;
} catch (e) {
throw new Error(
`Failed to find type definition for "${typeName}", please check that you have a valid codegen flow file`,
);
}
}
nextNodeForTypeAlias(typeAnnotation: $FlowFixMe): $FlowFixMe {
return typeAnnotation.right;
}
nextNodeForEnum(typeAnnotation: $FlowFixMe): $FlowFixMe {
return typeAnnotation.body;
}
genericTypeAnnotationErrorMessage(typeAnnotation: $FlowFixMe): string {
return `A non GenericTypeAnnotation must be a type declaration ('${this.typeAlias}') or enum ('${this.enumDeclaration}'). Instead, got the unsupported ${typeAnnotation.type}.`;
}
extractTypeFromTypeAnnotation(typeAnnotation: $FlowFixMe): string {
return typeAnnotation.type === 'GenericTypeAnnotation'
? typeAnnotation.id.name
: typeAnnotation.type;
}
getObjectProperties(typeAnnotation: $FlowFixMe): $FlowFixMe {
return typeAnnotation.properties;
}
getLiteralValue(option: $FlowFixMe): $FlowFixMe {
return option.value;
}
getPaperTopLevelNameDeprecated(typeAnnotation: $FlowFixMe): $FlowFixMe {
return typeAnnotation.typeParameters.params.length > 1
? typeAnnotation.typeParameters.params[1].value
: null;
}
}
module.exports = {
FlowParser,
};

View File

@@ -0,0 +1,21 @@
/**
* 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 getValueFromTypes(value, types) {
if (value.type === 'GenericTypeAnnotation' && types[value.id.name]) {
return getValueFromTypes(types[value.id.name].right, types);
}
return value;
}
module.exports = {
getValueFromTypes,
};

View File

@@ -0,0 +1,24 @@
/**
* 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 {ASTNode, TypeDeclarationMap} from '../utils';
function getValueFromTypes(value: ASTNode, types: TypeDeclarationMap): ASTNode {
if (value.type === 'GenericTypeAnnotation' && types[value.id.name]) {
return getValueFromTypes(types[value.id.name].right, types);
}
return value;
}
module.exports = {
getValueFromTypes,
};

View File

@@ -0,0 +1,17 @@
/**
* 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.
*/
import type { SchemaType } from '../CodegenSchema';
import type { ParserType } from './errors';
// useful members only for downstream
export interface Parser {
language(): ParserType;
parseFile(filename: string): SchemaType;
parseString(contents: string, filename?: string): SchemaType;
parseModuleFixture(filename: string): SchemaType;
}

View File

@@ -0,0 +1,11 @@
/**
* 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';

View File

@@ -0,0 +1,433 @@
/**
* 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 {
ExtendsPropsShape,
NamedShape,
NativeModuleAliasMap,
NativeModuleEnumMap,
NativeModuleEnumMembers,
NativeModuleEnumMemberType,
NativeModuleParamTypeAnnotation,
Nullable,
PropTypeAnnotation,
SchemaType,
UnionTypeAnnotationMemberType,
} from '../CodegenSchema';
import type {ParserType} from './errors';
import type {
ASTNode,
ParserErrorCapturer,
PropAST,
TypeDeclarationMap,
TypeResolutionStatus,
} from './utils';
export type GetTypeAnnotationFN = (
name: string,
annotation: $FlowFixMe | ASTNode,
defaultValue: $FlowFixMe | void,
withNullDefault: boolean,
types: TypeDeclarationMap,
parser: Parser,
buildSchema: (
property: PropAST,
types: TypeDeclarationMap,
parser: Parser,
) => $FlowFixMe,
) => $FlowFixMe;
export type SchemaInfo = {
name: string,
optional: boolean,
typeAnnotation: $FlowFixMe,
defaultValue: $FlowFixMe,
withNullDefault: boolean,
};
export type GetSchemaInfoFN = (
property: PropAST,
types: TypeDeclarationMap,
) => ?SchemaInfo;
export type BuildSchemaFN<T> = (
property: PropAST,
types: TypeDeclarationMap,
parser: Parser,
) => ?NamedShape<T>;
export type ResolveTypeAnnotationFN = (
typeAnnotation: $FlowFixMe,
types: TypeDeclarationMap,
parser: Parser,
) => {
nullable: boolean,
typeAnnotation: $FlowFixMe,
typeResolutionStatus: TypeResolutionStatus,
};
/**
* This is the main interface for Parsers of various languages.
* It exposes all the methods that contain language-specific logic.
*/
export interface Parser {
/**
* This is the TypeParameterInstantiation value
*/
typeParameterInstantiation: string;
/**
* TypeAlias property of the Parser
*/
typeAlias: string;
/**
* enumDeclaration Property of the Parser
*/
enumDeclaration: string;
/**
* InterfaceDeclaration property of the Parser
*/
interfaceDeclaration: string;
/**
* This is the NullLiteralTypeAnnotation value
*/
nullLiteralTypeAnnotation: string;
/**
* UndefinedLiteralTypeAnnotation property of the Parser
*/
undefinedLiteralTypeAnnotation: string;
/**
* Given a declaration, it returns true if it is a property
*/
isProperty(property: $FlowFixMe): boolean;
/**
* Given a property declaration, it returns the key name.
* @parameter property: an object containing a property declaration.
* @parameter hasteModuleName: a string with the native module name.
* @returns: the key name.
* @throws if property does not contain a property declaration.
*/
getKeyName(property: $FlowFixMe, hasteModuleName: string): string;
/**
* @returns: the Parser language.
*/
language(): ParserType;
/**
* Given a type annotation, it returns the type name.
* @parameter typeAnnotation: the annotation for a type in the AST.
* @returns: the name of the type.
*/
getTypeAnnotationName(typeAnnotation: $FlowFixMe): string;
/**
* Given a type arguments, it returns a boolean specifying if the Module is Invalid.
* @parameter typeArguments: the type arguments.
* @returns: a boolean specifying if the Module is Invalid.
*/
checkIfInvalidModule(typeArguments: $FlowFixMe): boolean;
/**
* Given a union annotation members types, it returns an array of remaped members names without duplicates.
* @parameter membersTypes: union annotation members types
* @returns: an array of remaped members names without duplicates.
*/
remapUnionTypeAnnotationMemberNames(
types: $FlowFixMe,
): UnionTypeAnnotationMemberType[];
/**
* Given the name of a file, it returns a Schema.
* @parameter filename: the name of the file.
* @returns: the Schema of the file.
*/
parseFile(filename: string): SchemaType;
/**
* Given the content of a file, it returns a Schema.
* @parameter contents: the content of the file.
* @parameter filename: the name of the file.
* @returns: the Schema of the file.
*/
parseString(contents: string, filename: ?string): SchemaType;
/**
* Given the name of a file, it returns a Schema.
* @parameter filename: the name of the file.
* @returns: the Schema of the file.
*/
parseModuleFixture(filename: string): SchemaType;
/**
* Given the content of a file, it returns an AST.
* @parameter contents: the content of the file.
* @parameter filename: the name of the file, if available.
* @throws if there is a syntax error.
* @returns: the AST of the file.
*/
getAst(contents: string, filename?: ?string): $FlowFixMe;
/**
* Given a FunctionTypeAnnotation, it returns an array of its parameters.
* @parameter functionTypeAnnotation: a FunctionTypeAnnotation
* @returns: the parameters of the FunctionTypeAnnotation.
*/
getFunctionTypeAnnotationParameters(
functionTypeAnnotation: $FlowFixMe,
): $ReadOnlyArray<$FlowFixMe>;
/**
* Given a parameter, it returns the function name of the parameter.
* @parameter parameter: a parameter of a FunctionTypeAnnotation.
* @returns: the function name of the parameter.
*/
getFunctionNameFromParameter(
parameter: NamedShape<Nullable<NativeModuleParamTypeAnnotation>>,
): $FlowFixMe;
/**
* Given a parameter, it returns its name.
* @parameter parameter: a parameter of a FunctionTypeAnnotation.
* @returns: the name of the parameter.
*/
getParameterName(parameter: $FlowFixMe): string;
/**
* Given a parameter, it returns its typeAnnotation.
* @parameter parameter: a parameter of a FunctionTypeAnnotation.
* @returns: the typeAnnotation of the parameter.
*/
getParameterTypeAnnotation(param: $FlowFixMe): $FlowFixMe;
/**
* Given a FunctionTypeAnnotation, it returns its returnType.
* @parameter functionTypeAnnotation: a FunctionTypeAnnotation
* @returns: the returnType of the FunctionTypeAnnotation.
*/
getFunctionTypeAnnotationReturnType(
functionTypeAnnotation: $FlowFixMe,
): $FlowFixMe;
/**
* Calculates an enum's members type
*/
parseEnumMembersType(typeAnnotation: $FlowFixMe): NativeModuleEnumMemberType;
/**
* Throws if enum mebers are not supported
*/
validateEnumMembersSupported(
typeAnnotation: $FlowFixMe,
enumMembersType: NativeModuleEnumMemberType,
): void;
/**
* Calculates enum's members
*/
parseEnumMembers(typeAnnotation: $FlowFixMe): NativeModuleEnumMembers;
/**
* Given a node, it returns true if it is a module interface
*/
isModuleInterface(node: $FlowFixMe): boolean;
/**
* Given a type name, it returns true if it is a generic type annotation
*/
isGenericTypeAnnotation(type: $FlowFixMe): boolean;
/**
* Given a typeAnnotation, it returns the annotated element.
* @parameter typeAnnotation: the annotation for a type.
* @parameter types: a map of type declarations.
* @returns: the annotated element.
*/
extractAnnotatedElement(
typeAnnotation: $FlowFixMe,
types: TypeDeclarationMap,
): $FlowFixMe;
/**
* Given the AST, returns the TypeDeclarationMap
*/
getTypes(ast: $FlowFixMe): TypeDeclarationMap;
/**
* Given a callExpression, it returns the typeParameters of the callExpression.
* @parameter callExpression: the callExpression.
* @returns: the typeParameters of the callExpression or null if it does not exist.
*/
callExpressionTypeParameters(callExpression: $FlowFixMe): $FlowFixMe | null;
/**
* Given an array of properties from a Partial type, it returns an array of remaped properties.
* @parameter properties: properties from a Partial types.
* @parameter hasteModuleName: a string with the native module name.
* @parameter types: a map of type declarations.
* @parameter aliasMap: a map of type aliases.
* @parameter enumMap: a map of type enums.
* @parameter tryParse: a parser error capturer.
* @parameter cxxOnly: a boolean specifying if the module is Cxx only.
* @returns: an array of remaped properties
*/
computePartialProperties(
properties: Array<$FlowFixMe>,
hasteModuleName: string,
types: TypeDeclarationMap,
aliasMap: {...NativeModuleAliasMap},
enumMap: {...NativeModuleEnumMap},
tryParse: ParserErrorCapturer,
cxxOnly: boolean,
): Array<$FlowFixMe>;
/**
* Given a propertyValueType, it returns a boolean specifying if the property is a function type annotation.
* @parameter propertyValueType: the propertyValueType.
* @returns: a boolean specifying if the property is a function type annotation.
*/
functionTypeAnnotation(propertyValueType: string): boolean;
/**
* Given a declaration, it returns the typeArgumentParams of the declaration.
* @parameter declaration: the declaration.
* @returns: the typeArgumentParams of the declaration.
*/
getTypeArgumentParamsFromDeclaration(declaration: $FlowFixMe): $FlowFixMe;
/**
* Given a typeArgumentParams and funcArgumentParams it returns a native component type.
* @parameter typeArgumentParams: the typeArgumentParams.
* @parameter funcArgumentParams: the funcArgumentParams.
* @returns: a native component type.
*/
getNativeComponentType(
typeArgumentParams: $FlowFixMe,
funcArgumentParams: $FlowFixMe,
): {[string]: string};
/**
* Given a annotatedElement, it returns the properties of annotated element.
* @parameter annotatedElement: the annotated element.
* @returns: the properties of annotated element.
*/
getAnnotatedElementProperties(annotatedElement: $FlowFixMe): $FlowFixMe;
/**
* Given a typeAlias, it returns an array of properties.
* @parameter typeAlias: the type alias.
* @returns: an array of properties.
*/
bodyProperties(typeAlias: $FlowFixMe): $ReadOnlyArray<$FlowFixMe>;
/**
* Given a keyword convert it to TypeAnnotation.
* @parameter keyword
* @returns: converted TypeAnnotation to Keywords
*/
convertKeywordToTypeAnnotation(keyword: string): string;
/**
* Given a prop return its arguments.
* @parameter prop
* @returns: Arguments of the prop
*/
argumentForProp(prop: PropAST): $FlowFixMe;
/**
* Given a prop return its name.
* @parameter prop
* @returns: name property
*/
nameForArgument(prop: PropAST): $FlowFixMe;
/**
* Given a property return if it is optional.
* @parameter property
* @returns: a boolean specifying if the Property is optional
*/
isOptionalProperty(property: $FlowFixMe): boolean;
getGetTypeAnnotationFN(): GetTypeAnnotationFN;
getGetSchemaInfoFN(): GetSchemaInfoFN;
/**
* Given a property return the type annotation.
* @parameter property
* @returns: the annotation for a type in the AST.
*/
getTypeAnnotationFromProperty(property: PropAST): $FlowFixMe;
getResolvedTypeAnnotation(
typeAnnotation: $FlowFixMe,
types: TypeDeclarationMap,
parser: Parser,
): {
nullable: boolean,
typeAnnotation: $FlowFixMe,
typeResolutionStatus: TypeResolutionStatus,
};
getResolveTypeAnnotationFN(): ResolveTypeAnnotationFN;
getProps(
typeDefinition: $ReadOnlyArray<PropAST>,
types: TypeDeclarationMap,
): {
props: $ReadOnlyArray<NamedShape<PropTypeAnnotation>>,
extendsProps: $ReadOnlyArray<ExtendsPropsShape>,
};
getProperties(typeName: string, types: TypeDeclarationMap): $FlowFixMe;
/**
* Given a typeAlias, it returns the next node.
*/
nextNodeForTypeAlias(typeAnnotation: $FlowFixMe): $FlowFixMe;
/**
* Given an enum Declaration, it returns the next node.
*/
nextNodeForEnum(typeAnnotation: $FlowFixMe): $FlowFixMe;
/**
* Given a unsupported typeAnnotation, returns an error message.
*/
genericTypeAnnotationErrorMessage(typeAnnotation: $FlowFixMe): string;
/**
* Given a type annotation, it extracts the type.
* @parameter typeAnnotation: the annotation for a type in the AST.
* @returns: the extracted type.
*/
extractTypeFromTypeAnnotation(typeAnnotation: $FlowFixMe): string;
/**
* Given a typeAnnotation return the properties of an object.
* @parameter property
* @returns: the properties of an object represented by a type annotation.
*/
getObjectProperties(typeAnnotation: $FlowFixMe): $FlowFixMe;
/**
* Given a option return the literal value.
* @parameter option
* @returns: the literal value of an union represented.
*/
getLiteralValue(option: $FlowFixMe): $FlowFixMe;
/**
* Given a type annotation, it returns top level name in the AST if it exists else returns null.
* @parameter typeAnnotation: the annotation for a type in the AST.
* @returns: the top level name properties in the AST if it exists else null.
*/
getPaperTopLevelNameDeprecated(typeAnnotation: $FlowFixMe): $FlowFixMe;
}

View File

@@ -0,0 +1,417 @@
/**
* 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 _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);
}
import invariant from 'invariant';
const _require = require('./errors'),
UnsupportedObjectPropertyTypeAnnotationParserError =
_require.UnsupportedObjectPropertyTypeAnnotationParserError;
const _require2 = require('./flow/parseFlowAndThrowErrors'),
parseFlowAndThrowErrors = _require2.parseFlowAndThrowErrors;
const _require3 = require('./parsers-commons'),
buildPropSchema = _require3.buildPropSchema;
const _require4 = require('./typescript/components/componentsUtils'),
flattenProperties = _require4.flattenProperties;
const schemaMock = {
modules: {
StringPropNativeComponentView: {
type: 'Component',
components: {
StringPropNativeComponentView: {
extendsProps: [],
events: [],
props: [],
commands: [],
},
},
},
},
};
export class MockedParser {
constructor() {
_defineProperty(
this,
'typeParameterInstantiation',
'TypeParameterInstantiation',
);
_defineProperty(this, 'typeAlias', 'TypeAlias');
_defineProperty(this, 'enumDeclaration', 'EnumDeclaration');
_defineProperty(this, 'interfaceDeclaration', 'InterfaceDeclaration');
_defineProperty(
this,
'nullLiteralTypeAnnotation',
'NullLiteralTypeAnnotation',
);
_defineProperty(
this,
'undefinedLiteralTypeAnnotation',
'VoidLiteralTypeAnnotation',
);
}
isProperty(property) {
return property.type === 'ObjectTypeProperty';
}
getKeyName(property, hasteModuleName) {
if (!this.isProperty(property)) {
throw new UnsupportedObjectPropertyTypeAnnotationParserError(
hasteModuleName,
property,
property.type,
this.language(),
);
}
return property.key.name;
}
language() {
return 'Flow';
}
getTypeAnnotationName(typeAnnotation) {
var _typeAnnotation$id;
return typeAnnotation === null || typeAnnotation === void 0
? void 0
: (_typeAnnotation$id = typeAnnotation.id) === null ||
_typeAnnotation$id === void 0
? void 0
: _typeAnnotation$id.name;
}
checkIfInvalidModule(typeArguments) {
return false;
}
remapUnionTypeAnnotationMemberNames(membersTypes) {
return [];
}
parseFile(filename) {
return schemaMock;
}
parseString(contents, filename) {
return schemaMock;
}
parseModuleFixture(filename) {
return schemaMock;
}
getAst(contents, filename) {
return parseFlowAndThrowErrors(contents, {
filename,
});
}
getFunctionTypeAnnotationParameters(functionTypeAnnotation) {
return functionTypeAnnotation.params;
}
getFunctionNameFromParameter(parameter) {
return parameter.name;
}
getParameterName(parameter) {
return parameter.name.name;
}
getParameterTypeAnnotation(parameter) {
return parameter.typeAnnotation;
}
getFunctionTypeAnnotationReturnType(functionTypeAnnotation) {
return functionTypeAnnotation.returnType;
}
parseEnumMembersType(typeAnnotation) {
return typeAnnotation.type;
}
validateEnumMembersSupported(typeAnnotation, enumMembersType) {
return;
}
parseEnumMembers(typeAnnotation) {
return typeAnnotation.type === 'StringTypeAnnotation'
? [
{
name: 'Hello',
value: 'hello',
},
{
name: 'Goodbye',
value: 'goodbye',
},
]
: [
{
name: 'On',
value: '1',
},
{
name: 'Off',
value: '0',
},
];
}
isModuleInterface(node) {
return (
node.type === 'InterfaceDeclaration' &&
node.extends.length === 1 &&
node.extends[0].type === 'InterfaceExtends' &&
node.extends[0].id.name === 'TurboModule'
);
}
isGenericTypeAnnotation(type) {
return true;
}
extractAnnotatedElement(typeAnnotation, types) {
return types[typeAnnotation.typeParameters.params[0].id.name];
}
getTypes(ast) {
return {};
}
callExpressionTypeParameters(callExpression) {
return callExpression.typeArguments || null;
}
computePartialProperties(
properties,
hasteModuleName,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
) {
return [
{
name: 'a',
optional: true,
typeAnnotation: {
type: 'StringTypeAnnotation',
},
},
{
name: 'b',
optional: true,
typeAnnotation: {
type: 'BooleanTypeAnnotation',
},
},
];
}
functionTypeAnnotation(propertyValueType) {
return propertyValueType === 'FunctionTypeAnnotation';
}
getTypeArgumentParamsFromDeclaration(declaration) {
return declaration.typeArguments.params;
}
getNativeComponentType(typeArgumentParams, funcArgumentParams) {
return {
propsTypeName: typeArgumentParams[0].id.name,
componentName: funcArgumentParams[0].value,
};
}
getAnnotatedElementProperties(annotatedElement) {
return annotatedElement.right.properties;
}
bodyProperties(typeAlias) {
return typeAlias.body.properties;
}
convertKeywordToTypeAnnotation(keyword) {
return keyword;
}
argumentForProp(prop) {
return prop.expression;
}
nameForArgument(prop) {
return prop.expression.name;
}
isOptionalProperty(property) {
return property.optional || false;
}
getTypeAnnotationFromProperty(property) {
return property.typeAnnotation.typeAnnotation;
}
getGetTypeAnnotationFN() {
return () => {
return {};
};
}
getGetSchemaInfoFN() {
return () => {
return {
name: 'MockedSchema',
optional: false,
typeAnnotation: 'BooleanTypeAnnotation',
defaultValue: false,
withNullDefault: false,
};
};
}
getResolveTypeAnnotationFN() {
return () => {
return {
nullable: false,
typeAnnotation: null,
typeResolutionStatus: {
successful: false,
},
};
};
}
getResolvedTypeAnnotation(typeAnnotation, types) {
invariant(
typeAnnotation != null,
'resolveTypeAnnotation(): typeAnnotation cannot be null',
);
let node = typeAnnotation;
let nullable = false;
let typeResolutionStatus = {
successful: false,
};
for (;;) {
if (node.type === 'NullableTypeAnnotation') {
nullable = true;
node = node.typeAnnotation;
continue;
}
if (node.type !== 'GenericTypeAnnotation') {
break;
}
const resolvedTypeAnnotation = types[node.id.name];
if (resolvedTypeAnnotation == null) {
break;
}
switch (resolvedTypeAnnotation.type) {
case 'TypeAlias': {
typeResolutionStatus = {
successful: true,
type: 'alias',
name: node.id.name,
};
node = resolvedTypeAnnotation.right;
break;
}
case 'EnumDeclaration': {
typeResolutionStatus = {
successful: true,
type: 'enum',
name: node.id.name,
};
node = resolvedTypeAnnotation.body;
break;
}
default: {
throw new TypeError(
`A non GenericTypeAnnotation must be a type declaration ('TypeAlias') or enum ('EnumDeclaration'). Instead, got the unsupported ${resolvedTypeAnnotation.type}.`,
);
}
}
}
return {
nullable: nullable,
typeAnnotation: node,
typeResolutionStatus,
};
}
getExtendsProps(typeDefinition, types) {
return typeDefinition
.filter(prop => prop.type === 'ObjectTypeSpreadProperty')
.map(prop => this.extendsForProp(prop, types, this))
.filter(Boolean);
}
extendsForProp(prop, types, parser) {
const argument = this.argumentForProp(prop);
if (!argument) {
console.log('null', prop);
}
const name = parser.nameForArgument(prop);
if (types[name] != null) {
// This type is locally defined in the file
return null;
}
switch (name) {
case 'ViewProps':
return {
type: 'ReactNativeBuiltInType',
knownTypeName: 'ReactNativeCoreViewProps',
};
default: {
throw new Error(`Unable to handle prop spread: ${name}`);
}
}
}
removeKnownExtends(typeDefinition, types) {
return typeDefinition.filter(
prop =>
prop.type !== 'ObjectTypeSpreadProperty' ||
this.extendsForProp(prop, types, this) === null,
);
}
getProps(typeDefinition, types) {
const nonExtendsProps = this.removeKnownExtends(typeDefinition, types);
const props = flattenProperties(nonExtendsProps, types, this)
.map(property => buildPropSchema(property, types, this))
.filter(Boolean);
return {
props,
extendsProps: this.getExtendsProps(typeDefinition, types),
};
}
getProperties(typeName, types) {
const typeAlias = types[typeName];
try {
return typeAlias.right.typeParameters.params[0].properties;
} catch (e) {
throw new Error(
`Failed to find type definition for "${typeName}", please check that you have a valid codegen flow file`,
);
}
}
nextNodeForTypeAlias(typeAnnotation) {
return typeAnnotation.right;
}
nextNodeForEnum(typeAnnotation) {
return typeAnnotation.body;
}
genericTypeAnnotationErrorMessage(typeAnnotation) {
return `A non GenericTypeAnnotation must be a type declaration ('${this.typeAlias}') or enum ('${this.enumDeclaration}'). Instead, got the unsupported ${typeAnnotation.type}.`;
}
extractTypeFromTypeAnnotation(typeAnnotation) {
return typeAnnotation.type === 'GenericTypeAnnotation'
? typeAnnotation.id.name
: typeAnnotation.type;
}
getObjectProperties(typeAnnotation) {
return typeAnnotation.properties;
}
getLiteralValue(option) {
return option.value;
}
getPaperTopLevelNameDeprecated(typeAnnotation) {
return typeAnnotation.typeParameters.params.length > 1
? typeAnnotation.typeParameters.params[1].value
: null;
}
}

View File

@@ -0,0 +1,492 @@
/**
* 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 {
ExtendsPropsShape,
NamedShape,
NativeModuleAliasMap,
NativeModuleEnumMap,
NativeModuleEnumMembers,
NativeModuleEnumMemberType,
NativeModuleParamTypeAnnotation,
Nullable,
PropTypeAnnotation,
SchemaType,
UnionTypeAnnotationMemberType,
} from '../CodegenSchema';
import type {ParserType} from './errors';
import type {
GetSchemaInfoFN,
GetTypeAnnotationFN,
Parser,
ResolveTypeAnnotationFN,
} from './parser';
import type {
ParserErrorCapturer,
PropAST,
TypeDeclarationMap,
TypeResolutionStatus,
} from './utils';
import invariant from 'invariant';
const {
UnsupportedObjectPropertyTypeAnnotationParserError,
} = require('./errors');
const {parseFlowAndThrowErrors} = require('./flow/parseFlowAndThrowErrors');
const {buildPropSchema} = require('./parsers-commons');
const {flattenProperties} = require('./typescript/components/componentsUtils');
type ExtendsForProp = null | {
type: 'ReactNativeBuiltInType',
knownTypeName: 'ReactNativeCoreViewProps',
};
const schemaMock = {
modules: {
StringPropNativeComponentView: {
type: 'Component',
components: {
StringPropNativeComponentView: {
extendsProps: [],
events: [],
props: [],
commands: [],
},
},
},
},
};
export class MockedParser implements Parser {
typeParameterInstantiation: string = 'TypeParameterInstantiation';
typeAlias: string = 'TypeAlias';
enumDeclaration: string = 'EnumDeclaration';
interfaceDeclaration: string = 'InterfaceDeclaration';
nullLiteralTypeAnnotation: string = 'NullLiteralTypeAnnotation';
undefinedLiteralTypeAnnotation: string = 'VoidLiteralTypeAnnotation';
isProperty(property: $FlowFixMe): boolean {
return property.type === 'ObjectTypeProperty';
}
getKeyName(property: $FlowFixMe, hasteModuleName: string): string {
if (!this.isProperty(property)) {
throw new UnsupportedObjectPropertyTypeAnnotationParserError(
hasteModuleName,
property,
property.type,
this.language(),
);
}
return property.key.name;
}
language(): ParserType {
return 'Flow';
}
getTypeAnnotationName(typeAnnotation: $FlowFixMe): string {
return typeAnnotation?.id?.name;
}
checkIfInvalidModule(typeArguments: $FlowFixMe): boolean {
return false;
}
remapUnionTypeAnnotationMemberNames(
membersTypes: $FlowFixMe[],
): UnionTypeAnnotationMemberType[] {
return [];
}
parseFile(filename: string): SchemaType {
return schemaMock;
}
parseString(contents: string, filename: ?string): SchemaType {
return schemaMock;
}
parseModuleFixture(filename: string): SchemaType {
return schemaMock;
}
getAst(contents: string, filename?: ?string): $FlowFixMe {
return parseFlowAndThrowErrors(contents, {filename});
}
getFunctionTypeAnnotationParameters(
functionTypeAnnotation: $FlowFixMe,
): $ReadOnlyArray<$FlowFixMe> {
return functionTypeAnnotation.params;
}
getFunctionNameFromParameter(
parameter: NamedShape<Nullable<NativeModuleParamTypeAnnotation>>,
): $FlowFixMe {
return parameter.name;
}
getParameterName(parameter: $FlowFixMe): string {
return parameter.name.name;
}
getParameterTypeAnnotation(parameter: $FlowFixMe): $FlowFixMe {
return parameter.typeAnnotation;
}
getFunctionTypeAnnotationReturnType(
functionTypeAnnotation: $FlowFixMe,
): $FlowFixMe {
return functionTypeAnnotation.returnType;
}
parseEnumMembersType(typeAnnotation: $FlowFixMe): NativeModuleEnumMemberType {
return typeAnnotation.type;
}
validateEnumMembersSupported(
typeAnnotation: $FlowFixMe,
enumMembersType: NativeModuleEnumMemberType,
): void {
return;
}
parseEnumMembers(typeAnnotation: $FlowFixMe): NativeModuleEnumMembers {
return typeAnnotation.type === 'StringTypeAnnotation'
? [
{
name: 'Hello',
value: 'hello',
},
{
name: 'Goodbye',
value: 'goodbye',
},
]
: [
{
name: 'On',
value: '1',
},
{
name: 'Off',
value: '0',
},
];
}
isModuleInterface(node: $FlowFixMe): boolean {
return (
node.type === 'InterfaceDeclaration' &&
node.extends.length === 1 &&
node.extends[0].type === 'InterfaceExtends' &&
node.extends[0].id.name === 'TurboModule'
);
}
isGenericTypeAnnotation(type: $FlowFixMe): boolean {
return true;
}
extractAnnotatedElement(
typeAnnotation: $FlowFixMe,
types: TypeDeclarationMap,
): $FlowFixMe {
return types[typeAnnotation.typeParameters.params[0].id.name];
}
getTypes(ast: $FlowFixMe): TypeDeclarationMap {
return {};
}
callExpressionTypeParameters(callExpression: $FlowFixMe): $FlowFixMe | null {
return callExpression.typeArguments || null;
}
computePartialProperties(
properties: Array<$FlowFixMe>,
hasteModuleName: string,
types: TypeDeclarationMap,
aliasMap: {...NativeModuleAliasMap},
enumMap: {...NativeModuleEnumMap},
tryParse: ParserErrorCapturer,
cxxOnly: boolean,
): Array<$FlowFixMe> {
return [
{
name: 'a',
optional: true,
typeAnnotation: {type: 'StringTypeAnnotation'},
},
{
name: 'b',
optional: true,
typeAnnotation: {type: 'BooleanTypeAnnotation'},
},
];
}
functionTypeAnnotation(propertyValueType: string): boolean {
return propertyValueType === 'FunctionTypeAnnotation';
}
getTypeArgumentParamsFromDeclaration(declaration: $FlowFixMe): $FlowFixMe {
return declaration.typeArguments.params;
}
getNativeComponentType(
typeArgumentParams: $FlowFixMe,
funcArgumentParams: $FlowFixMe,
): {[string]: string} {
return {
propsTypeName: typeArgumentParams[0].id.name,
componentName: funcArgumentParams[0].value,
};
}
getAnnotatedElementProperties(annotatedElement: $FlowFixMe): $FlowFixMe {
return annotatedElement.right.properties;
}
bodyProperties(typeAlias: $FlowFixMe): $ReadOnlyArray<$FlowFixMe> {
return typeAlias.body.properties;
}
convertKeywordToTypeAnnotation(keyword: string): string {
return keyword;
}
argumentForProp(prop: PropAST): $FlowFixMe {
return prop.expression;
}
nameForArgument(prop: PropAST): $FlowFixMe {
return prop.expression.name;
}
isOptionalProperty(property: $FlowFixMe): boolean {
return property.optional || false;
}
getTypeAnnotationFromProperty(property: PropAST): $FlowFixMe {
return property.typeAnnotation.typeAnnotation;
}
getGetTypeAnnotationFN(): GetTypeAnnotationFN {
return () => {
return {};
};
}
getGetSchemaInfoFN(): GetSchemaInfoFN {
return () => {
return {
name: 'MockedSchema',
optional: false,
typeAnnotation: 'BooleanTypeAnnotation',
defaultValue: false,
withNullDefault: false,
};
};
}
getResolveTypeAnnotationFN(): ResolveTypeAnnotationFN {
return () => {
return {
nullable: false,
typeAnnotation: null,
typeResolutionStatus: {successful: false},
};
};
}
getResolvedTypeAnnotation(
typeAnnotation: $FlowFixMe,
types: TypeDeclarationMap,
): {
nullable: boolean,
typeAnnotation: $FlowFixMe,
typeResolutionStatus: TypeResolutionStatus,
} {
invariant(
typeAnnotation != null,
'resolveTypeAnnotation(): typeAnnotation cannot be null',
);
let node = typeAnnotation;
let nullable = false;
let typeResolutionStatus: TypeResolutionStatus = {
successful: false,
};
for (;;) {
if (node.type === 'NullableTypeAnnotation') {
nullable = true;
node = node.typeAnnotation;
continue;
}
if (node.type !== 'GenericTypeAnnotation') {
break;
}
const resolvedTypeAnnotation = types[node.id.name];
if (resolvedTypeAnnotation == null) {
break;
}
switch (resolvedTypeAnnotation.type) {
case 'TypeAlias': {
typeResolutionStatus = {
successful: true,
type: 'alias',
name: node.id.name,
};
node = resolvedTypeAnnotation.right;
break;
}
case 'EnumDeclaration': {
typeResolutionStatus = {
successful: true,
type: 'enum',
name: node.id.name,
};
node = resolvedTypeAnnotation.body;
break;
}
default: {
throw new TypeError(
`A non GenericTypeAnnotation must be a type declaration ('TypeAlias') or enum ('EnumDeclaration'). Instead, got the unsupported ${resolvedTypeAnnotation.type}.`,
);
}
}
}
return {
nullable: nullable,
typeAnnotation: node,
typeResolutionStatus,
};
}
getExtendsProps(
typeDefinition: $ReadOnlyArray<PropAST>,
types: TypeDeclarationMap,
): $ReadOnlyArray<ExtendsPropsShape> {
return typeDefinition
.filter(prop => prop.type === 'ObjectTypeSpreadProperty')
.map(prop => this.extendsForProp(prop, types, this))
.filter(Boolean);
}
extendsForProp(
prop: PropAST,
types: TypeDeclarationMap,
parser: Parser,
): ExtendsForProp {
const argument = this.argumentForProp(prop);
if (!argument) {
console.log('null', prop);
}
const name = parser.nameForArgument(prop);
if (types[name] != null) {
// This type is locally defined in the file
return null;
}
switch (name) {
case 'ViewProps':
return {
type: 'ReactNativeBuiltInType',
knownTypeName: 'ReactNativeCoreViewProps',
};
default: {
throw new Error(`Unable to handle prop spread: ${name}`);
}
}
}
removeKnownExtends(
typeDefinition: $ReadOnlyArray<PropAST>,
types: TypeDeclarationMap,
): $ReadOnlyArray<PropAST> {
return typeDefinition.filter(
prop =>
prop.type !== 'ObjectTypeSpreadProperty' ||
this.extendsForProp(prop, types, this) === null,
);
}
getProps(
typeDefinition: $ReadOnlyArray<PropAST>,
types: TypeDeclarationMap,
): {
props: $ReadOnlyArray<NamedShape<PropTypeAnnotation>>,
extendsProps: $ReadOnlyArray<ExtendsPropsShape>,
} {
const nonExtendsProps = this.removeKnownExtends(typeDefinition, types);
const props = flattenProperties(nonExtendsProps, types, this)
.map(property => buildPropSchema(property, types, this))
.filter(Boolean);
return {
props,
extendsProps: this.getExtendsProps(typeDefinition, types),
};
}
getProperties(typeName: string, types: TypeDeclarationMap): $FlowFixMe {
const typeAlias = types[typeName];
try {
return typeAlias.right.typeParameters.params[0].properties;
} catch (e) {
throw new Error(
`Failed to find type definition for "${typeName}", please check that you have a valid codegen flow file`,
);
}
}
nextNodeForTypeAlias(typeAnnotation: $FlowFixMe): $FlowFixMe {
return typeAnnotation.right;
}
nextNodeForEnum(typeAnnotation: $FlowFixMe): $FlowFixMe {
return typeAnnotation.body;
}
genericTypeAnnotationErrorMessage(typeAnnotation: $FlowFixMe): string {
return `A non GenericTypeAnnotation must be a type declaration ('${this.typeAlias}') or enum ('${this.enumDeclaration}'). Instead, got the unsupported ${typeAnnotation.type}.`;
}
extractTypeFromTypeAnnotation(typeAnnotation: $FlowFixMe): string {
return typeAnnotation.type === 'GenericTypeAnnotation'
? typeAnnotation.id.name
: typeAnnotation.type;
}
getObjectProperties(typeAnnotation: $FlowFixMe): $FlowFixMe {
return typeAnnotation.properties;
}
getLiteralValue(option: $FlowFixMe): $FlowFixMe {
return option.value;
}
getPaperTopLevelNameDeprecated(typeAnnotation: $FlowFixMe): $FlowFixMe {
return typeAnnotation.typeParameters.params.length > 1
? typeAnnotation.typeParameters.params[1].value
: null;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,662 @@
/**
* 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 _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(arr, i) {
var _i =
null == arr
? null
: ('undefined' != typeof Symbol && arr[Symbol.iterator]) ||
arr['@@iterator'];
if (null != _i) {
var _s,
_e,
_x,
_r,
_arr = [],
_n = !0,
_d = !1;
try {
if (((_x = (_i = _i.call(arr)).next), 0 === i)) {
if (Object(_i) !== _i) return;
_n = !1;
} else
for (
;
!(_n = (_s = _x.call(_i)).done) &&
(_arr.push(_s.value), _arr.length !== i);
_n = !0
);
} catch (err) {
(_d = !0), (_e = err);
} finally {
try {
if (!_n && null != _i.return && ((_r = _i.return()), Object(_r) !== _r))
return;
} finally {
if (_d) throw _e;
}
}
return _arr;
}
}
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
const _require = require('./error-utils'),
throwIfArrayElementTypeAnnotationIsUnsupported =
_require.throwIfArrayElementTypeAnnotationIsUnsupported,
throwIfPartialNotAnnotatingTypeParameter =
_require.throwIfPartialNotAnnotatingTypeParameter,
throwIfPartialWithMoreParameter = _require.throwIfPartialWithMoreParameter;
const _require2 = require('./errors'),
ParserError = _require2.ParserError,
UnsupportedTypeAnnotationParserError =
_require2.UnsupportedTypeAnnotationParserError,
UnsupportedUnionTypeAnnotationParserError =
_require2.UnsupportedUnionTypeAnnotationParserError;
const _require3 = require('./parsers-commons'),
assertGenericTypeAnnotationHasExactlyOneTypeParameter =
_require3.assertGenericTypeAnnotationHasExactlyOneTypeParameter,
translateFunctionTypeAnnotation = _require3.translateFunctionTypeAnnotation,
unwrapNullable = _require3.unwrapNullable,
wrapNullable = _require3.wrapNullable;
const _require4 = require('./parsers-utils'),
nullGuard = _require4.nullGuard;
const _require5 = require('./utils'),
isModuleRegistryCall = _require5.isModuleRegistryCall;
function emitBoolean(nullable) {
return wrapNullable(nullable, {
type: 'BooleanTypeAnnotation',
});
}
function emitInt32(nullable) {
return wrapNullable(nullable, {
type: 'Int32TypeAnnotation',
});
}
function emitInt32Prop(name, optional) {
return {
name,
optional,
typeAnnotation: {
type: 'Int32TypeAnnotation',
},
};
}
function emitNumber(nullable) {
return wrapNullable(nullable, {
type: 'NumberTypeAnnotation',
});
}
function emitRootTag(nullable) {
return wrapNullable(nullable, {
type: 'ReservedTypeAnnotation',
name: 'RootTag',
});
}
function emitDouble(nullable) {
return wrapNullable(nullable, {
type: 'DoubleTypeAnnotation',
});
}
function emitDoubleProp(name, optional) {
return {
name,
optional,
typeAnnotation: {
type: 'DoubleTypeAnnotation',
},
};
}
function emitVoid(nullable) {
return wrapNullable(nullable, {
type: 'VoidTypeAnnotation',
});
}
function emitStringish(nullable) {
return wrapNullable(nullable, {
type: 'StringTypeAnnotation',
});
}
function emitFunction(
nullable,
hasteModuleName,
typeAnnotation,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
translateTypeAnnotation,
parser,
) {
const translateFunctionTypeAnnotationValue = translateFunctionTypeAnnotation(
hasteModuleName,
typeAnnotation,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
translateTypeAnnotation,
parser,
);
return wrapNullable(nullable, translateFunctionTypeAnnotationValue);
}
function emitMixed(nullable) {
return wrapNullable(nullable, {
type: 'MixedTypeAnnotation',
});
}
function emitString(nullable) {
return wrapNullable(nullable, {
type: 'StringTypeAnnotation',
});
}
function emitStringProp(name, optional) {
return {
name,
optional,
typeAnnotation: {
type: 'StringTypeAnnotation',
},
};
}
function typeAliasResolution(
typeResolution,
objectTypeAnnotation,
aliasMap,
nullable,
) {
if (!typeResolution.successful) {
return wrapNullable(nullable, objectTypeAnnotation);
}
/**
* All aliases RHS are required.
*/
aliasMap[typeResolution.name] = 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: typeResolution.name,
});
}
function typeEnumResolution(
typeAnnotation,
typeResolution,
nullable,
hasteModuleName,
enumMap,
parser,
) {
if (!typeResolution.successful || typeResolution.type !== 'enum') {
throw new UnsupportedTypeAnnotationParserError(
hasteModuleName,
typeAnnotation,
parser.language(),
);
}
const enumName = typeResolution.name;
const enumMemberType = parser.parseEnumMembersType(typeAnnotation);
try {
parser.validateEnumMembersSupported(typeAnnotation, enumMemberType);
} catch (e) {
if (e instanceof Error) {
throw new ParserError(
hasteModuleName,
typeAnnotation,
`Failed parsing the enum ${enumName} in ${hasteModuleName} with the error: ${e.message}`,
);
} else {
throw e;
}
}
const enumMembers = parser.parseEnumMembers(typeAnnotation);
enumMap[enumName] = {
name: enumName,
type: 'EnumDeclarationWithMembers',
memberType: enumMemberType,
members: enumMembers,
};
return wrapNullable(nullable, {
name: enumName,
type: 'EnumDeclaration',
memberType: enumMemberType,
});
}
function emitPromise(
hasteModuleName,
typeAnnotation,
parser,
nullable,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
translateTypeAnnotation,
) {
assertGenericTypeAnnotationHasExactlyOneTypeParameter(
hasteModuleName,
typeAnnotation,
parser,
);
const elementType = typeAnnotation.typeParameters.params[0];
if (
elementType.type === 'ExistsTypeAnnotation' ||
elementType.type === 'EmptyTypeAnnotation'
) {
return wrapNullable(nullable, {
type: 'PromiseTypeAnnotation',
});
} else {
try {
return wrapNullable(nullable, {
type: 'PromiseTypeAnnotation',
elementType: translateTypeAnnotation(
hasteModuleName,
typeAnnotation.typeParameters.params[0],
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
),
});
} catch {
return wrapNullable(nullable, {
type: 'PromiseTypeAnnotation',
});
}
}
}
function emitGenericObject(nullable) {
return wrapNullable(nullable, {
type: 'GenericObjectTypeAnnotation',
});
}
function emitDictionary(nullable, valueType) {
return wrapNullable(nullable, {
type: 'GenericObjectTypeAnnotation',
dictionaryValueType: valueType,
});
}
function emitObject(nullable, properties) {
return wrapNullable(nullable, {
type: 'ObjectTypeAnnotation',
properties,
});
}
function emitFloat(nullable) {
return wrapNullable(nullable, {
type: 'FloatTypeAnnotation',
});
}
function emitFloatProp(name, optional) {
return {
name,
optional,
typeAnnotation: {
type: 'FloatTypeAnnotation',
},
};
}
function emitUnion(nullable, hasteModuleName, typeAnnotation, parser) {
const unionTypes = parser.remapUnionTypeAnnotationMemberNames(
typeAnnotation.types,
);
// Only support unionTypes of the same kind
if (unionTypes.length > 1) {
throw new UnsupportedUnionTypeAnnotationParserError(
hasteModuleName,
typeAnnotation,
unionTypes,
);
}
return wrapNullable(nullable, {
type: 'UnionTypeAnnotation',
memberType: unionTypes[0],
});
}
function translateArrayTypeAnnotation(
hasteModuleName,
types,
aliasMap,
enumMap,
cxxOnly,
arrayType,
elementType,
nullable,
translateTypeAnnotation,
parser,
) {
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,
elementType,
types,
aliasMap,
enumMap,
/**
* 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,
parser,
),
),
_unwrapNullable2 = _slicedToArray(_unwrapNullable, 2),
_elementType = _unwrapNullable2[0],
isElementTypeNullable = _unwrapNullable2[1];
throwIfArrayElementTypeAnnotationIsUnsupported(
hasteModuleName,
elementType,
arrayType,
_elementType.type,
);
return wrapNullable(nullable, {
type: 'ArrayTypeAnnotation',
// $FlowFixMe[incompatible-call]
elementType: wrapNullable(isElementTypeNullable, _elementType),
});
} catch (ex) {
return wrapNullable(nullable, {
type: 'ArrayTypeAnnotation',
});
}
}
function emitArrayType(
hasteModuleName,
typeAnnotation,
parser,
types,
aliasMap,
enumMap,
cxxOnly,
nullable,
translateTypeAnnotation,
) {
assertGenericTypeAnnotationHasExactlyOneTypeParameter(
hasteModuleName,
typeAnnotation,
parser,
);
return translateArrayTypeAnnotation(
hasteModuleName,
types,
aliasMap,
enumMap,
cxxOnly,
typeAnnotation.type,
typeAnnotation.typeParameters.params[0],
nullable,
translateTypeAnnotation,
parser,
);
}
function Visitor(infoMap) {
return {
CallExpression(node) {
if (
node.callee.type === 'Identifier' &&
node.callee.name === 'codegenNativeComponent'
) {
infoMap.isComponent = true;
}
if (isModuleRegistryCall(node)) {
infoMap.isModule = true;
}
},
InterfaceExtends(node) {
if (node.id.name === 'TurboModule') {
infoMap.isModule = true;
}
},
TSInterfaceDeclaration(node) {
if (
Array.isArray(node.extends) &&
node.extends.some(
extension => extension.expression.name === 'TurboModule',
)
) {
infoMap.isModule = true;
}
},
};
}
function emitPartial(
nullable,
hasteModuleName,
typeAnnotation,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
) {
throwIfPartialWithMoreParameter(typeAnnotation);
throwIfPartialNotAnnotatingTypeParameter(typeAnnotation, types, parser);
const annotatedElement = parser.extractAnnotatedElement(
typeAnnotation,
types,
);
const annotatedElementProperties =
parser.getAnnotatedElementProperties(annotatedElement);
const partialProperties = parser.computePartialProperties(
annotatedElementProperties,
hasteModuleName,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
);
return emitObject(nullable, partialProperties);
}
function emitCommonTypes(
hasteModuleName,
types,
typeAnnotation,
aliasMap,
enumMap,
tryParse,
cxxOnly,
nullable,
parser,
) {
const typeMap = {
Stringish: emitStringish,
Int32: emitInt32,
Double: emitDouble,
Float: emitFloat,
UnsafeObject: emitGenericObject,
Object: emitGenericObject,
$Partial: emitPartial,
Partial: emitPartial,
BooleanTypeAnnotation: emitBoolean,
NumberTypeAnnotation: emitNumber,
VoidTypeAnnotation: emitVoid,
StringTypeAnnotation: emitString,
MixedTypeAnnotation: cxxOnly ? emitMixed : emitGenericObject,
};
const typeAnnotationName = parser.convertKeywordToTypeAnnotation(
typeAnnotation.type,
);
const simpleEmitter = typeMap[typeAnnotationName];
if (simpleEmitter) {
return simpleEmitter(nullable);
}
const genericTypeAnnotationName =
parser.getTypeAnnotationName(typeAnnotation);
const emitter = typeMap[genericTypeAnnotationName];
if (!emitter) {
return null;
}
return emitter(
nullable,
hasteModuleName,
typeAnnotation,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
);
}
function emitBoolProp(name, optional) {
return {
name,
optional,
typeAnnotation: {
type: 'BooleanTypeAnnotation',
},
};
}
function emitMixedProp(name, optional) {
return {
name,
optional,
typeAnnotation: {
type: 'MixedTypeAnnotation',
},
};
}
function emitObjectProp(
name,
optional,
parser,
typeAnnotation,
extractArrayElementType,
) {
return {
name,
optional,
typeAnnotation: extractArrayElementType(typeAnnotation, name, parser),
};
}
function emitUnionProp(name, optional, parser, typeAnnotation) {
return {
name,
optional,
typeAnnotation: {
type: 'StringEnumTypeAnnotation',
options: typeAnnotation.types.map(option =>
parser.getLiteralValue(option),
),
},
};
}
module.exports = {
emitArrayType,
emitBoolean,
emitBoolProp,
emitDouble,
emitDoubleProp,
emitFloat,
emitFloatProp,
emitFunction,
emitInt32,
emitInt32Prop,
emitMixedProp,
emitNumber,
emitGenericObject,
emitDictionary,
emitObject,
emitPromise,
emitRootTag,
emitVoid,
emitString,
emitStringish,
emitStringProp,
emitMixed,
emitUnion,
emitPartial,
emitCommonTypes,
typeAliasResolution,
typeEnumResolution,
translateArrayTypeAnnotation,
Visitor,
emitObjectProp,
emitUnionProp,
};

View File

@@ -0,0 +1,726 @@
/**
* 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 {
BooleanTypeAnnotation,
DoubleTypeAnnotation,
EventTypeAnnotation,
Int32TypeAnnotation,
NamedShape,
NativeModuleAliasMap,
NativeModuleBaseTypeAnnotation,
NativeModuleEnumDeclaration,
NativeModuleEnumMap,
NativeModuleFloatTypeAnnotation,
NativeModuleFunctionTypeAnnotation,
NativeModuleGenericObjectTypeAnnotation,
NativeModuleMixedTypeAnnotation,
NativeModuleNumberTypeAnnotation,
NativeModuleObjectTypeAnnotation,
NativeModulePromiseTypeAnnotation,
NativeModuleTypeAliasTypeAnnotation,
NativeModuleTypeAnnotation,
NativeModuleUnionTypeAnnotation,
Nullable,
ObjectTypeAnnotation,
ReservedTypeAnnotation,
StringTypeAnnotation,
VoidTypeAnnotation,
} from '../CodegenSchema';
import type {Parser} from './parser';
import type {
ParserErrorCapturer,
TypeDeclarationMap,
TypeResolutionStatus,
} from './utils';
const {
throwIfArrayElementTypeAnnotationIsUnsupported,
throwIfPartialNotAnnotatingTypeParameter,
throwIfPartialWithMoreParameter,
} = require('./error-utils');
const {
ParserError,
UnsupportedTypeAnnotationParserError,
UnsupportedUnionTypeAnnotationParserError,
} = require('./errors');
const {
assertGenericTypeAnnotationHasExactlyOneTypeParameter,
translateFunctionTypeAnnotation,
unwrapNullable,
wrapNullable,
} = require('./parsers-commons');
const {nullGuard} = require('./parsers-utils');
const {isModuleRegistryCall} = require('./utils');
function emitBoolean(nullable: boolean): Nullable<BooleanTypeAnnotation> {
return wrapNullable(nullable, {
type: 'BooleanTypeAnnotation',
});
}
function emitInt32(nullable: boolean): Nullable<Int32TypeAnnotation> {
return wrapNullable(nullable, {
type: 'Int32TypeAnnotation',
});
}
function emitInt32Prop(
name: string,
optional: boolean,
): NamedShape<Int32TypeAnnotation> {
return {
name,
optional,
typeAnnotation: {
type: 'Int32TypeAnnotation',
},
};
}
function emitNumber(
nullable: boolean,
): Nullable<NativeModuleNumberTypeAnnotation> {
return wrapNullable(nullable, {
type: 'NumberTypeAnnotation',
});
}
function emitRootTag(nullable: boolean): Nullable<ReservedTypeAnnotation> {
return wrapNullable(nullable, {
type: 'ReservedTypeAnnotation',
name: 'RootTag',
});
}
function emitDouble(nullable: boolean): Nullable<DoubleTypeAnnotation> {
return wrapNullable(nullable, {
type: 'DoubleTypeAnnotation',
});
}
function emitDoubleProp(
name: string,
optional: boolean,
): NamedShape<DoubleTypeAnnotation> {
return {
name,
optional,
typeAnnotation: {
type: 'DoubleTypeAnnotation',
},
};
}
function emitVoid(nullable: boolean): Nullable<VoidTypeAnnotation> {
return wrapNullable(nullable, {
type: 'VoidTypeAnnotation',
});
}
function emitStringish(nullable: boolean): Nullable<StringTypeAnnotation> {
return wrapNullable(nullable, {
type: 'StringTypeAnnotation',
});
}
function emitFunction(
nullable: boolean,
hasteModuleName: string,
typeAnnotation: $FlowFixMe,
types: TypeDeclarationMap,
aliasMap: {...NativeModuleAliasMap},
enumMap: {...NativeModuleEnumMap},
tryParse: ParserErrorCapturer,
cxxOnly: boolean,
translateTypeAnnotation: $FlowFixMe,
parser: Parser,
): Nullable<NativeModuleFunctionTypeAnnotation> {
const translateFunctionTypeAnnotationValue: NativeModuleFunctionTypeAnnotation =
translateFunctionTypeAnnotation(
hasteModuleName,
typeAnnotation,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
translateTypeAnnotation,
parser,
);
return wrapNullable(nullable, translateFunctionTypeAnnotationValue);
}
function emitMixed(
nullable: boolean,
): Nullable<NativeModuleMixedTypeAnnotation> {
return wrapNullable(nullable, {
type: 'MixedTypeAnnotation',
});
}
function emitString(nullable: boolean): Nullable<StringTypeAnnotation> {
return wrapNullable(nullable, {
type: 'StringTypeAnnotation',
});
}
function emitStringProp(
name: string,
optional: boolean,
): NamedShape<StringTypeAnnotation> {
return {
name,
optional,
typeAnnotation: {
type: 'StringTypeAnnotation',
},
};
}
function typeAliasResolution(
typeResolution: TypeResolutionStatus,
objectTypeAnnotation: ObjectTypeAnnotation<
Nullable<NativeModuleBaseTypeAnnotation>,
>,
aliasMap: {...NativeModuleAliasMap},
nullable: boolean,
):
| Nullable<NativeModuleTypeAliasTypeAnnotation>
| Nullable<ObjectTypeAnnotation<Nullable<NativeModuleBaseTypeAnnotation>>> {
if (!typeResolution.successful) {
return wrapNullable(nullable, objectTypeAnnotation);
}
/**
* All aliases RHS are required.
*/
aliasMap[typeResolution.name] = 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: typeResolution.name,
});
}
function typeEnumResolution(
typeAnnotation: $FlowFixMe,
typeResolution: TypeResolutionStatus,
nullable: boolean,
hasteModuleName: string,
enumMap: {...NativeModuleEnumMap},
parser: Parser,
): Nullable<NativeModuleEnumDeclaration> {
if (!typeResolution.successful || typeResolution.type !== 'enum') {
throw new UnsupportedTypeAnnotationParserError(
hasteModuleName,
typeAnnotation,
parser.language(),
);
}
const enumName = typeResolution.name;
const enumMemberType = parser.parseEnumMembersType(typeAnnotation);
try {
parser.validateEnumMembersSupported(typeAnnotation, enumMemberType);
} catch (e) {
if (e instanceof Error) {
throw new ParserError(
hasteModuleName,
typeAnnotation,
`Failed parsing the enum ${enumName} in ${hasteModuleName} with the error: ${e.message}`,
);
} else {
throw e;
}
}
const enumMembers = parser.parseEnumMembers(typeAnnotation);
enumMap[enumName] = {
name: enumName,
type: 'EnumDeclarationWithMembers',
memberType: enumMemberType,
members: enumMembers,
};
return wrapNullable(nullable, {
name: enumName,
type: 'EnumDeclaration',
memberType: enumMemberType,
});
}
function emitPromise(
hasteModuleName: string,
typeAnnotation: $FlowFixMe,
parser: Parser,
nullable: boolean,
types: TypeDeclarationMap,
aliasMap: {...NativeModuleAliasMap},
enumMap: {...NativeModuleEnumMap},
tryParse: ParserErrorCapturer,
cxxOnly: boolean,
translateTypeAnnotation: $FlowFixMe,
): Nullable<NativeModulePromiseTypeAnnotation> {
assertGenericTypeAnnotationHasExactlyOneTypeParameter(
hasteModuleName,
typeAnnotation,
parser,
);
const elementType = typeAnnotation.typeParameters.params[0];
if (
elementType.type === 'ExistsTypeAnnotation' ||
elementType.type === 'EmptyTypeAnnotation'
) {
return wrapNullable(nullable, {
type: 'PromiseTypeAnnotation',
});
} else {
try {
return wrapNullable(nullable, {
type: 'PromiseTypeAnnotation',
elementType: translateTypeAnnotation(
hasteModuleName,
typeAnnotation.typeParameters.params[0],
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
),
});
} catch {
return wrapNullable(nullable, {
type: 'PromiseTypeAnnotation',
});
}
}
}
function emitGenericObject(
nullable: boolean,
): Nullable<NativeModuleGenericObjectTypeAnnotation> {
return wrapNullable(nullable, {
type: 'GenericObjectTypeAnnotation',
});
}
function emitDictionary(
nullable: boolean,
valueType: Nullable<NativeModuleTypeAnnotation>,
): Nullable<NativeModuleGenericObjectTypeAnnotation> {
return wrapNullable(nullable, {
type: 'GenericObjectTypeAnnotation',
dictionaryValueType: valueType,
});
}
function emitObject(
nullable: boolean,
properties: Array<$FlowFixMe>,
): Nullable<NativeModuleObjectTypeAnnotation> {
return wrapNullable(nullable, {
type: 'ObjectTypeAnnotation',
properties,
});
}
function emitFloat(
nullable: boolean,
): Nullable<NativeModuleFloatTypeAnnotation> {
return wrapNullable(nullable, {
type: 'FloatTypeAnnotation',
});
}
function emitFloatProp(
name: string,
optional: boolean,
): NamedShape<EventTypeAnnotation> {
return {
name,
optional,
typeAnnotation: {
type: 'FloatTypeAnnotation',
},
};
}
function emitUnion(
nullable: boolean,
hasteModuleName: string,
typeAnnotation: $FlowFixMe,
parser: Parser,
): Nullable<NativeModuleUnionTypeAnnotation> {
const unionTypes = parser.remapUnionTypeAnnotationMemberNames(
typeAnnotation.types,
);
// Only support unionTypes of the same kind
if (unionTypes.length > 1) {
throw new UnsupportedUnionTypeAnnotationParserError(
hasteModuleName,
typeAnnotation,
unionTypes,
);
}
return wrapNullable(nullable, {
type: 'UnionTypeAnnotation',
memberType: unionTypes[0],
});
}
function translateArrayTypeAnnotation(
hasteModuleName: string,
types: TypeDeclarationMap,
aliasMap: {...NativeModuleAliasMap},
enumMap: {...NativeModuleEnumMap},
cxxOnly: boolean,
arrayType: 'Array' | 'ReadonlyArray',
elementType: $FlowFixMe,
nullable: boolean,
translateTypeAnnotation: $FlowFixMe,
parser: Parser,
): Nullable<NativeModuleTypeAnnotation> {
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<$FlowFixMe>(
translateTypeAnnotation(
hasteModuleName,
elementType,
types,
aliasMap,
enumMap,
/**
* 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,
parser,
),
);
throwIfArrayElementTypeAnnotationIsUnsupported(
hasteModuleName,
elementType,
arrayType,
_elementType.type,
);
return wrapNullable(nullable, {
type: 'ArrayTypeAnnotation',
// $FlowFixMe[incompatible-call]
elementType: wrapNullable(isElementTypeNullable, _elementType),
});
} catch (ex) {
return wrapNullable(nullable, {
type: 'ArrayTypeAnnotation',
});
}
}
function emitArrayType(
hasteModuleName: string,
typeAnnotation: $FlowFixMe,
parser: Parser,
types: TypeDeclarationMap,
aliasMap: {...NativeModuleAliasMap},
enumMap: {...NativeModuleEnumMap},
cxxOnly: boolean,
nullable: boolean,
translateTypeAnnotation: $FlowFixMe,
): Nullable<NativeModuleTypeAnnotation> {
assertGenericTypeAnnotationHasExactlyOneTypeParameter(
hasteModuleName,
typeAnnotation,
parser,
);
return translateArrayTypeAnnotation(
hasteModuleName,
types,
aliasMap,
enumMap,
cxxOnly,
typeAnnotation.type,
typeAnnotation.typeParameters.params[0],
nullable,
translateTypeAnnotation,
parser,
);
}
function Visitor(infoMap: {isComponent: boolean, isModule: boolean}): {
[type: string]: (node: $FlowFixMe) => void,
} {
return {
CallExpression(node: $FlowFixMe) {
if (
node.callee.type === 'Identifier' &&
node.callee.name === 'codegenNativeComponent'
) {
infoMap.isComponent = true;
}
if (isModuleRegistryCall(node)) {
infoMap.isModule = true;
}
},
InterfaceExtends(node: $FlowFixMe) {
if (node.id.name === 'TurboModule') {
infoMap.isModule = true;
}
},
TSInterfaceDeclaration(node: $FlowFixMe) {
if (
Array.isArray(node.extends) &&
node.extends.some(
extension => extension.expression.name === 'TurboModule',
)
) {
infoMap.isModule = true;
}
},
};
}
function emitPartial(
nullable: boolean,
hasteModuleName: string,
typeAnnotation: $FlowFixMe,
types: TypeDeclarationMap,
aliasMap: {...NativeModuleAliasMap},
enumMap: {...NativeModuleEnumMap},
tryParse: ParserErrorCapturer,
cxxOnly: boolean,
parser: Parser,
): Nullable<NativeModuleTypeAnnotation> {
throwIfPartialWithMoreParameter(typeAnnotation);
throwIfPartialNotAnnotatingTypeParameter(typeAnnotation, types, parser);
const annotatedElement = parser.extractAnnotatedElement(
typeAnnotation,
types,
);
const annotatedElementProperties =
parser.getAnnotatedElementProperties(annotatedElement);
const partialProperties = parser.computePartialProperties(
annotatedElementProperties,
hasteModuleName,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
);
return emitObject(nullable, partialProperties);
}
function emitCommonTypes(
hasteModuleName: string,
types: TypeDeclarationMap,
typeAnnotation: $FlowFixMe,
aliasMap: {...NativeModuleAliasMap},
enumMap: {...NativeModuleEnumMap},
tryParse: ParserErrorCapturer,
cxxOnly: boolean,
nullable: boolean,
parser: Parser,
): $FlowFixMe {
const typeMap = {
Stringish: emitStringish,
Int32: emitInt32,
Double: emitDouble,
Float: emitFloat,
UnsafeObject: emitGenericObject,
Object: emitGenericObject,
$Partial: emitPartial,
Partial: emitPartial,
BooleanTypeAnnotation: emitBoolean,
NumberTypeAnnotation: emitNumber,
VoidTypeAnnotation: emitVoid,
StringTypeAnnotation: emitString,
MixedTypeAnnotation: cxxOnly ? emitMixed : emitGenericObject,
};
const typeAnnotationName = parser.convertKeywordToTypeAnnotation(
typeAnnotation.type,
);
const simpleEmitter = typeMap[typeAnnotationName];
if (simpleEmitter) {
return simpleEmitter(nullable);
}
const genericTypeAnnotationName =
parser.getTypeAnnotationName(typeAnnotation);
const emitter = typeMap[genericTypeAnnotationName];
if (!emitter) {
return null;
}
return emitter(
nullable,
hasteModuleName,
typeAnnotation,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
);
}
function emitBoolProp(
name: string,
optional: boolean,
): NamedShape<EventTypeAnnotation> {
return {
name,
optional,
typeAnnotation: {
type: 'BooleanTypeAnnotation',
},
};
}
function emitMixedProp(
name: string,
optional: boolean,
): NamedShape<EventTypeAnnotation> {
return {
name,
optional,
typeAnnotation: {
type: 'MixedTypeAnnotation',
},
};
}
function emitObjectProp(
name: string,
optional: boolean,
parser: Parser,
typeAnnotation: $FlowFixMe,
extractArrayElementType: (
typeAnnotation: $FlowFixMe,
name: string,
parser: Parser,
) => EventTypeAnnotation,
): NamedShape<EventTypeAnnotation> {
return {
name,
optional,
typeAnnotation: extractArrayElementType(typeAnnotation, name, parser),
};
}
function emitUnionProp(
name: string,
optional: boolean,
parser: Parser,
typeAnnotation: $FlowFixMe,
): NamedShape<EventTypeAnnotation> {
return {
name,
optional,
typeAnnotation: {
type: 'StringEnumTypeAnnotation',
options: typeAnnotation.types.map(option =>
parser.getLiteralValue(option),
),
},
};
}
module.exports = {
emitArrayType,
emitBoolean,
emitBoolProp,
emitDouble,
emitDoubleProp,
emitFloat,
emitFloatProp,
emitFunction,
emitInt32,
emitInt32Prop,
emitMixedProp,
emitNumber,
emitGenericObject,
emitDictionary,
emitObject,
emitPromise,
emitRootTag,
emitVoid,
emitString,
emitStringish,
emitStringProp,
emitMixed,
emitUnion,
emitPartial,
emitCommonTypes,
typeAliasResolution,
typeEnumResolution,
translateArrayTypeAnnotation,
Visitor,
emitObjectProp,
emitUnionProp,
};

View File

@@ -0,0 +1,18 @@
/**
* 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 nullGuard(fn) {
return fn();
}
module.exports = {
nullGuard,
};

View File

@@ -0,0 +1,19 @@
/**
* 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
* @flow strict
*/
'use strict';
function nullGuard<T>(fn: () => T): ?T {
return fn();
}
module.exports = {
nullGuard,
};

View File

@@ -0,0 +1,106 @@
/**
* 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(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
enumerableOnly &&
(symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
})),
keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread(target) {
for (var i = 1; i < arguments.length; i++) {
var source = null != arguments[i] ? arguments[i] : {};
i % 2
? ownKeys(Object(source), !0).forEach(function (key) {
_defineProperty(target, key, source[key]);
})
: Object.getOwnPropertyDescriptors
? Object.defineProperties(
target,
Object.getOwnPropertyDescriptors(source),
)
: ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(
target,
key,
Object.getOwnPropertyDescriptor(source, key),
);
});
}
return target;
}
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 wrapComponentSchema({
filename,
componentName,
extendsProps,
events,
props,
options,
commands,
}) {
return {
modules: {
[filename]: {
type: 'Component',
components: {
[componentName]: _objectSpread(
_objectSpread({}, options || {}),
{},
{
extendsProps,
events,
props,
commands,
},
),
},
},
},
};
}
module.exports = {
wrapComponentSchema,
};

View File

@@ -0,0 +1,62 @@
/**
* 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
* @flow strict
*/
'use strict';
import type {
CommandTypeAnnotation,
EventTypeShape,
ExtendsPropsShape,
NamedShape,
OptionsShape,
PropTypeAnnotation,
SchemaType,
} from '../CodegenSchema.js';
export type ComponentSchemaBuilderConfig = $ReadOnly<{
filename: string,
componentName: string,
extendsProps: $ReadOnlyArray<ExtendsPropsShape>,
events: $ReadOnlyArray<EventTypeShape>,
props: $ReadOnlyArray<NamedShape<PropTypeAnnotation>>,
commands: $ReadOnlyArray<NamedShape<CommandTypeAnnotation>>,
options?: ?OptionsShape,
}>;
function wrapComponentSchema({
filename,
componentName,
extendsProps,
events,
props,
options,
commands,
}: ComponentSchemaBuilderConfig): SchemaType {
return {
modules: {
[filename]: {
type: 'Component',
components: {
[componentName]: {
...(options || {}),
extendsProps,
events,
props,
commands,
},
},
},
},
};
}
module.exports = {
wrapComponentSchema,
};

View File

@@ -0,0 +1,10 @@
/**
* 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.
*/
import type { SchemaType } from '../../CodegenSchema';
export declare function parse(filename: string): SchemaType | undefined;

View File

@@ -0,0 +1,23 @@
/**
* 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 parse(filename) {
try {
// $FlowFixMe[unsupported-syntax] Can't require dynamic variables
return require(filename);
} catch (err) {
// Ignore
}
}
module.exports = {
parse,
};

View File

@@ -0,0 +1,26 @@
/**
* 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 {SchemaType} from '../../CodegenSchema.js';
function parse(filename: string): ?SchemaType {
try {
// $FlowFixMe[unsupported-syntax] Can't require dynamic variables
return require(filename);
} catch (err) {
// Ignore
}
}
module.exports = {
parse,
};

View File

@@ -0,0 +1,489 @@
/**
* 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
*/
// @licenselint-loose-mode
'use strict';
const COMMANDS_DEFINED_INLINE = `
/**
* 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
*/
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
export interface ModuleProps extends ViewProps {
// No props
}
export const Commands = codegenNativeCommands<{
readonly hotspotUpdate: (
ref: React.Ref<'RCTView'>,
x: Int32,
y: Int32,
) => void;
}>({
supportedCommands: ['hotspotUpdate'],
});
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const COMMANDS_DEFINED_MULTIPLE_TIMES = `
/**
* 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
*/
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
readonly hotspotUpdate: (
viewRef: React.Ref<'RCTView'>,
x: Int32,
y: Int32,
) => void;
}
export interface ModuleProps extends ViewProps {
// No props or events
}
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate'],
});
export const Commands2 = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate'],
});
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const COMMANDS_DEFINED_WITHOUT_REF = `
/**
* 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
*/
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
readonly hotspotUpdate: (x: Int32, y: Int32) => void;
}
export interface ModuleProps extends ViewProps {
// No props or events
}
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate'],
});
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const COMMANDS_DEFINED_WITH_NULLABLE_REF = `
/**
* 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
*/
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
readonly hotspotUpdate: (viewRef: React.Ref<'RCTView'> | null | undefined, x: Int32, y: Int32) => void;
}
export interface ModuleProps extends ViewProps {
// No props or events
}
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate'],
});
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const COMMANDS_DEFINED_WITH_MISMATCHED_METHOD_NAMES = `
/**
* 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
*/
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
readonly hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void;
readonly scrollTo: (
viewRef: React.Ref<'RCTView'>,
y: Int32,
animated: boolean,
) => void;
}
export interface ModuleProps extends ViewProps {
// No props or events
}
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['scrollTo'],
});
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const COMMANDS_DEFINED_WITHOUT_METHOD_NAMES = `
/**
* 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
*/
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
readonly hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void;
readonly scrollTo: (
viewRef: React.Ref<'RCTView'>,
y: Int32,
animated: boolean,
) => void;
}
export interface ModuleProps extends ViewProps {
// No props or events
}
export const Commands = codegenNativeCommands<NativeCommands>();
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const NULLABLE_WITH_DEFAULT = `
/**
* 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
*/
const codegenNativeComponent = require('codegenNativeComponent');
import type {WithDefault, Float} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
export interface ModuleProps extends ViewProps {
nullable_with_default: WithDefault<Float, 1.0> | null | undefined;
}
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const NON_OPTIONAL_KEY_WITH_DEFAULT_VALUE = `
/**
* 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
*/
const codegenNativeComponent = require('codegenNativeComponent');
import type {WithDefault, Float} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
export interface ModuleProps extends ViewProps {
required_key_with_default: WithDefault<Float, 1.0>;
}
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const PROPS_CONFLICT_NAMES = `
/**
* 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
*/
const codegenNativeComponent = require('codegenNativeComponent');
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
export interface ModuleProps extends ViewProps {
isEnabled: string,
isEnabled: boolean,
}
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const PROPS_CONFLICT_WITH_SPREAD_PROPS = `
/**
* 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
*/
const codegenNativeComponent = require('codegenNativeComponent');
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
type PropsInFile = Readonly<{
isEnabled: boolean,
}>;
export interface ModuleProps extends ViewProps, PropsInFile {
isEnabled: boolean,
}
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const PROP_NUMBER_TYPE = `
/**
* 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
*/
const codegenNativeComponent = require('codegenNativeComponent');
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
export interface ModuleProps extends ViewProps {
someProp: number
}
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const PROP_MIXED_ENUM = `
/**
* 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
*/
const codegenNativeComponent = require('codegenNativeComponent');
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
import type {WithDefault} from 'CodegenTypes';
export interface ModuleProps extends ViewProps {
someProp?: WithDefault<'foo' | 1, 1>;
}
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const PROP_ENUM_BOOLEAN = `
/**
* 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
*/
const codegenNativeComponent = require('codegenNativeComponent');
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
import type {WithDefault} from 'CodegenTypes';
export interface ModuleProps extends ViewProps {
someProp?: WithDefault<false | true, false>
}
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const PROP_ARRAY_MIXED_ENUM = `
/**
* 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
*/
const codegenNativeComponent = require('codegenNativeComponent');
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
import type {WithDefault} from 'CodegenTypes';
export interface ModuleProps extends ViewProps {
someProp?: WithDefault<ReadonlyArray<'foo' | 1>, 1>;
}
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const PROP_ARRAY_ENUM_BOOLEAN = `
/**
* 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
*/
const codegenNativeComponent = require('codegenNativeComponent');
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
import type {WithDefault} from 'CodegenTypes';
export interface ModuleProps extends ViewProps {
someProp?: WithDefault<ReadonlyArray<false | true>, false>;
}
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const PROP_ARRAY_ENUM_INT = `
/**
* 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
*/
const codegenNativeComponent = require('codegenNativeComponent');
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
import type {WithDefault} from 'CodegenTypes';
export interface ModuleProps extends ViewProps {
someProp?: WithDefault<ReadonlyArray<0 | 1>, 0>;
}
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
module.exports = {
COMMANDS_DEFINED_INLINE,
COMMANDS_DEFINED_MULTIPLE_TIMES,
COMMANDS_DEFINED_WITH_MISMATCHED_METHOD_NAMES,
COMMANDS_DEFINED_WITHOUT_METHOD_NAMES,
COMMANDS_DEFINED_WITHOUT_REF,
COMMANDS_DEFINED_WITH_NULLABLE_REF,
NULLABLE_WITH_DEFAULT,
NON_OPTIONAL_KEY_WITH_DEFAULT_VALUE,
PROPS_CONFLICT_NAMES,
PROPS_CONFLICT_WITH_SPREAD_PROPS,
PROP_NUMBER_TYPE,
PROP_MIXED_ENUM,
PROP_ENUM_BOOLEAN,
PROP_ARRAY_MIXED_ENUM,
PROP_ARRAY_ENUM_BOOLEAN,
PROP_ARRAY_ENUM_INT,
};

View File

@@ -0,0 +1,505 @@
/**
* 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
*/
// @licenselint-loose-mode
'use strict';
const COMMANDS_DEFINED_INLINE = `
/**
* 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
*/
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
export interface ModuleProps extends ViewProps {
// No props
}
export const Commands = codegenNativeCommands<{
readonly hotspotUpdate: (
ref: React.Ref<'RCTView'>,
x: Int32,
y: Int32,
) => void;
}>({
supportedCommands: ['hotspotUpdate'],
});
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const COMMANDS_DEFINED_MULTIPLE_TIMES = `
/**
* 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
*/
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
readonly hotspotUpdate: (
viewRef: React.Ref<'RCTView'>,
x: Int32,
y: Int32,
) => void;
}
export interface ModuleProps extends ViewProps {
// No props or events
}
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate'],
});
export const Commands2 = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate'],
});
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const COMMANDS_DEFINED_WITHOUT_REF = `
/**
* 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
*/
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
readonly hotspotUpdate: (x: Int32, y: Int32) => void;
}
export interface ModuleProps extends ViewProps {
// No props or events
}
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate'],
});
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const COMMANDS_DEFINED_WITH_NULLABLE_REF = `
/**
* 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
*/
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
readonly hotspotUpdate: (viewRef: React.Ref<'RCTView'> | null | undefined, x: Int32, y: Int32) => void;
}
export interface ModuleProps extends ViewProps {
// No props or events
}
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate'],
});
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const COMMANDS_DEFINED_WITH_MISMATCHED_METHOD_NAMES = `
/**
* 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
*/
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
readonly hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void;
readonly scrollTo: (
viewRef: React.Ref<'RCTView'>,
y: Int32,
animated: boolean,
) => void;
}
export interface ModuleProps extends ViewProps {
// No props or events
}
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['scrollTo'],
});
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const COMMANDS_DEFINED_WITHOUT_METHOD_NAMES = `
/**
* 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
*/
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
readonly hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void;
readonly scrollTo: (
viewRef: React.Ref<'RCTView'>,
y: Int32,
animated: boolean,
) => void;
}
export interface ModuleProps extends ViewProps {
// No props or events
}
export const Commands = codegenNativeCommands<NativeCommands>();
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const NULLABLE_WITH_DEFAULT = `
/**
* 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
*/
const codegenNativeComponent = require('codegenNativeComponent');
import type {WithDefault, Float} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
export interface ModuleProps extends ViewProps {
nullable_with_default: WithDefault<Float, 1.0> | null | undefined;
}
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const NON_OPTIONAL_KEY_WITH_DEFAULT_VALUE = `
/**
* 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
*/
const codegenNativeComponent = require('codegenNativeComponent');
import type {WithDefault, Float} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
export interface ModuleProps extends ViewProps {
required_key_with_default: WithDefault<Float, 1.0>;
}
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const PROPS_CONFLICT_NAMES = `
/**
* 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
*/
const codegenNativeComponent = require('codegenNativeComponent');
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
export interface ModuleProps extends ViewProps {
isEnabled: string,
isEnabled: boolean,
}
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const PROPS_CONFLICT_WITH_SPREAD_PROPS = `
/**
* 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
*/
const codegenNativeComponent = require('codegenNativeComponent');
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
type PropsInFile = Readonly<{
isEnabled: boolean,
}>;
export interface ModuleProps extends ViewProps, PropsInFile {
isEnabled: boolean,
}
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const PROP_NUMBER_TYPE = `
/**
* 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
*/
const codegenNativeComponent = require('codegenNativeComponent');
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
export interface ModuleProps extends ViewProps {
someProp: number
}
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const PROP_MIXED_ENUM = `
/**
* 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
*/
const codegenNativeComponent = require('codegenNativeComponent');
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
import type {WithDefault} from 'CodegenTypes';
export interface ModuleProps extends ViewProps {
someProp?: WithDefault<'foo' | 1, 1>;
}
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const PROP_ENUM_BOOLEAN = `
/**
* 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
*/
const codegenNativeComponent = require('codegenNativeComponent');
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
import type {WithDefault} from 'CodegenTypes';
export interface ModuleProps extends ViewProps {
someProp?: WithDefault<false | true, false>
}
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const PROP_ARRAY_MIXED_ENUM = `
/**
* 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
*/
const codegenNativeComponent = require('codegenNativeComponent');
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
import type {WithDefault} from 'CodegenTypes';
export interface ModuleProps extends ViewProps {
someProp?: WithDefault<ReadonlyArray<'foo' | 1>, 1>;
}
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const PROP_ARRAY_ENUM_BOOLEAN = `
/**
* 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
*/
const codegenNativeComponent = require('codegenNativeComponent');
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
import type {WithDefault} from 'CodegenTypes';
export interface ModuleProps extends ViewProps {
someProp?: WithDefault<ReadonlyArray<false | true>, false>;
}
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
const PROP_ARRAY_ENUM_INT = `
/**
* 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
*/
const codegenNativeComponent = require('codegenNativeComponent');
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
import type {WithDefault} from 'CodegenTypes';
export interface ModuleProps extends ViewProps {
someProp?: WithDefault<ReadonlyArray<0 | 1>, 0>;
}
export default codegenNativeComponent<ModuleProps>(
'Module',
) as HostComponent<ModuleProps>;
`;
module.exports = {
COMMANDS_DEFINED_INLINE,
COMMANDS_DEFINED_MULTIPLE_TIMES,
COMMANDS_DEFINED_WITH_MISMATCHED_METHOD_NAMES,
COMMANDS_DEFINED_WITHOUT_METHOD_NAMES,
COMMANDS_DEFINED_WITHOUT_REF,
COMMANDS_DEFINED_WITH_NULLABLE_REF,
NULLABLE_WITH_DEFAULT,
NON_OPTIONAL_KEY_WITH_DEFAULT_VALUE,
PROPS_CONFLICT_NAMES,
PROPS_CONFLICT_WITH_SPREAD_PROPS,
PROP_NUMBER_TYPE,
PROP_MIXED_ENUM,
PROP_ENUM_BOOLEAN,
PROP_ARRAY_MIXED_ENUM,
PROP_ARRAY_ENUM_BOOLEAN,
PROP_ARRAY_ENUM_INT,
};

View File

@@ -0,0 +1,143 @@
/**
* 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';
const _require = require('../parseTopLevelType'),
parseTopLevelType = _require.parseTopLevelType;
const _require2 = require('./componentsUtils'),
getPrimitiveTypeAnnotation = _require2.getPrimitiveTypeAnnotation;
// $FlowFixMe[unclear-type] there's no flowtype for ASTs
function buildCommandSchemaInternal(name, optional, parameters, types) {
var _firstParam$typeAnnot, _firstParam$typeAnnot2;
const firstParam = parameters[0].typeAnnotation;
if (
!(
firstParam.typeAnnotation != null &&
firstParam.typeAnnotation.type === 'TSTypeReference' &&
((_firstParam$typeAnnot = firstParam.typeAnnotation.typeName.left) ===
null || _firstParam$typeAnnot === void 0
? void 0
: _firstParam$typeAnnot.name) === 'React' &&
((_firstParam$typeAnnot2 = firstParam.typeAnnotation.typeName.right) ===
null || _firstParam$typeAnnot2 === void 0
? void 0
: _firstParam$typeAnnot2.name) === 'ElementRef'
)
) {
throw new Error(
`The first argument of method ${name} must be of type React.ElementRef<>`,
);
}
const params = parameters.slice(1).map(param => {
const paramName = param.name;
const paramValue = parseTopLevelType(
param.typeAnnotation.typeAnnotation,
types,
).type;
const type =
paramValue.type === 'TSTypeReference'
? paramValue.typeName.name
: paramValue.type;
let returnType;
switch (type) {
case 'RootTag':
returnType = {
type: 'ReservedTypeAnnotation',
name: 'RootTag',
};
break;
case 'TSBooleanKeyword':
case 'Int32':
case 'Double':
case 'Float':
case 'TSStringKeyword':
returnType = getPrimitiveTypeAnnotation(type);
break;
case 'Array':
case 'ReadOnlyArray':
if (!paramValue.type === 'TSTypeReference') {
throw new Error(
'Array and ReadOnlyArray are TSTypeReference for array',
);
}
returnType = {
type: 'ArrayTypeAnnotation',
elementType: getPrimitiveTypeAnnotation(
// TODO: T172453752 support complex type annotation for array element
paramValue.typeParameters.params[0].type,
),
};
break;
case 'TSArrayType':
returnType = {
type: 'ArrayTypeAnnotation',
elementType: {
// TODO: T172453752 support complex type annotation for array element
type: getPrimitiveTypeAnnotation(paramValue.elementType.type).type,
},
};
break;
default:
type;
throw new Error(
`Unsupported param type for method "${name}", param "${paramName}". Found ${type}`,
);
}
return {
name: paramName,
optional: false,
typeAnnotation: returnType,
};
});
return {
name,
optional,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
params,
returnTypeAnnotation: {
type: 'VoidTypeAnnotation',
},
},
};
}
function buildCommandSchema(property, types) {
if (property.type === 'TSPropertySignature') {
const topLevelType = parseTopLevelType(
property.typeAnnotation.typeAnnotation,
types,
);
const name = property.key.name;
const optional = property.optional || topLevelType.optional;
const parameters = topLevelType.type.parameters || topLevelType.type.params;
return buildCommandSchemaInternal(name, optional, parameters, types);
} else {
const name = property.key.name;
const optional = property.optional || false;
const parameters = property.parameters || property.params;
return buildCommandSchemaInternal(name, optional, parameters, types);
}
}
function getCommands(commandTypeAST, types) {
return commandTypeAST
.filter(
property =>
property.type === 'TSPropertySignature' ||
property.type === 'TSMethodSignature',
)
.map(property => buildCommandSchema(property, types))
.filter(Boolean);
}
module.exports = {
getCommands,
};

View File

@@ -0,0 +1,160 @@
/**
* 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 {
CommandTypeAnnotation,
NamedShape,
} from '../../../CodegenSchema.js';
import type {TypeDeclarationMap} from '../../utils';
const {parseTopLevelType} = require('../parseTopLevelType');
const {getPrimitiveTypeAnnotation} = require('./componentsUtils');
// $FlowFixMe[unclear-type] there's no flowtype for ASTs
type EventTypeAST = Object;
function buildCommandSchemaInternal(
name: string,
optional: boolean,
parameters: Array<$FlowFixMe>,
types: TypeDeclarationMap,
): NamedShape<CommandTypeAnnotation> {
const firstParam = parameters[0].typeAnnotation;
if (
!(
firstParam.typeAnnotation != null &&
firstParam.typeAnnotation.type === 'TSTypeReference' &&
firstParam.typeAnnotation.typeName.left?.name === 'React' &&
firstParam.typeAnnotation.typeName.right?.name === 'ElementRef'
)
) {
throw new Error(
`The first argument of method ${name} must be of type React.ElementRef<>`,
);
}
const params = parameters.slice(1).map(param => {
const paramName = param.name;
const paramValue = parseTopLevelType(
param.typeAnnotation.typeAnnotation,
types,
).type;
const type =
paramValue.type === 'TSTypeReference'
? paramValue.typeName.name
: paramValue.type;
let returnType;
switch (type) {
case 'RootTag':
returnType = {
type: 'ReservedTypeAnnotation',
name: 'RootTag',
};
break;
case 'TSBooleanKeyword':
case 'Int32':
case 'Double':
case 'Float':
case 'TSStringKeyword':
returnType = getPrimitiveTypeAnnotation(type);
break;
case 'Array':
case 'ReadOnlyArray':
if (!paramValue.type === 'TSTypeReference') {
throw new Error(
'Array and ReadOnlyArray are TSTypeReference for array',
);
}
returnType = {
type: 'ArrayTypeAnnotation',
elementType: getPrimitiveTypeAnnotation(
// TODO: T172453752 support complex type annotation for array element
paramValue.typeParameters.params[0].type,
),
};
break;
case 'TSArrayType':
returnType = {
type: 'ArrayTypeAnnotation',
elementType: {
// TODO: T172453752 support complex type annotation for array element
type: getPrimitiveTypeAnnotation(paramValue.elementType.type).type,
},
};
break;
default:
(type: empty);
throw new Error(
`Unsupported param type for method "${name}", param "${paramName}". Found ${type}`,
);
}
return {
name: paramName,
optional: false,
typeAnnotation: returnType,
};
});
return {
name,
optional,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
params,
returnTypeAnnotation: {
type: 'VoidTypeAnnotation',
},
},
};
}
function buildCommandSchema(
property: EventTypeAST,
types: TypeDeclarationMap,
): NamedShape<CommandTypeAnnotation> {
if (property.type === 'TSPropertySignature') {
const topLevelType = parseTopLevelType(
property.typeAnnotation.typeAnnotation,
types,
);
const name = property.key.name;
const optional = property.optional || topLevelType.optional;
const parameters = topLevelType.type.parameters || topLevelType.type.params;
return buildCommandSchemaInternal(name, optional, parameters, types);
} else {
const name = property.key.name;
const optional = property.optional || false;
const parameters = property.parameters || property.params;
return buildCommandSchemaInternal(name, optional, parameters, types);
}
}
function getCommands(
commandTypeAST: $ReadOnlyArray<EventTypeAST>,
types: TypeDeclarationMap,
): $ReadOnlyArray<NamedShape<CommandTypeAnnotation>> {
return commandTypeAST
.filter(
property =>
property.type === 'TSPropertySignature' ||
property.type === 'TSMethodSignature',
)
.map(property => buildCommandSchema(property, types))
.filter(Boolean);
}
module.exports = {
getCommands,
};

View File

@@ -0,0 +1,490 @@
/**
* 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';
const _require = require('../../parsers-commons'),
verifyPropNotAlreadyDefined = _require.verifyPropNotAlreadyDefined;
const _require2 = require('../parseTopLevelType'),
flattenIntersectionType = _require2.flattenIntersectionType,
parseTopLevelType = _require2.parseTopLevelType;
function getUnionOfLiterals(name, forArray, elementTypes, defaultValue, types) {
var _elementTypes$0$liter, _elementTypes$0$liter2;
elementTypes.reduce((lastType, currType) => {
const lastFlattenedType =
lastType && lastType.type === 'TSLiteralType'
? lastType.literal.type
: lastType.type;
const currFlattenedType =
currType.type === 'TSLiteralType' ? currType.literal.type : currType.type;
if (lastFlattenedType && currFlattenedType !== lastFlattenedType) {
throw new Error(`Mixed types are not supported (see "${name}")`);
}
return currType;
});
if (defaultValue === undefined) {
throw new Error(`A default enum value is required for "${name}"`);
}
const unionType = elementTypes[0].type;
if (
unionType === 'TSLiteralType' &&
((_elementTypes$0$liter = elementTypes[0].literal) === null ||
_elementTypes$0$liter === void 0
? void 0
: _elementTypes$0$liter.type) === 'StringLiteral'
) {
return {
type: 'StringEnumTypeAnnotation',
default: defaultValue,
options: elementTypes.map(option => option.literal.value),
};
} else if (
unionType === 'TSLiteralType' &&
((_elementTypes$0$liter2 = elementTypes[0].literal) === null ||
_elementTypes$0$liter2 === void 0
? void 0
: _elementTypes$0$liter2.type) === 'NumericLiteral'
) {
if (forArray) {
throw new Error(`Arrays of int enums are not supported (see: "${name}")`);
} else {
return {
type: 'Int32EnumTypeAnnotation',
default: defaultValue,
options: elementTypes.map(option => option.literal.value),
};
}
} else {
var _elementTypes$0$liter3;
throw new Error(
`Unsupported union type for "${name}", received "${
unionType === 'TSLiteralType'
? (_elementTypes$0$liter3 = elementTypes[0].literal) === null ||
_elementTypes$0$liter3 === void 0
? void 0
: _elementTypes$0$liter3.type
: unionType
}"`,
);
}
}
function detectArrayType(
name,
typeAnnotation,
defaultValue,
types,
parser,
buildSchema,
) {
// Covers: readonly T[]
if (
typeAnnotation.type === 'TSTypeOperator' &&
typeAnnotation.operator === 'readonly' &&
typeAnnotation.typeAnnotation.type === 'TSArrayType'
) {
return {
type: 'ArrayTypeAnnotation',
elementType: getTypeAnnotationForArray(
name,
typeAnnotation.typeAnnotation.elementType,
defaultValue,
types,
parser,
buildSchema,
),
};
}
// Covers: T[]
if (typeAnnotation.type === 'TSArrayType') {
return {
type: 'ArrayTypeAnnotation',
elementType: getTypeAnnotationForArray(
name,
typeAnnotation.elementType,
defaultValue,
types,
parser,
buildSchema,
),
};
}
// Covers: Array<T> and ReadonlyArray<T>
if (
typeAnnotation.type === 'TSTypeReference' &&
(parser.getTypeAnnotationName(typeAnnotation) === 'ReadonlyArray' ||
parser.getTypeAnnotationName(typeAnnotation) === 'Array')
) {
return {
type: 'ArrayTypeAnnotation',
elementType: getTypeAnnotationForArray(
name,
typeAnnotation.typeParameters.params[0],
defaultValue,
types,
parser,
buildSchema,
),
};
}
return null;
}
function buildObjectType(rawProperties, types, parser, buildSchema) {
const flattenedProperties = flattenProperties(rawProperties, types, parser);
const properties = flattenedProperties
.map(prop => buildSchema(prop, types, parser))
.filter(Boolean);
return {
type: 'ObjectTypeAnnotation',
properties,
};
}
function getPrimitiveTypeAnnotation(type) {
switch (type) {
case 'Int32':
return {
type: 'Int32TypeAnnotation',
};
case 'Double':
return {
type: 'DoubleTypeAnnotation',
};
case 'Float':
return {
type: 'FloatTypeAnnotation',
};
case 'TSBooleanKeyword':
return {
type: 'BooleanTypeAnnotation',
};
case 'Stringish':
case 'TSStringKeyword':
return {
type: 'StringTypeAnnotation',
};
default:
throw new Error(`Unknown primitive type "${type}"`);
}
}
function getCommonTypeAnnotation(
name,
forArray,
type,
typeAnnotation,
defaultValue,
types,
parser,
buildSchema,
) {
switch (type) {
case 'TSTypeLiteral':
return buildObjectType(
typeAnnotation.members,
types,
parser,
buildSchema,
);
case 'TSInterfaceDeclaration':
return buildObjectType([typeAnnotation], types, parser, buildSchema);
case 'TSIntersectionType':
return buildObjectType(
flattenIntersectionType(typeAnnotation, types),
types,
parser,
buildSchema,
);
case 'ImageSource':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ImageSourcePrimitive',
};
case 'ImageRequest':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ImageRequestPrimitive',
};
case 'ColorValue':
case 'ProcessedColorValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ColorPrimitive',
};
case 'PointValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'PointPrimitive',
};
case 'EdgeInsetsValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'EdgeInsetsPrimitive',
};
case 'DimensionValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'DimensionPrimitive',
};
case 'TSUnionType':
return getUnionOfLiterals(
name,
forArray,
typeAnnotation.types,
defaultValue,
types,
);
case 'Int32':
case 'Double':
case 'Float':
case 'TSBooleanKeyword':
case 'Stringish':
case 'TSStringKeyword':
return getPrimitiveTypeAnnotation(type);
case 'UnsafeMixed':
return {
type: 'MixedTypeAnnotation',
};
default:
return undefined;
}
}
function getTypeAnnotationForArray(
name,
typeAnnotation,
defaultValue,
types,
parser,
buildSchema,
) {
var _extractedTypeAnnotat, _extractedTypeAnnotat2;
// unpack WithDefault, (T) or T|U
const topLevelType = parseTopLevelType(typeAnnotation, types);
if (topLevelType.defaultValue !== undefined) {
throw new Error(
'Nested optionals such as "ReadonlyArray<boolean | null | undefined>" are not supported, please declare optionals at the top level of value definitions as in "ReadonlyArray<boolean> | null | undefined"',
);
}
if (topLevelType.optional) {
throw new Error(
'Nested optionals such as "ReadonlyArray<boolean | null | undefined>" are not supported, please declare optionals at the top level of value definitions as in "ReadonlyArray<boolean> | null | undefined"',
);
}
const extractedTypeAnnotation = topLevelType.type;
const arrayType = detectArrayType(
name,
extractedTypeAnnotation,
defaultValue,
types,
parser,
buildSchema,
);
if (arrayType) {
if (arrayType.elementType.type !== 'ObjectTypeAnnotation') {
throw new Error(
`Only array of array of object is supported for "${name}".`,
);
}
return arrayType;
}
const type =
extractedTypeAnnotation.elementType === 'TSTypeReference'
? extractedTypeAnnotation.elementType.typeName.name
: ((_extractedTypeAnnotat = extractedTypeAnnotation.elementType) ===
null || _extractedTypeAnnotat === void 0
? void 0
: _extractedTypeAnnotat.type) ||
((_extractedTypeAnnotat2 = extractedTypeAnnotation.typeName) === null ||
_extractedTypeAnnotat2 === void 0
? void 0
: _extractedTypeAnnotat2.name) ||
extractedTypeAnnotation.type;
const common = getCommonTypeAnnotation(
name,
true,
type,
extractedTypeAnnotation,
defaultValue,
types,
parser,
buildSchema,
);
if (common) {
return common;
}
switch (type) {
case 'TSNumberKeyword':
return {
type: 'FloatTypeAnnotation',
};
default:
type;
throw new Error(`Unknown prop type for "${name}": ${type}`);
}
}
function setDefaultValue(common, defaultValue) {
switch (common.type) {
case 'Int32TypeAnnotation':
case 'DoubleTypeAnnotation':
common.default = defaultValue ? defaultValue : 0;
break;
case 'FloatTypeAnnotation':
common.default =
defaultValue === null ? null : defaultValue ? defaultValue : 0;
break;
case 'BooleanTypeAnnotation':
common.default = defaultValue === null ? null : !!defaultValue;
break;
case 'StringTypeAnnotation':
common.default = defaultValue === undefined ? null : defaultValue;
break;
}
}
function getTypeAnnotation(
name,
annotation,
defaultValue,
withNullDefault,
// Just to make `getTypeAnnotation` signature match with the one from Flow
types,
parser,
buildSchema,
) {
// unpack WithDefault, (T) or T|U
const topLevelType = parseTopLevelType(annotation, types);
const typeAnnotation = topLevelType.type;
const arrayType = detectArrayType(
name,
typeAnnotation,
defaultValue,
types,
parser,
buildSchema,
);
if (arrayType) {
return arrayType;
}
const type =
typeAnnotation.type === 'TSTypeReference' ||
typeAnnotation.type === 'TSTypeAliasDeclaration'
? parser.getTypeAnnotationName(typeAnnotation)
: typeAnnotation.type;
const common = getCommonTypeAnnotation(
name,
false,
type,
typeAnnotation,
defaultValue,
types,
parser,
buildSchema,
);
if (common) {
setDefaultValue(common, defaultValue);
return common;
}
switch (type) {
case 'ColorArrayValue':
return {
type: 'ArrayTypeAnnotation',
elementType: {
type: 'ReservedPropTypeAnnotation',
name: 'ColorPrimitive',
},
};
case 'TSNumberKeyword':
throw new Error(
`Cannot use "${type}" type annotation for "${name}": must use a specific numeric type like Int32, Double, or Float`,
);
case 'TSFunctionType':
throw new Error(
`Cannot use "${type}" type annotation for "${name}": must use a specific function type like BubblingEventHandler, or DirectEventHandler`,
);
default:
throw new Error(`Unknown prop type for "${name}": "${type}"`);
}
}
function getSchemaInfo(property, types) {
// unpack WithDefault, (T) or T|U
const topLevelType = parseTopLevelType(
property.typeAnnotation.typeAnnotation,
types,
);
const name = property.key.name;
if (!property.optional && topLevelType.defaultValue !== undefined) {
throw new Error(
`key ${name} must be optional if used with WithDefault<> annotation`,
);
}
return {
name,
optional: property.optional || topLevelType.optional,
typeAnnotation: topLevelType.type,
defaultValue: topLevelType.defaultValue,
withNullDefault: false, // Just to make `getTypeAnnotation` signature match with the one from Flow
};
}
function flattenProperties(typeDefinition, types, parser) {
return typeDefinition
.map(property => {
if (property.type === 'TSPropertySignature') {
return property;
} else if (property.type === 'TSTypeReference') {
return flattenProperties(
parser.getProperties(property.typeName.name, types),
types,
parser,
);
} else if (
property.type === 'TSExpressionWithTypeArguments' ||
property.type === 'TSInterfaceHeritage'
) {
return flattenProperties(
parser.getProperties(property.expression.name, types),
types,
parser,
);
} else if (property.type === 'TSTypeLiteral') {
return flattenProperties(property.members, types, parser);
} else if (property.type === 'TSInterfaceDeclaration') {
return flattenProperties(
parser.getProperties(property.id.name, types),
types,
parser,
);
} else if (property.type === 'TSIntersectionType') {
return flattenProperties(property.types, types, parser);
} else {
throw new Error(
`${property.type} is not a supported object literal type.`,
);
}
})
.filter(Boolean)
.reduce((acc, item) => {
if (Array.isArray(item)) {
item.forEach(prop => {
verifyPropNotAlreadyDefined(acc, prop);
});
return acc.concat(item);
} else {
verifyPropNotAlreadyDefined(acc, item);
acc.push(item);
return acc;
}
}, [])
.filter(Boolean);
}
module.exports = {
getSchemaInfo,
getTypeAnnotation,
getPrimitiveTypeAnnotation,
flattenProperties,
};

View File

@@ -0,0 +1,533 @@
/**
* 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 {BuildSchemaFN, Parser} from '../../parser';
import type {ASTNode, PropAST, TypeDeclarationMap} from '../../utils';
const {verifyPropNotAlreadyDefined} = require('../../parsers-commons');
const {
flattenIntersectionType,
parseTopLevelType,
} = require('../parseTopLevelType');
function getUnionOfLiterals(
name: string,
forArray: boolean,
elementTypes: $FlowFixMe[],
defaultValue: $FlowFixMe | void,
types: TypeDeclarationMap,
) {
elementTypes.reduce((lastType, currType) => {
const lastFlattenedType =
lastType && lastType.type === 'TSLiteralType'
? lastType.literal.type
: lastType.type;
const currFlattenedType =
currType.type === 'TSLiteralType' ? currType.literal.type : currType.type;
if (lastFlattenedType && currFlattenedType !== lastFlattenedType) {
throw new Error(`Mixed types are not supported (see "${name}")`);
}
return currType;
});
if (defaultValue === undefined) {
throw new Error(`A default enum value is required for "${name}"`);
}
const unionType = elementTypes[0].type;
if (
unionType === 'TSLiteralType' &&
elementTypes[0].literal?.type === 'StringLiteral'
) {
return {
type: 'StringEnumTypeAnnotation',
default: (defaultValue: string),
options: elementTypes.map(option => option.literal.value),
};
} else if (
unionType === 'TSLiteralType' &&
elementTypes[0].literal?.type === 'NumericLiteral'
) {
if (forArray) {
throw new Error(`Arrays of int enums are not supported (see: "${name}")`);
} else {
return {
type: 'Int32EnumTypeAnnotation',
default: (defaultValue: number),
options: elementTypes.map(option => option.literal.value),
};
}
} else {
throw new Error(
`Unsupported union type for "${name}", received "${
unionType === 'TSLiteralType'
? elementTypes[0].literal?.type
: unionType
}"`,
);
}
}
function detectArrayType<T>(
name: string,
typeAnnotation: $FlowFixMe | ASTNode,
defaultValue: $FlowFixMe | void,
types: TypeDeclarationMap,
parser: Parser,
buildSchema: BuildSchemaFN<T>,
): $FlowFixMe {
// Covers: readonly T[]
if (
typeAnnotation.type === 'TSTypeOperator' &&
typeAnnotation.operator === 'readonly' &&
typeAnnotation.typeAnnotation.type === 'TSArrayType'
) {
return {
type: 'ArrayTypeAnnotation',
elementType: getTypeAnnotationForArray(
name,
typeAnnotation.typeAnnotation.elementType,
defaultValue,
types,
parser,
buildSchema,
),
};
}
// Covers: T[]
if (typeAnnotation.type === 'TSArrayType') {
return {
type: 'ArrayTypeAnnotation',
elementType: getTypeAnnotationForArray(
name,
typeAnnotation.elementType,
defaultValue,
types,
parser,
buildSchema,
),
};
}
// Covers: Array<T> and ReadonlyArray<T>
if (
typeAnnotation.type === 'TSTypeReference' &&
(parser.getTypeAnnotationName(typeAnnotation) === 'ReadonlyArray' ||
parser.getTypeAnnotationName(typeAnnotation) === 'Array')
) {
return {
type: 'ArrayTypeAnnotation',
elementType: getTypeAnnotationForArray(
name,
typeAnnotation.typeParameters.params[0],
defaultValue,
types,
parser,
buildSchema,
),
};
}
return null;
}
function buildObjectType<T>(
rawProperties: Array<$FlowFixMe>,
types: TypeDeclarationMap,
parser: Parser,
buildSchema: BuildSchemaFN<T>,
): $FlowFixMe {
const flattenedProperties = flattenProperties(rawProperties, types, parser);
const properties = flattenedProperties
.map(prop => buildSchema(prop, types, parser))
.filter(Boolean);
return {
type: 'ObjectTypeAnnotation',
properties,
};
}
function getPrimitiveTypeAnnotation(type: string): $FlowFixMe {
switch (type) {
case 'Int32':
return {
type: 'Int32TypeAnnotation',
};
case 'Double':
return {
type: 'DoubleTypeAnnotation',
};
case 'Float':
return {
type: 'FloatTypeAnnotation',
};
case 'TSBooleanKeyword':
return {
type: 'BooleanTypeAnnotation',
};
case 'Stringish':
case 'TSStringKeyword':
return {
type: 'StringTypeAnnotation',
};
default:
throw new Error(`Unknown primitive type "${type}"`);
}
}
function getCommonTypeAnnotation<T>(
name: string,
forArray: boolean,
type: string,
typeAnnotation: $FlowFixMe,
defaultValue: $FlowFixMe | void,
types: TypeDeclarationMap,
parser: Parser,
buildSchema: BuildSchemaFN<T>,
): $FlowFixMe {
switch (type) {
case 'TSTypeLiteral':
return buildObjectType(
typeAnnotation.members,
types,
parser,
buildSchema,
);
case 'TSInterfaceDeclaration':
return buildObjectType([typeAnnotation], types, parser, buildSchema);
case 'TSIntersectionType':
return buildObjectType(
flattenIntersectionType(typeAnnotation, types),
types,
parser,
buildSchema,
);
case 'ImageSource':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ImageSourcePrimitive',
};
case 'ImageRequest':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ImageRequestPrimitive',
};
case 'ColorValue':
case 'ProcessedColorValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ColorPrimitive',
};
case 'PointValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'PointPrimitive',
};
case 'EdgeInsetsValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'EdgeInsetsPrimitive',
};
case 'DimensionValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'DimensionPrimitive',
};
case 'TSUnionType':
return getUnionOfLiterals(
name,
forArray,
typeAnnotation.types,
defaultValue,
types,
);
case 'Int32':
case 'Double':
case 'Float':
case 'TSBooleanKeyword':
case 'Stringish':
case 'TSStringKeyword':
return getPrimitiveTypeAnnotation(type);
case 'UnsafeMixed':
return {
type: 'MixedTypeAnnotation',
};
default:
return undefined;
}
}
function getTypeAnnotationForArray<T>(
name: string,
typeAnnotation: $FlowFixMe,
defaultValue: $FlowFixMe | void,
types: TypeDeclarationMap,
parser: Parser,
buildSchema: BuildSchemaFN<T>,
): $FlowFixMe {
// unpack WithDefault, (T) or T|U
const topLevelType = parseTopLevelType(typeAnnotation, types);
if (topLevelType.defaultValue !== undefined) {
throw new Error(
'Nested optionals such as "ReadonlyArray<boolean | null | undefined>" are not supported, please declare optionals at the top level of value definitions as in "ReadonlyArray<boolean> | null | undefined"',
);
}
if (topLevelType.optional) {
throw new Error(
'Nested optionals such as "ReadonlyArray<boolean | null | undefined>" are not supported, please declare optionals at the top level of value definitions as in "ReadonlyArray<boolean> | null | undefined"',
);
}
const extractedTypeAnnotation = topLevelType.type;
const arrayType = detectArrayType(
name,
extractedTypeAnnotation,
defaultValue,
types,
parser,
buildSchema,
);
if (arrayType) {
if (arrayType.elementType.type !== 'ObjectTypeAnnotation') {
throw new Error(
`Only array of array of object is supported for "${name}".`,
);
}
return arrayType;
}
const type =
extractedTypeAnnotation.elementType === 'TSTypeReference'
? extractedTypeAnnotation.elementType.typeName.name
: extractedTypeAnnotation.elementType?.type ||
extractedTypeAnnotation.typeName?.name ||
extractedTypeAnnotation.type;
const common = getCommonTypeAnnotation(
name,
true,
type,
extractedTypeAnnotation,
defaultValue,
types,
parser,
buildSchema,
);
if (common) {
return common;
}
switch (type) {
case 'TSNumberKeyword':
return {
type: 'FloatTypeAnnotation',
};
default:
(type: empty);
throw new Error(`Unknown prop type for "${name}": ${type}`);
}
}
function setDefaultValue(
common: $FlowFixMe,
defaultValue: $FlowFixMe | void,
): void {
switch (common.type) {
case 'Int32TypeAnnotation':
case 'DoubleTypeAnnotation':
common.default = ((defaultValue ? defaultValue : 0): number);
break;
case 'FloatTypeAnnotation':
common.default = ((defaultValue === null
? null
: defaultValue
? defaultValue
: 0): number | null);
break;
case 'BooleanTypeAnnotation':
common.default = defaultValue === null ? null : !!defaultValue;
break;
case 'StringTypeAnnotation':
common.default = ((defaultValue === undefined ? null : defaultValue):
| string
| null);
break;
}
}
function getTypeAnnotation<T>(
name: string,
annotation: $FlowFixMe | ASTNode,
defaultValue: $FlowFixMe | void,
withNullDefault: boolean, // Just to make `getTypeAnnotation` signature match with the one from Flow
types: TypeDeclarationMap,
parser: Parser,
buildSchema: BuildSchemaFN<T>,
): $FlowFixMe {
// unpack WithDefault, (T) or T|U
const topLevelType = parseTopLevelType(annotation, types);
const typeAnnotation = topLevelType.type;
const arrayType = detectArrayType(
name,
typeAnnotation,
defaultValue,
types,
parser,
buildSchema,
);
if (arrayType) {
return arrayType;
}
const type =
typeAnnotation.type === 'TSTypeReference' ||
typeAnnotation.type === 'TSTypeAliasDeclaration'
? parser.getTypeAnnotationName(typeAnnotation)
: typeAnnotation.type;
const common = getCommonTypeAnnotation(
name,
false,
type,
typeAnnotation,
defaultValue,
types,
parser,
buildSchema,
);
if (common) {
setDefaultValue(common, defaultValue);
return common;
}
switch (type) {
case 'ColorArrayValue':
return {
type: 'ArrayTypeAnnotation',
elementType: {
type: 'ReservedPropTypeAnnotation',
name: 'ColorPrimitive',
},
};
case 'TSNumberKeyword':
throw new Error(
`Cannot use "${type}" type annotation for "${name}": must use a specific numeric type like Int32, Double, or Float`,
);
case 'TSFunctionType':
throw new Error(
`Cannot use "${type}" type annotation for "${name}": must use a specific function type like BubblingEventHandler, or DirectEventHandler`,
);
default:
throw new Error(`Unknown prop type for "${name}": "${type}"`);
}
}
type SchemaInfo = {
name: string,
optional: boolean,
typeAnnotation: $FlowFixMe,
defaultValue: $FlowFixMe,
withNullDefault: boolean, // Just to make `getTypeAnnotation` signature match with the one from Flow
};
function getSchemaInfo(
property: PropAST,
types: TypeDeclarationMap,
): SchemaInfo {
// unpack WithDefault, (T) or T|U
const topLevelType = parseTopLevelType(
property.typeAnnotation.typeAnnotation,
types,
);
const name = property.key.name;
if (!property.optional && topLevelType.defaultValue !== undefined) {
throw new Error(
`key ${name} must be optional if used with WithDefault<> annotation`,
);
}
return {
name,
optional: property.optional || topLevelType.optional,
typeAnnotation: topLevelType.type,
defaultValue: topLevelType.defaultValue,
withNullDefault: false, // Just to make `getTypeAnnotation` signature match with the one from Flow
};
}
function flattenProperties(
typeDefinition: $ReadOnlyArray<PropAST>,
types: TypeDeclarationMap,
parser: Parser,
): $ReadOnlyArray<PropAST> {
return typeDefinition
.map(property => {
if (property.type === 'TSPropertySignature') {
return property;
} else if (property.type === 'TSTypeReference') {
return flattenProperties(
parser.getProperties(property.typeName.name, types),
types,
parser,
);
} else if (
property.type === 'TSExpressionWithTypeArguments' ||
property.type === 'TSInterfaceHeritage'
) {
return flattenProperties(
parser.getProperties(property.expression.name, types),
types,
parser,
);
} else if (property.type === 'TSTypeLiteral') {
return flattenProperties(property.members, types, parser);
} else if (property.type === 'TSInterfaceDeclaration') {
return flattenProperties(
parser.getProperties(property.id.name, types),
types,
parser,
);
} else if (property.type === 'TSIntersectionType') {
return flattenProperties(property.types, types, parser);
} else {
throw new Error(
`${property.type} is not a supported object literal type.`,
);
}
})
.filter(Boolean)
.reduce((acc: Array<PropAST>, item) => {
if (Array.isArray(item)) {
item.forEach((prop: PropAST) => {
verifyPropNotAlreadyDefined(acc, prop);
});
return acc.concat(item);
} else {
verifyPropNotAlreadyDefined(acc, item);
acc.push(item);
return acc;
}
}, [])
.filter(Boolean);
}
module.exports = {
getSchemaInfo,
getTypeAnnotation,
getPrimitiveTypeAnnotation,
flattenProperties,
};

View File

@@ -0,0 +1,261 @@
/**
* 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';
const _require = require('../../error-utils'),
throwIfArgumentPropsAreNull = _require.throwIfArgumentPropsAreNull,
throwIfBubblingTypeIsNull = _require.throwIfBubblingTypeIsNull,
throwIfEventHasNoName = _require.throwIfEventHasNoName;
const _require2 = require('../../parsers-commons'),
buildPropertiesForEvent = _require2.buildPropertiesForEvent,
emitBuildEventSchema = _require2.emitBuildEventSchema,
getEventArgument = _require2.getEventArgument,
handleEventHandler = _require2.handleEventHandler;
const _require3 = require('../../parsers-primitives'),
emitBoolProp = _require3.emitBoolProp,
emitDoubleProp = _require3.emitDoubleProp,
emitFloatProp = _require3.emitFloatProp,
emitInt32Prop = _require3.emitInt32Prop,
emitMixedProp = _require3.emitMixedProp,
emitObjectProp = _require3.emitObjectProp,
emitStringProp = _require3.emitStringProp,
emitUnionProp = _require3.emitUnionProp;
const _require4 = require('../parseTopLevelType'),
parseTopLevelType = _require4.parseTopLevelType;
const _require5 = require('./componentsUtils'),
flattenProperties = _require5.flattenProperties;
function getPropertyType(
/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's
* LTI update could not be added via codemod */
name,
optionalProperty,
/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's
* LTI update could not be added via codemod */
annotation,
parser,
) {
const topLevelType = parseTopLevelType(annotation);
const typeAnnotation = topLevelType.type;
const optional = optionalProperty || topLevelType.optional;
const type =
typeAnnotation.type === 'TSTypeReference'
? parser.getTypeAnnotationName(typeAnnotation)
: typeAnnotation.type;
switch (type) {
case 'TSBooleanKeyword':
return emitBoolProp(name, optional);
case 'TSStringKeyword':
return emitStringProp(name, optional);
case 'Int32':
return emitInt32Prop(name, optional);
case 'Double':
return emitDoubleProp(name, optional);
case 'Float':
return emitFloatProp(name, optional);
case 'TSTypeLiteral':
return emitObjectProp(
name,
optional,
parser,
typeAnnotation,
extractArrayElementType,
);
case 'TSUnionType':
return emitUnionProp(name, optional, parser, typeAnnotation);
case 'UnsafeMixed':
return emitMixedProp(name, optional);
case 'TSArrayType':
return {
name,
optional,
typeAnnotation: extractArrayElementType(typeAnnotation, name, parser),
};
default:
throw new Error(`Unable to determine event type for "${name}": ${type}`);
}
}
function extractArrayElementType(typeAnnotation, name, parser) {
const type = extractTypeFromTypeAnnotation(typeAnnotation, parser);
switch (type) {
case 'TSParenthesizedType':
return extractArrayElementType(
typeAnnotation.typeAnnotation,
name,
parser,
);
case 'TSBooleanKeyword':
return {
type: 'BooleanTypeAnnotation',
};
case 'TSStringKeyword':
return {
type: 'StringTypeAnnotation',
};
case 'Float':
return {
type: 'FloatTypeAnnotation',
};
case 'Int32':
return {
type: 'Int32TypeAnnotation',
};
case 'TSNumberKeyword':
case 'Double':
return {
type: 'DoubleTypeAnnotation',
};
case 'TSUnionType':
return {
type: 'StringEnumTypeAnnotation',
options: typeAnnotation.types.map(option =>
parser.getLiteralValue(option),
),
};
case 'TSTypeLiteral':
return {
type: 'ObjectTypeAnnotation',
properties: parser
.getObjectProperties(typeAnnotation)
.map(member =>
buildPropertiesForEvent(member, parser, getPropertyType),
),
};
case 'TSArrayType':
return {
type: 'ArrayTypeAnnotation',
elementType: extractArrayElementType(
typeAnnotation.elementType,
name,
parser,
),
};
default:
throw new Error(
`Unrecognized ${type} for Array ${name} in events.\n${JSON.stringify(
typeAnnotation,
null,
2,
)}`,
);
}
}
function extractTypeFromTypeAnnotation(typeAnnotation, parser) {
return typeAnnotation.type === 'TSTypeReference'
? parser.getTypeAnnotationName(typeAnnotation)
: typeAnnotation.type;
}
function findEventArgumentsAndType(
parser,
typeAnnotation,
types,
bubblingType,
paperName,
) {
if (typeAnnotation.type === 'TSInterfaceDeclaration') {
return {
argumentProps: flattenProperties([typeAnnotation], types, parser),
paperTopLevelNameDeprecated: paperName,
bubblingType,
};
}
if (typeAnnotation.type === 'TSTypeLiteral') {
return {
argumentProps: parser.getObjectProperties(typeAnnotation),
paperTopLevelNameDeprecated: paperName,
bubblingType,
};
}
throwIfEventHasNoName(typeAnnotation, parser);
const name = parser.getTypeAnnotationName(typeAnnotation);
if (name === 'Readonly') {
return findEventArgumentsAndType(
parser,
typeAnnotation.typeParameters.params[0],
types,
bubblingType,
paperName,
);
} else if (name === 'BubblingEventHandler' || name === 'DirectEventHandler') {
return handleEventHandler(
name,
typeAnnotation,
parser,
types,
findEventArgumentsAndType,
);
} else if (types[name]) {
let elementType = types[name];
if (elementType.type === 'TSTypeAliasDeclaration') {
elementType = elementType.typeAnnotation;
}
return findEventArgumentsAndType(
parser,
elementType,
types,
bubblingType,
paperName,
);
} else {
return {
argumentProps: null,
bubblingType: null,
paperTopLevelNameDeprecated: null,
};
}
}
// $FlowFixMe[unclear-type] TODO(T108222691): Use flow-types for @babel/parser
function buildEventSchema(types, property, parser) {
// unpack WithDefault, (T) or T|U
const topLevelType = parseTopLevelType(
property.typeAnnotation.typeAnnotation,
types,
);
const name = property.key.name;
const typeAnnotation = topLevelType.type;
const optional = property.optional || topLevelType.optional;
const _findEventArgumentsAn = findEventArgumentsAndType(
parser,
typeAnnotation,
types,
),
argumentProps = _findEventArgumentsAn.argumentProps,
bubblingType = _findEventArgumentsAn.bubblingType,
paperTopLevelNameDeprecated =
_findEventArgumentsAn.paperTopLevelNameDeprecated;
const nonNullableArgumentProps = throwIfArgumentPropsAreNull(
argumentProps,
name,
);
const nonNullableBubblingType = throwIfBubblingTypeIsNull(bubblingType, name);
const argument = getEventArgument(
nonNullableArgumentProps,
parser,
getPropertyType,
);
return emitBuildEventSchema(
paperTopLevelNameDeprecated,
name,
optional,
nonNullableBubblingType,
argument,
);
}
function getEvents(eventTypeAST, types, parser) {
return eventTypeAST
.map(property => buildEventSchema(types, property, parser))
.filter(Boolean);
}
module.exports = {
getEvents,
extractArrayElementType,
};

View File

@@ -0,0 +1,292 @@
/**
* 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 {
EventTypeAnnotation,
EventTypeShape,
NamedShape,
} from '../../../CodegenSchema.js';
import type {Parser} from '../../parser';
import type {TypeDeclarationMap} from '../../utils';
const {
throwIfArgumentPropsAreNull,
throwIfBubblingTypeIsNull,
throwIfEventHasNoName,
} = require('../../error-utils');
const {
buildPropertiesForEvent,
emitBuildEventSchema,
getEventArgument,
handleEventHandler,
} = require('../../parsers-commons');
const {
emitBoolProp,
emitDoubleProp,
emitFloatProp,
emitInt32Prop,
emitMixedProp,
emitObjectProp,
emitStringProp,
emitUnionProp,
} = require('../../parsers-primitives');
const {parseTopLevelType} = require('../parseTopLevelType');
const {flattenProperties} = require('./componentsUtils');
function getPropertyType(
/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's
* LTI update could not be added via codemod */
name,
optionalProperty: boolean,
/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's
* LTI update could not be added via codemod */
annotation,
parser: Parser,
): NamedShape<EventTypeAnnotation> {
const topLevelType = parseTopLevelType(annotation);
const typeAnnotation = topLevelType.type;
const optional = optionalProperty || topLevelType.optional;
const type =
typeAnnotation.type === 'TSTypeReference'
? parser.getTypeAnnotationName(typeAnnotation)
: typeAnnotation.type;
switch (type) {
case 'TSBooleanKeyword':
return emitBoolProp(name, optional);
case 'TSStringKeyword':
return emitStringProp(name, optional);
case 'Int32':
return emitInt32Prop(name, optional);
case 'Double':
return emitDoubleProp(name, optional);
case 'Float':
return emitFloatProp(name, optional);
case 'TSTypeLiteral':
return emitObjectProp(
name,
optional,
parser,
typeAnnotation,
extractArrayElementType,
);
case 'TSUnionType':
return emitUnionProp(name, optional, parser, typeAnnotation);
case 'UnsafeMixed':
return emitMixedProp(name, optional);
case 'TSArrayType':
return {
name,
optional,
typeAnnotation: extractArrayElementType(typeAnnotation, name, parser),
};
default:
throw new Error(`Unable to determine event type for "${name}": ${type}`);
}
}
function extractArrayElementType(
typeAnnotation: $FlowFixMe,
name: string,
parser: Parser,
): EventTypeAnnotation {
const type = extractTypeFromTypeAnnotation(typeAnnotation, parser);
switch (type) {
case 'TSParenthesizedType':
return extractArrayElementType(
typeAnnotation.typeAnnotation,
name,
parser,
);
case 'TSBooleanKeyword':
return {type: 'BooleanTypeAnnotation'};
case 'TSStringKeyword':
return {type: 'StringTypeAnnotation'};
case 'Float':
return {
type: 'FloatTypeAnnotation',
};
case 'Int32':
return {
type: 'Int32TypeAnnotation',
};
case 'TSNumberKeyword':
case 'Double':
return {
type: 'DoubleTypeAnnotation',
};
case 'TSUnionType':
return {
type: 'StringEnumTypeAnnotation',
options: typeAnnotation.types.map(option =>
parser.getLiteralValue(option),
),
};
case 'TSTypeLiteral':
return {
type: 'ObjectTypeAnnotation',
properties: parser
.getObjectProperties(typeAnnotation)
.map(member =>
buildPropertiesForEvent(member, parser, getPropertyType),
),
};
case 'TSArrayType':
return {
type: 'ArrayTypeAnnotation',
elementType: extractArrayElementType(
typeAnnotation.elementType,
name,
parser,
),
};
default:
throw new Error(
`Unrecognized ${type} for Array ${name} in events.\n${JSON.stringify(
typeAnnotation,
null,
2,
)}`,
);
}
}
function extractTypeFromTypeAnnotation(
typeAnnotation: $FlowFixMe,
parser: Parser,
): string {
return typeAnnotation.type === 'TSTypeReference'
? parser.getTypeAnnotationName(typeAnnotation)
: typeAnnotation.type;
}
function findEventArgumentsAndType(
parser: Parser,
typeAnnotation: $FlowFixMe,
types: TypeDeclarationMap,
bubblingType: void | 'direct' | 'bubble',
paperName: ?$FlowFixMe,
): {
argumentProps: ?$ReadOnlyArray<$FlowFixMe>,
paperTopLevelNameDeprecated: ?$FlowFixMe,
bubblingType: ?'direct' | 'bubble',
} {
if (typeAnnotation.type === 'TSInterfaceDeclaration') {
return {
argumentProps: flattenProperties([typeAnnotation], types, parser),
paperTopLevelNameDeprecated: paperName,
bubblingType,
};
}
if (typeAnnotation.type === 'TSTypeLiteral') {
return {
argumentProps: parser.getObjectProperties(typeAnnotation),
paperTopLevelNameDeprecated: paperName,
bubblingType,
};
}
throwIfEventHasNoName(typeAnnotation, parser);
const name = parser.getTypeAnnotationName(typeAnnotation);
if (name === 'Readonly') {
return findEventArgumentsAndType(
parser,
typeAnnotation.typeParameters.params[0],
types,
bubblingType,
paperName,
);
} else if (name === 'BubblingEventHandler' || name === 'DirectEventHandler') {
return handleEventHandler(
name,
typeAnnotation,
parser,
types,
findEventArgumentsAndType,
);
} else if (types[name]) {
let elementType = types[name];
if (elementType.type === 'TSTypeAliasDeclaration') {
elementType = elementType.typeAnnotation;
}
return findEventArgumentsAndType(
parser,
elementType,
types,
bubblingType,
paperName,
);
} else {
return {
argumentProps: null,
bubblingType: null,
paperTopLevelNameDeprecated: null,
};
}
}
// $FlowFixMe[unclear-type] TODO(T108222691): Use flow-types for @babel/parser
type EventTypeAST = Object;
function buildEventSchema(
types: TypeDeclarationMap,
property: EventTypeAST,
parser: Parser,
): ?EventTypeShape {
// unpack WithDefault, (T) or T|U
const topLevelType = parseTopLevelType(
property.typeAnnotation.typeAnnotation,
types,
);
const name = property.key.name;
const typeAnnotation = topLevelType.type;
const optional = property.optional || topLevelType.optional;
const {argumentProps, bubblingType, paperTopLevelNameDeprecated} =
findEventArgumentsAndType(parser, typeAnnotation, types);
const nonNullableArgumentProps = throwIfArgumentPropsAreNull(
argumentProps,
name,
);
const nonNullableBubblingType = throwIfBubblingTypeIsNull(bubblingType, name);
const argument = getEventArgument(
nonNullableArgumentProps,
parser,
getPropertyType,
);
return emitBuildEventSchema(
paperTopLevelNameDeprecated,
name,
optional,
nonNullableBubblingType,
argument,
);
}
function getEvents(
eventTypeAST: $ReadOnlyArray<EventTypeAST>,
types: TypeDeclarationMap,
parser: Parser,
): $ReadOnlyArray<EventTypeShape> {
return eventTypeAST
.map(property => buildEventSchema(types, property, parser))
.filter(Boolean);
}
module.exports = {
getEvents,
extractArrayElementType,
};

View File

@@ -0,0 +1,41 @@
/**
* 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';
const _require = require('../parseTopLevelType'),
parseTopLevelType = _require.parseTopLevelType;
function isEvent(typeAnnotation) {
if (typeAnnotation.type !== 'TSTypeReference') {
return false;
}
const eventNames = new Set(['BubblingEventHandler', 'DirectEventHandler']);
return eventNames.has(typeAnnotation.typeName.name);
}
// $FlowFixMe[unclear-type] TODO(T108222691): Use flow-types for @babel/parser
function categorizeProps(typeDefinition, types, events) {
// find events
for (const prop of typeDefinition) {
if (prop.type === 'TSPropertySignature') {
const topLevelType = parseTopLevelType(
prop.typeAnnotation.typeAnnotation,
types,
);
if (isEvent(topLevelType.type)) {
events.push(prop);
}
}
}
}
module.exports = {
categorizeProps,
};

View File

@@ -0,0 +1,50 @@
/**
* 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 {TypeDeclarationMap} from '../../utils';
const {parseTopLevelType} = require('../parseTopLevelType');
function isEvent(typeAnnotation: $FlowFixMe): boolean {
if (typeAnnotation.type !== 'TSTypeReference') {
return false;
}
const eventNames = new Set(['BubblingEventHandler', 'DirectEventHandler']);
return eventNames.has(typeAnnotation.typeName.name);
}
// $FlowFixMe[unclear-type] TODO(T108222691): Use flow-types for @babel/parser
type PropsAST = Object;
function categorizeProps(
typeDefinition: $ReadOnlyArray<PropsAST>,
types: TypeDeclarationMap,
events: Array<PropsAST>,
): void {
// find events
for (const prop of typeDefinition) {
if (prop.type === 'TSPropertySignature') {
const topLevelType = parseTopLevelType(
prop.typeAnnotation.typeAnnotation,
types,
);
if (isEvent(topLevelType.type)) {
events.push(prop);
}
}
}
}
module.exports = {
categorizeProps,
};

View File

@@ -0,0 +1,55 @@
/**
* 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';
const _require = require('../../parsers-commons'),
findComponentConfig = _require.findComponentConfig,
getCommandProperties = _require.getCommandProperties,
getOptions = _require.getOptions;
const _require2 = require('./commands'),
getCommands = _require2.getCommands;
const _require3 = require('./events'),
getEvents = _require3.getEvents;
const _require4 = require('./extends'),
categorizeProps = _require4.categorizeProps;
// $FlowFixMe[unclear-type] TODO(T108222691): Use flow-types for @babel/parser
// $FlowFixMe[signature-verification-failure] TODO(T108222691): Use flow-types for @babel/parser
function buildComponentSchema(ast, parser) {
const _findComponentConfig = findComponentConfig(ast, parser),
componentName = _findComponentConfig.componentName,
propsTypeName = _findComponentConfig.propsTypeName,
optionsExpression = _findComponentConfig.optionsExpression;
const types = parser.getTypes(ast);
const propProperties = parser.getProperties(propsTypeName, types);
const commandProperties = getCommandProperties(ast, parser);
const options = getOptions(optionsExpression);
const componentEventAsts = [];
categorizeProps(propProperties, types, componentEventAsts);
const _parser$getProps = parser.getProps(propProperties, types),
props = _parser$getProps.props,
extendsProps = _parser$getProps.extendsProps;
const events = getEvents(componentEventAsts, types, parser);
const commands = getCommands(commandProperties, types);
return {
filename: componentName,
componentName,
options,
extendsProps,
events,
props,
commands,
};
}
module.exports = {
buildComponentSchema,
};

View File

@@ -0,0 +1,64 @@
/**
* 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 {Parser} from '../../parser';
import type {ComponentSchemaBuilderConfig} from '../../schema.js';
const {
findComponentConfig,
getCommandProperties,
getOptions,
} = require('../../parsers-commons');
const {getCommands} = require('./commands');
const {getEvents} = require('./events');
const {categorizeProps} = require('./extends');
// $FlowFixMe[unclear-type] TODO(T108222691): Use flow-types for @babel/parser
type PropsAST = Object;
// $FlowFixMe[signature-verification-failure] TODO(T108222691): Use flow-types for @babel/parser
function buildComponentSchema(
ast: $FlowFixMe,
parser: Parser,
): ComponentSchemaBuilderConfig {
const {componentName, propsTypeName, optionsExpression} = findComponentConfig(
ast,
parser,
);
const types = parser.getTypes(ast);
const propProperties = parser.getProperties(propsTypeName, types);
const commandProperties = getCommandProperties(ast, parser);
const options = getOptions(optionsExpression);
const componentEventAsts: Array<PropsAST> = [];
categorizeProps(propProperties, types, componentEventAsts);
const {props, extendsProps} = parser.getProps(propProperties, types);
const events = getEvents(componentEventAsts, types, parser);
const commands = getCommands(commandProperties, types);
return {
filename: componentName,
componentName,
options,
extendsProps,
events,
props,
commands,
};
}
module.exports = {
buildComponentSchema,
};

View File

@@ -0,0 +1,212 @@
/**
* 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
*/
// @licenselint-loose-mode
const NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
getString: (arg: string) => Array;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT_AS_PARAM = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
getString: (arg: Array) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_NOT_ONLY_METHODS = `
/**
* 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
*/
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getBool: (arg: boolean) => boolean;
readonly getNumber: (arg: number) => number;
readonly getString: (arg: string) => string;
sampleBool: boolean,
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_UNNAMED_PARAMS = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getBool: (boolean) => boolean;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_PROMISE_WITHOUT_TYPE = `
/**
* 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
*/
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getBool: (arg: boolean) => Promise;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT = `
/**
* 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
*/
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export default TurboModuleRegistry.getEnforcing<Spec1>('SampleTurboModule1');
export default TurboModuleRegistry.getEnforcing<Spec2>('SampleTurboModule2');
`;
const TWO_NATIVE_EXTENDING_TURBO_MODULE = `
/**
* 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
*/
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getSth: (a: number | null | undefined) => void;
}
export interface Spec2 extends TurboModule {
readonly getSth: (a: number | null | undefined) => void;
}
`;
const EMPTY_ENUM_NATIVE_MODULE = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export enum SomeEnum {
}
export interface Spec extends TurboModule {
readonly getEnums: (a: SomeEnum) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>(
'EmptyEnumNativeModule',
);
`;
const MIXED_VALUES_ENUM_NATIVE_MODULE = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export enum SomeEnum {
NUM = 1,
STR = 'str',
}
export interface Spec extends TurboModule {
readonly getEnums: (a: SomeEnum) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>(
'MixedValuesEnumNativeModule',
);
`;
module.exports = {
NATIVE_MODULES_WITH_UNNAMED_PARAMS,
NATIVE_MODULES_WITH_PROMISE_WITHOUT_TYPE,
NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT_AS_PARAM,
NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT,
TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT,
NATIVE_MODULES_WITH_NOT_ONLY_METHODS,
TWO_NATIVE_EXTENDING_TURBO_MODULE,
EMPTY_ENUM_NATIVE_MODULE,
MIXED_VALUES_ENUM_NATIVE_MODULE,
};

View File

@@ -0,0 +1,221 @@
/**
* 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
*/
// @licenselint-loose-mode
const NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
getString: (arg: string) => Array;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT_AS_PARAM = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
getString: (arg: Array) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_NOT_ONLY_METHODS = `
/**
* 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
*/
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getBool: (arg: boolean) => boolean;
readonly getNumber: (arg: number) => number;
readonly getString: (arg: string) => string;
sampleBool: boolean,
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_UNNAMED_PARAMS = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getBool: (boolean) => boolean;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_PROMISE_WITHOUT_TYPE = `
/**
* 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
*/
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getBool: (arg: boolean) => Promise;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT = `
/**
* 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
*/
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export default TurboModuleRegistry.getEnforcing<Spec1>('SampleTurboModule1');
export default TurboModuleRegistry.getEnforcing<Spec2>('SampleTurboModule2');
`;
const TWO_NATIVE_EXTENDING_TURBO_MODULE = `
/**
* 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
*/
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getSth: (a: number | null | undefined) => void;
}
export interface Spec2 extends TurboModule {
readonly getSth: (a: number | null | undefined) => void;
}
`;
const EMPTY_ENUM_NATIVE_MODULE = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export enum SomeEnum {
}
export interface Spec extends TurboModule {
readonly getEnums: (a: SomeEnum) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>(
'EmptyEnumNativeModule',
);
`;
const MIXED_VALUES_ENUM_NATIVE_MODULE = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export enum SomeEnum {
NUM = 1,
STR = 'str',
}
export interface Spec extends TurboModule {
readonly getEnums: (a: SomeEnum) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>(
'MixedValuesEnumNativeModule',
);
`;
module.exports = {
NATIVE_MODULES_WITH_UNNAMED_PARAMS,
NATIVE_MODULES_WITH_PROMISE_WITHOUT_TYPE,
NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT_AS_PARAM,
NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT,
TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT,
NATIVE_MODULES_WITH_NOT_ONLY_METHODS,
TWO_NATIVE_EXTENDING_TURBO_MODULE,
EMPTY_ENUM_NATIVE_MODULE,
MIXED_VALUES_ENUM_NATIVE_MODULE,
};

View File

@@ -0,0 +1,856 @@
/**
* 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
*/
// @licenselint-loose-mode
const EMPTY_NATIVE_MODULE = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_COMPLEX_OBJECTS = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export type String = string;
export interface Spec extends TurboModule {
// Exported methods.
readonly getObject: (arg: {const1: {const1: boolean}}) => {
const1: {const1: boolean},
};
readonly getReadOnlyObject: (arg: Readonly<{const1: Readonly<{const1: boolean}>}>) => Readonly<{
const1: {const1: boolean},
}>;
readonly getObject2: (arg: { a: String }) => Object;
readonly getObjectInArray: (arg: {const1: {const1: boolean}}) => Array<{
const1: {const1: boolean},
}>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_COMPLEX_OBJECTS_WITH_NULLABLE_KEY = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export type String = string;
export interface Spec extends TurboModule {
readonly getConstants: () => {
isTesting: boolean;
reactNativeVersion: {
major: number;
minor: number;
patch?: number;
prerelease: number | null | undefined;
};
forceTouchAvailable: boolean;
osVersion: string;
systemName: string;
interfaceIdiom: string;
};
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_BASIC_PARAM_TYPES = `
/**
* 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
*/
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly passBool?: (arg: boolean) => void;
readonly passNumber: (arg: number) => void;
readonly passString: (arg: string) => void;
readonly passStringish: (arg: Stringish) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ALIASES = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
type NumNum = number;
export type Num = (arg: NumNum) => void;
type Num2 = Num;
export type Void = void;
export type A = number;
export type B = number;
export type ObjectAlias = {
x: number;
y: number;
label: string;
truthy: boolean;
};
export type ReadOnlyAlias = Readonly<ObjectAlias>;
export interface Spec extends TurboModule {
// Exported methods.
readonly getNumber: Num2;
readonly getVoid: () => Void;
readonly getArray: (a: Array<A>) => {a: B};
readonly getStringFromAlias: (a: ObjectAlias) => string;
readonly getStringFromNullableAlias: (a: ObjectAlias | null) => string;
readonly getStringFromReadOnlyAlias: (a: ReadOnlyAlias) => string;
readonly getStringFromNullableReadOnlyAlias: (a: ReadOnlyAlias | null) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_NESTED_ALIASES = `
/**
* 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
*/
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
type Bar = {
z: number
};
type Foo = {
bar1: Bar,
bar2: Bar,
};
export interface Spec extends TurboModule {
// Exported methods.
foo1: (x: Foo) => Foo;
foo2: (x: Foo) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_NESTED_INTERFACES = `
/**
* 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
*/
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
interface Bar {
z: number
};
interface Base1 {
bar1: Bar,
}
interface Base2 {
bar2: Bar,
}
interface Base3 extends Base2 {
bar3: Bar,
}
interface Foo extends Base1, Base3 {
bar4: Bar,
};
export interface Spec extends TurboModule {
// Exported methods.
foo1: (x: Foo) => Foo;
foo2: (x: Foo) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_INTERSECTION_TYPES = `
/**
* 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
*/
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
type Bar = {
z: number
};
type Base1 = {
bar1: Bar,
}
type Base2 = {
bar2: Bar,
}
type Base3 = Base2 & {
bar3: Bar,
}
type Foo = Base1 & Base3 & {
bar4: Bar,
};
export interface Spec extends TurboModule {
// Exported methods.
foo1: (x: Foo) => Foo;
foo2: (x: Foo) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_FLOAT_AND_INT32 = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import type {Int32, Float} from 'react-native/Libraries/Types/CodegenTypes';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getInt: (arg: Int32) => Int32;
readonly getFloat: (arg: Float) => Float;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_SIMPLE_OBJECT = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getObject: (o: Object) => Object,
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_UNSAFE_OBJECT = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
import type {UnsafeObject} from 'react-native/Libraries/Types/CodegenTypes';
export interface Spec extends TurboModule {
readonly getUnsafeObject: (o: UnsafeObject) => UnsafeObject;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_PARTIALS = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export type SomeObj = {
a: string,
b?: boolean,
};
export interface Spec extends TurboModule {
getSomeObj: () => SomeObj;
getPartialSomeObj: () => Partial<SomeObj>;
getSomeObjFromPartialSomeObj: (value: Partial<SomeObj>) => SomeObj;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_PARTIALS_COMPLEX = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export type SomeObj = {
a: string,
b?: boolean,
};
export type PartialSomeObj = Partial<SomeObj>;
export interface Spec extends TurboModule {
getPartialPartial: (value1: Partial<SomeObj>, value2: PartialSomeObj) => SomeObj;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ROOT_TAG = `
/**
* 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
*/
import type {
TurboModule,
RootTag,
} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getRootTag: (rootTag: RootTag) => RootTag;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_NULLABLE_PARAM = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly voidFunc: (arg: string | null | undefined) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_BASIC_ARRAY = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getArray: (arg: Array<string>) => (Array<(string)>);
readonly getArray: (arg: ReadonlyArray<string>) => (ReadonlyArray<(string)>);
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_BASIC_ARRAY2 = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getArray: (arg: string[]) => ((string)[]);
readonly getArray: (arg: readonly string[]) => (readonly (string)[]);
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_OBJECT_WITH_OBJECT_DEFINED_IN_FILE_AS_PROPERTY = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
type DisplayMetricsAndroid = {
width: number;
};
export interface Spec extends TurboModule {
readonly getConstants: () => {
readonly Dimensions: {
windowPhysicalPixels: DisplayMetricsAndroid;
};
};
readonly getConstants2: () => Readonly<{
readonly Dimensions: {
windowPhysicalPixels: DisplayMetricsAndroid;
};
}>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ARRAY_WITH_UNION_AND_TOUPLE = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getArray: (
arg: Array<[string, string]>,
) => Array<string | number | boolean>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ARRAY2_WITH_UNION_AND_TOUPLE = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
getArray(
arg: [string, string][],
): (string | number | boolean)[];
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ARRAY_WITH_ALIAS = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export type SomeString = string;
export interface Spec extends TurboModule {
readonly getArray: (arg: Array<SomeString>) => Array<string>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ARRAY2_WITH_ALIAS = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export type SomeString = string;
export interface Spec extends TurboModule {
readonly getArray: (arg: SomeString[]) => string[];
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_COMPLEX_ARRAY = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getArray: (
arg: Array<Array<Array<Array<Array<string>>>>>,
) => Array<Array<Array<string>>>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_COMPLEX_ARRAY2 = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getArray: (
arg: string[][][][][],
) => string[][][];
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_PROMISE = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export type String = string;
export type SomeObj = { a: string };
export interface Spec extends TurboModule {
readonly getValueWithPromise: () => Promise<string>;
readonly getValueWithPromiseDefinedSomewhereElse: () => Promise<String>;
readonly getValueWithPromiseObjDefinedSomewhereElse: () => Promise<SomeObj>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_CALLBACK = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getValueWithCallback: (
callback: (value: string, arr: Array<Array<string>>) => void,
) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_UNION = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export type ChooseInt = 1 | 2 | 3;
export type ChooseFloat = 1.44 | 2.88 | 5.76;
export type ChooseObject = {} | {low: string};
export type ChooseString = 'One' | 'Two' | 'Three';
export interface Spec extends TurboModule {
readonly getUnion: (chooseInt: ChooseInt, chooseFloat: ChooseFloat, chooseObject: ChooseObject, chooseString: ChooseString) => ChooseObject;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const ANDROID_ONLY_NATIVE_MODULE = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {}
export default TurboModuleRegistry.getEnforcing<Spec>(
'SampleTurboModuleAndroid',
);
`;
const IOS_ONLY_NATIVE_MODULE = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export enum Quality {
SD,
HD,
}
export enum Resolution {
Low = 720,
High = 1080,
}
export enum Floppy {
LowDensity = 0.72,
HighDensity = 1.44,
}
export enum StringOptions {
One = 'one',
Two = 'two',
Three = 'three',
}
export interface Spec extends TurboModule {
readonly getEnums: (quality: Quality, resolution?: Resolution, floppy: Floppy, stringOptions: StringOptions) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>(
'SampleTurboModuleIOS',
);
`;
const CXX_ONLY_NATIVE_MODULE = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export enum Quality {
SD,
HD,
}
export enum Resolution {
Low = 720,
High = 1080,
}
export enum Floppy {
LowDensity = 0.72,
HighDensity = 1.44,
}
export enum StringOptions {
One = 'one',
Two = 'two',
Three = 'three',
}
export type ChooseInt = 1 | 2 | 3;
export type ChooseFloat = 1.44 | 2.88 | 5.76;
export type ChooseObject = {} | {low: string};
export type ChooseString = 'One' | 'Two' | 'Three';
export type BinaryTreeNode = {
left?: BinaryTreeNode,
value: number,
right?: BinaryTreeNode,
};
export type GraphNode = {
label: string,
neighbors?: Array<GraphNode>,
};
export interface Spec extends TurboModule {
readonly getCallback: () => () => void;
readonly getMixed: (arg: unknown) => unknown;
readonly getEnums: (quality: Quality, resolution?: Resolution, floppy: Floppy, stringOptions: StringOptions) => string;
readonly getBinaryTreeNode: (arg: BinaryTreeNode) => BinaryTreeNode;
readonly getGraphNode: (arg: GraphNode) => GraphNode;
readonly getMap: (arg: {[a: string]: number | null;}) => {[b: string]: number | null;};
readonly getAnotherMap: (arg: {[key: string]: string}) => {[key: string]: string};
readonly getUnion: (chooseInt: ChooseInt, chooseFloat: ChooseFloat, chooseObject: ChooseObject, chooseString: ChooseString) => ChooseObject;
}
export default TurboModuleRegistry.getEnforcing<Spec>(
'SampleTurboModuleCxx',
);
`;
module.exports = {
NATIVE_MODULE_WITH_OBJECT_WITH_OBJECT_DEFINED_IN_FILE_AS_PROPERTY,
NATIVE_MODULE_WITH_ARRAY_WITH_UNION_AND_TOUPLE,
NATIVE_MODULE_WITH_ARRAY2_WITH_UNION_AND_TOUPLE,
NATIVE_MODULE_WITH_FLOAT_AND_INT32,
NATIVE_MODULE_WITH_ALIASES,
NATIVE_MODULE_WITH_NESTED_ALIASES,
NATIVE_MODULE_WITH_NESTED_INTERFACES,
NATIVE_MODULE_WITH_INTERSECTION_TYPES,
NATIVE_MODULE_WITH_PROMISE,
NATIVE_MODULE_WITH_COMPLEX_OBJECTS,
NATIVE_MODULE_WITH_COMPLEX_OBJECTS_WITH_NULLABLE_KEY,
NATIVE_MODULE_WITH_SIMPLE_OBJECT,
NATIVE_MODULE_WITH_UNSAFE_OBJECT,
NATIVE_MODULE_WITH_PARTIALS,
NATIVE_MODULE_WITH_PARTIALS_COMPLEX,
NATIVE_MODULE_WITH_ROOT_TAG,
NATIVE_MODULE_WITH_NULLABLE_PARAM,
NATIVE_MODULE_WITH_BASIC_ARRAY,
NATIVE_MODULE_WITH_BASIC_ARRAY2,
NATIVE_MODULE_WITH_COMPLEX_ARRAY,
NATIVE_MODULE_WITH_COMPLEX_ARRAY2,
NATIVE_MODULE_WITH_ARRAY_WITH_ALIAS,
NATIVE_MODULE_WITH_ARRAY2_WITH_ALIAS,
NATIVE_MODULE_WITH_BASIC_PARAM_TYPES,
NATIVE_MODULE_WITH_CALLBACK,
NATIVE_MODULE_WITH_UNION,
EMPTY_NATIVE_MODULE,
ANDROID_ONLY_NATIVE_MODULE,
IOS_ONLY_NATIVE_MODULE,
CXX_ONLY_NATIVE_MODULE,
};

View File

@@ -0,0 +1,886 @@
/**
* 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
*/
// @licenselint-loose-mode
const EMPTY_NATIVE_MODULE = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_COMPLEX_OBJECTS = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export type String = string;
export interface Spec extends TurboModule {
// Exported methods.
readonly getObject: (arg: {const1: {const1: boolean}}) => {
const1: {const1: boolean},
};
readonly getReadOnlyObject: (arg: Readonly<{const1: Readonly<{const1: boolean}>}>) => Readonly<{
const1: {const1: boolean},
}>;
readonly getObject2: (arg: { a: String }) => Object;
readonly getObjectInArray: (arg: {const1: {const1: boolean}}) => Array<{
const1: {const1: boolean},
}>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_COMPLEX_OBJECTS_WITH_NULLABLE_KEY = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export type String = string;
export interface Spec extends TurboModule {
readonly getConstants: () => {
isTesting: boolean;
reactNativeVersion: {
major: number;
minor: number;
patch?: number;
prerelease: number | null | undefined;
};
forceTouchAvailable: boolean;
osVersion: string;
systemName: string;
interfaceIdiom: string;
};
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_BASIC_PARAM_TYPES = `
/**
* 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
*/
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly passBool?: (arg: boolean) => void;
readonly passNumber: (arg: number) => void;
readonly passString: (arg: string) => void;
readonly passStringish: (arg: Stringish) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ALIASES = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
type NumNum = number;
export type Num = (arg: NumNum) => void;
type Num2 = Num;
export type Void = void;
export type A = number;
export type B = number;
export type ObjectAlias = {
x: number;
y: number;
label: string;
truthy: boolean;
};
export type ReadOnlyAlias = Readonly<ObjectAlias>;
export interface Spec extends TurboModule {
// Exported methods.
readonly getNumber: Num2;
readonly getVoid: () => Void;
readonly getArray: (a: Array<A>) => {a: B};
readonly getStringFromAlias: (a: ObjectAlias) => string;
readonly getStringFromNullableAlias: (a: ObjectAlias | null) => string;
readonly getStringFromReadOnlyAlias: (a: ReadOnlyAlias) => string;
readonly getStringFromNullableReadOnlyAlias: (a: ReadOnlyAlias | null) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_NESTED_ALIASES = `
/**
* 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
*/
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
type Bar = {
z: number
};
type Foo = {
bar1: Bar,
bar2: Bar,
};
export interface Spec extends TurboModule {
// Exported methods.
foo1: (x: Foo) => Foo;
foo2: (x: Foo) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_NESTED_INTERFACES = `
/**
* 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
*/
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
interface Bar {
z: number
};
interface Base1 {
bar1: Bar,
}
interface Base2 {
bar2: Bar,
}
interface Base3 extends Base2 {
bar3: Bar,
}
interface Foo extends Base1, Base3 {
bar4: Bar,
};
export interface Spec extends TurboModule {
// Exported methods.
foo1: (x: Foo) => Foo;
foo2: (x: Foo) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_INTERSECTION_TYPES = `
/**
* 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
*/
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
type Bar = {
z: number
};
type Base1 = {
bar1: Bar,
}
type Base2 = {
bar2: Bar,
}
type Base3 = Base2 & {
bar3: Bar,
}
type Foo = Base1 & Base3 & {
bar4: Bar,
};
export interface Spec extends TurboModule {
// Exported methods.
foo1: (x: Foo) => Foo;
foo2: (x: Foo) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_FLOAT_AND_INT32 = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import type {Int32, Float} from 'react-native/Libraries/Types/CodegenTypes';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getInt: (arg: Int32) => Int32;
readonly getFloat: (arg: Float) => Float;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_SIMPLE_OBJECT = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getObject: (o: Object) => Object,
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_UNSAFE_OBJECT = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
import type {UnsafeObject} from 'react-native/Libraries/Types/CodegenTypes';
export interface Spec extends TurboModule {
readonly getUnsafeObject: (o: UnsafeObject) => UnsafeObject;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_PARTIALS = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export type SomeObj = {
a: string,
b?: boolean,
};
export interface Spec extends TurboModule {
getSomeObj: () => SomeObj;
getPartialSomeObj: () => Partial<SomeObj>;
getSomeObjFromPartialSomeObj: (value: Partial<SomeObj>) => SomeObj;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_PARTIALS_COMPLEX = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export type SomeObj = {
a: string,
b?: boolean,
};
export type PartialSomeObj = Partial<SomeObj>;
export interface Spec extends TurboModule {
getPartialPartial: (value1: Partial<SomeObj>, value2: PartialSomeObj) => SomeObj;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ROOT_TAG = `
/**
* 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
*/
import type {
TurboModule,
RootTag,
} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getRootTag: (rootTag: RootTag) => RootTag;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_NULLABLE_PARAM = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly voidFunc: (arg: string | null | undefined) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_BASIC_ARRAY = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getArray: (arg: Array<string>) => (Array<(string)>);
readonly getArray: (arg: ReadonlyArray<string>) => (ReadonlyArray<(string)>);
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_BASIC_ARRAY2 = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getArray: (arg: string[]) => ((string)[]);
readonly getArray: (arg: readonly string[]) => (readonly (string)[]);
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_OBJECT_WITH_OBJECT_DEFINED_IN_FILE_AS_PROPERTY = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
type DisplayMetricsAndroid = {
width: number;
};
export interface Spec extends TurboModule {
readonly getConstants: () => {
readonly Dimensions: {
windowPhysicalPixels: DisplayMetricsAndroid;
};
};
readonly getConstants2: () => Readonly<{
readonly Dimensions: {
windowPhysicalPixels: DisplayMetricsAndroid;
};
}>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ARRAY_WITH_UNION_AND_TOUPLE = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getArray: (
arg: Array<[string, string]>,
) => Array<string | number | boolean>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ARRAY2_WITH_UNION_AND_TOUPLE = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
getArray(
arg: [string, string][],
): (string | number | boolean)[];
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ARRAY_WITH_ALIAS = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export type SomeString = string;
export interface Spec extends TurboModule {
readonly getArray: (arg: Array<SomeString>) => Array<string>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ARRAY2_WITH_ALIAS = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export type SomeString = string;
export interface Spec extends TurboModule {
readonly getArray: (arg: SomeString[]) => string[];
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_COMPLEX_ARRAY = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getArray: (
arg: Array<Array<Array<Array<Array<string>>>>>,
) => Array<Array<Array<string>>>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_COMPLEX_ARRAY2 = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getArray: (
arg: string[][][][][],
) => string[][][];
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_PROMISE = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export type String = string;
export type SomeObj = { a: string };
export interface Spec extends TurboModule {
readonly getValueWithPromise: () => Promise<string>;
readonly getValueWithPromiseDefinedSomewhereElse: () => Promise<String>;
readonly getValueWithPromiseObjDefinedSomewhereElse: () => Promise<SomeObj>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_CALLBACK = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly getValueWithCallback: (
callback: (value: string, arr: Array<Array<string>>) => void,
) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_UNION = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export type ChooseInt = 1 | 2 | 3;
export type ChooseFloat = 1.44 | 2.88 | 5.76;
export type ChooseObject = {} | {low: string};
export type ChooseString = 'One' | 'Two' | 'Three';
export interface Spec extends TurboModule {
readonly getUnion: (chooseInt: ChooseInt, chooseFloat: ChooseFloat, chooseObject: ChooseObject, chooseString: ChooseString) => ChooseObject;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const ANDROID_ONLY_NATIVE_MODULE = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {}
export default TurboModuleRegistry.getEnforcing<Spec>(
'SampleTurboModuleAndroid',
);
`;
const IOS_ONLY_NATIVE_MODULE = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export enum Quality {
SD,
HD,
}
export enum Resolution {
Low = 720,
High = 1080,
}
export enum Floppy {
LowDensity = 0.72,
HighDensity = 1.44,
}
export enum StringOptions {
One = 'one',
Two = 'two',
Three = 'three',
}
export interface Spec extends TurboModule {
readonly getEnums: (quality: Quality, resolution?: Resolution, floppy: Floppy, stringOptions: StringOptions) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>(
'SampleTurboModuleIOS',
);
`;
const CXX_ONLY_NATIVE_MODULE = `
/**
* 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
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export enum Quality {
SD,
HD,
}
export enum Resolution {
Low = 720,
High = 1080,
}
export enum Floppy {
LowDensity = 0.72,
HighDensity = 1.44,
}
export enum StringOptions {
One = 'one',
Two = 'two',
Three = 'three',
}
export type ChooseInt = 1 | 2 | 3;
export type ChooseFloat = 1.44 | 2.88 | 5.76;
export type ChooseObject = {} | {low: string};
export type ChooseString = 'One' | 'Two' | 'Three';
export type BinaryTreeNode = {
left?: BinaryTreeNode,
value: number,
right?: BinaryTreeNode,
};
export type GraphNode = {
label: string,
neighbors?: Array<GraphNode>,
};
export interface Spec extends TurboModule {
readonly getCallback: () => () => void;
readonly getMixed: (arg: unknown) => unknown;
readonly getEnums: (quality: Quality, resolution?: Resolution, floppy: Floppy, stringOptions: StringOptions) => string;
readonly getBinaryTreeNode: (arg: BinaryTreeNode) => BinaryTreeNode;
readonly getGraphNode: (arg: GraphNode) => GraphNode;
readonly getMap: (arg: {[a: string]: number | null;}) => {[b: string]: number | null;};
readonly getAnotherMap: (arg: {[key: string]: string}) => {[key: string]: string};
readonly getUnion: (chooseInt: ChooseInt, chooseFloat: ChooseFloat, chooseObject: ChooseObject, chooseString: ChooseString) => ChooseObject;
}
export default TurboModuleRegistry.getEnforcing<Spec>(
'SampleTurboModuleCxx',
);
`;
module.exports = {
NATIVE_MODULE_WITH_OBJECT_WITH_OBJECT_DEFINED_IN_FILE_AS_PROPERTY,
NATIVE_MODULE_WITH_ARRAY_WITH_UNION_AND_TOUPLE,
NATIVE_MODULE_WITH_ARRAY2_WITH_UNION_AND_TOUPLE,
NATIVE_MODULE_WITH_FLOAT_AND_INT32,
NATIVE_MODULE_WITH_ALIASES,
NATIVE_MODULE_WITH_NESTED_ALIASES,
NATIVE_MODULE_WITH_NESTED_INTERFACES,
NATIVE_MODULE_WITH_INTERSECTION_TYPES,
NATIVE_MODULE_WITH_PROMISE,
NATIVE_MODULE_WITH_COMPLEX_OBJECTS,
NATIVE_MODULE_WITH_COMPLEX_OBJECTS_WITH_NULLABLE_KEY,
NATIVE_MODULE_WITH_SIMPLE_OBJECT,
NATIVE_MODULE_WITH_UNSAFE_OBJECT,
NATIVE_MODULE_WITH_PARTIALS,
NATIVE_MODULE_WITH_PARTIALS_COMPLEX,
NATIVE_MODULE_WITH_ROOT_TAG,
NATIVE_MODULE_WITH_NULLABLE_PARAM,
NATIVE_MODULE_WITH_BASIC_ARRAY,
NATIVE_MODULE_WITH_BASIC_ARRAY2,
NATIVE_MODULE_WITH_COMPLEX_ARRAY,
NATIVE_MODULE_WITH_COMPLEX_ARRAY2,
NATIVE_MODULE_WITH_ARRAY_WITH_ALIAS,
NATIVE_MODULE_WITH_ARRAY2_WITH_ALIAS,
NATIVE_MODULE_WITH_BASIC_PARAM_TYPES,
NATIVE_MODULE_WITH_CALLBACK,
NATIVE_MODULE_WITH_UNION,
EMPTY_NATIVE_MODULE,
ANDROID_ONLY_NATIVE_MODULE,
IOS_ONLY_NATIVE_MODULE,
CXX_ONLY_NATIVE_MODULE,
};

View File

@@ -0,0 +1,388 @@
/**
* 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';
const _require = require('../../errors'),
UnsupportedGenericParserError = _require.UnsupportedGenericParserError,
UnsupportedTypeAnnotationParserError =
_require.UnsupportedTypeAnnotationParserError;
const _require2 = require('../../parsers-commons'),
parseObjectProperty = _require2.parseObjectProperty;
const _require3 = require('../../parsers-primitives'),
emitArrayType = _require3.emitArrayType,
emitCommonTypes = _require3.emitCommonTypes,
emitDictionary = _require3.emitDictionary,
emitFunction = _require3.emitFunction,
emitPromise = _require3.emitPromise,
emitRootTag = _require3.emitRootTag,
emitUnion = _require3.emitUnion,
translateArrayTypeAnnotation = _require3.translateArrayTypeAnnotation,
typeAliasResolution = _require3.typeAliasResolution,
typeEnumResolution = _require3.typeEnumResolution;
const _require4 = require('../components/componentsUtils'),
flattenProperties = _require4.flattenProperties;
const _require5 = require('../parseTopLevelType'),
flattenIntersectionType = _require5.flattenIntersectionType;
function translateObjectTypeAnnotation(
hasteModuleName,
/**
* TODO(T108222691): Use flow-types for @babel/parser
*/
typeScriptTypeAnnotation,
nullable,
objectMembers,
typeResolutionStatus,
baseTypes,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
) {
// $FlowFixMe[missing-type-arg]
const properties = objectMembers
.map(property => {
return tryParse(() => {
return parseObjectProperty(
typeScriptTypeAnnotation,
property,
hasteModuleName,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
nullable,
translateTypeAnnotation,
parser,
);
});
})
.filter(Boolean);
let objectTypeAnnotation;
if (baseTypes.length === 0) {
objectTypeAnnotation = {
type: 'ObjectTypeAnnotation',
properties,
};
} else {
objectTypeAnnotation = {
type: 'ObjectTypeAnnotation',
properties,
baseTypes,
};
}
return typeAliasResolution(
typeResolutionStatus,
objectTypeAnnotation,
aliasMap,
nullable,
);
}
function translateTypeReferenceAnnotation(
typeName,
nullable,
typeAnnotation,
hasteModuleName,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
) {
switch (typeName) {
case 'RootTag': {
return emitRootTag(nullable);
}
case 'Promise': {
return emitPromise(
hasteModuleName,
typeAnnotation,
parser,
nullable,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
translateTypeAnnotation,
);
}
case 'Array':
case 'ReadonlyArray': {
return emitArrayType(
hasteModuleName,
typeAnnotation,
parser,
types,
aliasMap,
enumMap,
cxxOnly,
nullable,
translateTypeAnnotation,
);
}
default: {
const commonType = emitCommonTypes(
hasteModuleName,
types,
typeAnnotation,
aliasMap,
enumMap,
tryParse,
cxxOnly,
nullable,
parser,
);
if (!commonType) {
throw new UnsupportedGenericParserError(
hasteModuleName,
typeAnnotation,
parser,
);
}
return commonType;
}
}
}
function translateTypeAnnotation(
hasteModuleName,
/**
* TODO(T108222691): Use flow-types for @babel/parser
*/
typeScriptTypeAnnotation,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
) {
const _parser$getResolvedTy = parser.getResolvedTypeAnnotation(
typeScriptTypeAnnotation,
types,
parser,
),
nullable = _parser$getResolvedTy.nullable,
typeAnnotation = _parser$getResolvedTy.typeAnnotation,
typeResolutionStatus = _parser$getResolvedTy.typeResolutionStatus;
const resolveTypeaAnnotationFn = parser.getResolveTypeAnnotationFN();
resolveTypeaAnnotationFn(typeScriptTypeAnnotation, types, parser);
switch (typeAnnotation.type) {
case 'TSArrayType': {
return translateArrayTypeAnnotation(
hasteModuleName,
types,
aliasMap,
enumMap,
cxxOnly,
'Array',
typeAnnotation.elementType,
nullable,
translateTypeAnnotation,
parser,
);
}
case 'TSTypeOperator': {
if (
typeAnnotation.operator === 'readonly' &&
typeAnnotation.typeAnnotation.type === 'TSArrayType'
) {
return translateArrayTypeAnnotation(
hasteModuleName,
types,
aliasMap,
enumMap,
cxxOnly,
'ReadonlyArray',
typeAnnotation.typeAnnotation.elementType,
nullable,
translateTypeAnnotation,
parser,
);
} else {
throw new UnsupportedGenericParserError(
hasteModuleName,
typeAnnotation,
parser,
);
}
}
case 'TSTypeReference': {
return translateTypeReferenceAnnotation(
parser.getTypeAnnotationName(typeAnnotation),
nullable,
typeAnnotation,
hasteModuleName,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
);
}
case 'TSInterfaceDeclaration': {
var _typeAnnotation$exten;
const baseTypes = (
(_typeAnnotation$exten = typeAnnotation.extends) !== null &&
_typeAnnotation$exten !== void 0
? _typeAnnotation$exten
: []
).map(extend => extend.expression.name);
for (const baseType of baseTypes) {
// ensure base types exist and appear in aliasMap
translateTypeAnnotation(
hasteModuleName,
{
type: 'TSTypeReference',
typeName: {
type: 'Identifier',
name: baseType,
},
},
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
);
}
return translateObjectTypeAnnotation(
hasteModuleName,
typeScriptTypeAnnotation,
nullable,
flattenProperties([typeAnnotation], types, parser),
typeResolutionStatus,
baseTypes,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
);
}
case 'TSIntersectionType': {
return translateObjectTypeAnnotation(
hasteModuleName,
typeScriptTypeAnnotation,
nullable,
flattenProperties(
flattenIntersectionType(typeAnnotation, types),
types,
parser,
),
typeResolutionStatus,
[],
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
);
}
case 'TSTypeLiteral': {
// if there is TSIndexSignature, then it is a dictionary
if (typeAnnotation.members) {
const indexSignatures = typeAnnotation.members.filter(
member => member.type === 'TSIndexSignature',
);
if (indexSignatures.length > 0) {
// check the property type to prevent developers from using unsupported types
// the return value from `translateTypeAnnotation` is unused
const propertyType = indexSignatures[0].typeAnnotation;
const valueType = translateTypeAnnotation(
hasteModuleName,
propertyType,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
);
// no need to do further checking
return emitDictionary(nullable, valueType);
}
}
return translateObjectTypeAnnotation(
hasteModuleName,
typeScriptTypeAnnotation,
nullable,
typeAnnotation.members,
typeResolutionStatus,
[],
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
);
}
case 'TSEnumDeclaration': {
return typeEnumResolution(
typeAnnotation,
typeResolutionStatus,
nullable,
hasteModuleName,
enumMap,
parser,
);
}
case 'TSFunctionType': {
return emitFunction(
nullable,
hasteModuleName,
typeAnnotation,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
translateTypeAnnotation,
parser,
);
}
case 'TSUnionType': {
return emitUnion(nullable, hasteModuleName, typeAnnotation, parser);
}
default: {
const commonType = emitCommonTypes(
hasteModuleName,
types,
typeAnnotation,
aliasMap,
enumMap,
tryParse,
cxxOnly,
nullable,
parser,
);
if (!commonType) {
throw new UnsupportedTypeAnnotationParserError(
hasteModuleName,
typeAnnotation,
parser.language(),
);
}
return commonType;
}
}
}
module.exports = {
typeScriptTranslateTypeAnnotation: translateTypeAnnotation,
};

View File

@@ -0,0 +1,398 @@
/**
* 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 {
NamedShape,
NativeModuleAliasMap,
NativeModuleBaseTypeAnnotation,
NativeModuleEnumMap,
NativeModuleTypeAnnotation,
Nullable,
} from '../../../CodegenSchema';
import type {Parser} from '../../parser';
import type {
ParserErrorCapturer,
TypeDeclarationMap,
TypeResolutionStatus,
} from '../../utils';
const {
UnsupportedGenericParserError,
UnsupportedTypeAnnotationParserError,
} = require('../../errors');
const {parseObjectProperty} = require('../../parsers-commons');
const {
emitArrayType,
emitCommonTypes,
emitDictionary,
emitFunction,
emitPromise,
emitRootTag,
emitUnion,
translateArrayTypeAnnotation,
typeAliasResolution,
typeEnumResolution,
} = require('../../parsers-primitives');
const {flattenProperties} = require('../components/componentsUtils');
const {flattenIntersectionType} = require('../parseTopLevelType');
function translateObjectTypeAnnotation(
hasteModuleName: string,
/**
* TODO(T108222691): Use flow-types for @babel/parser
*/
typeScriptTypeAnnotation: $FlowFixMe,
nullable: boolean,
objectMembers: $ReadOnlyArray<$FlowFixMe>,
typeResolutionStatus: TypeResolutionStatus,
baseTypes: $ReadOnlyArray<string>,
types: TypeDeclarationMap,
aliasMap: {...NativeModuleAliasMap},
enumMap: {...NativeModuleEnumMap},
tryParse: ParserErrorCapturer,
cxxOnly: boolean,
parser: Parser,
): Nullable<NativeModuleTypeAnnotation> {
// $FlowFixMe[missing-type-arg]
const properties = objectMembers
.map<?NamedShape<Nullable<NativeModuleBaseTypeAnnotation>>>(property => {
return tryParse(() => {
return parseObjectProperty(
typeScriptTypeAnnotation,
property,
hasteModuleName,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
nullable,
translateTypeAnnotation,
parser,
);
});
})
.filter(Boolean);
let objectTypeAnnotation;
if (baseTypes.length === 0) {
objectTypeAnnotation = {
type: 'ObjectTypeAnnotation',
properties,
};
} else {
objectTypeAnnotation = {
type: 'ObjectTypeAnnotation',
properties,
baseTypes,
};
}
return typeAliasResolution(
typeResolutionStatus,
objectTypeAnnotation,
aliasMap,
nullable,
);
}
function translateTypeReferenceAnnotation(
typeName: string,
nullable: boolean,
typeAnnotation: $FlowFixMe,
hasteModuleName: string,
types: TypeDeclarationMap,
aliasMap: {...NativeModuleAliasMap},
enumMap: {...NativeModuleEnumMap},
tryParse: ParserErrorCapturer,
cxxOnly: boolean,
parser: Parser,
): Nullable<NativeModuleTypeAnnotation> {
switch (typeName) {
case 'RootTag': {
return emitRootTag(nullable);
}
case 'Promise': {
return emitPromise(
hasteModuleName,
typeAnnotation,
parser,
nullable,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
translateTypeAnnotation,
);
}
case 'Array':
case 'ReadonlyArray': {
return emitArrayType(
hasteModuleName,
typeAnnotation,
parser,
types,
aliasMap,
enumMap,
cxxOnly,
nullable,
translateTypeAnnotation,
);
}
default: {
const commonType = emitCommonTypes(
hasteModuleName,
types,
typeAnnotation,
aliasMap,
enumMap,
tryParse,
cxxOnly,
nullable,
parser,
);
if (!commonType) {
throw new UnsupportedGenericParserError(
hasteModuleName,
typeAnnotation,
parser,
);
}
return commonType;
}
}
}
function translateTypeAnnotation(
hasteModuleName: string,
/**
* TODO(T108222691): Use flow-types for @babel/parser
*/
typeScriptTypeAnnotation: $FlowFixMe,
types: TypeDeclarationMap,
aliasMap: {...NativeModuleAliasMap},
enumMap: {...NativeModuleEnumMap},
tryParse: ParserErrorCapturer,
cxxOnly: boolean,
parser: Parser,
): Nullable<NativeModuleTypeAnnotation> {
const {nullable, typeAnnotation, typeResolutionStatus} =
parser.getResolvedTypeAnnotation(typeScriptTypeAnnotation, types, parser);
const resolveTypeaAnnotationFn = parser.getResolveTypeAnnotationFN();
resolveTypeaAnnotationFn(typeScriptTypeAnnotation, types, parser);
switch (typeAnnotation.type) {
case 'TSArrayType': {
return translateArrayTypeAnnotation(
hasteModuleName,
types,
aliasMap,
enumMap,
cxxOnly,
'Array',
typeAnnotation.elementType,
nullable,
translateTypeAnnotation,
parser,
);
}
case 'TSTypeOperator': {
if (
typeAnnotation.operator === 'readonly' &&
typeAnnotation.typeAnnotation.type === 'TSArrayType'
) {
return translateArrayTypeAnnotation(
hasteModuleName,
types,
aliasMap,
enumMap,
cxxOnly,
'ReadonlyArray',
typeAnnotation.typeAnnotation.elementType,
nullable,
translateTypeAnnotation,
parser,
);
} else {
throw new UnsupportedGenericParserError(
hasteModuleName,
typeAnnotation,
parser,
);
}
}
case 'TSTypeReference': {
return translateTypeReferenceAnnotation(
parser.getTypeAnnotationName(typeAnnotation),
nullable,
typeAnnotation,
hasteModuleName,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
);
}
case 'TSInterfaceDeclaration': {
const baseTypes = (typeAnnotation.extends ?? []).map(
extend => extend.expression.name,
);
for (const baseType of baseTypes) {
// ensure base types exist and appear in aliasMap
translateTypeAnnotation(
hasteModuleName,
{
type: 'TSTypeReference',
typeName: {type: 'Identifier', name: baseType},
},
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
);
}
return translateObjectTypeAnnotation(
hasteModuleName,
typeScriptTypeAnnotation,
nullable,
flattenProperties([typeAnnotation], types, parser),
typeResolutionStatus,
baseTypes,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
);
}
case 'TSIntersectionType': {
return translateObjectTypeAnnotation(
hasteModuleName,
typeScriptTypeAnnotation,
nullable,
flattenProperties(
flattenIntersectionType(typeAnnotation, types),
types,
parser,
),
typeResolutionStatus,
[],
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
);
}
case 'TSTypeLiteral': {
// if there is TSIndexSignature, then it is a dictionary
if (typeAnnotation.members) {
const indexSignatures = typeAnnotation.members.filter(
member => member.type === 'TSIndexSignature',
);
if (indexSignatures.length > 0) {
// check the property type to prevent developers from using unsupported types
// the return value from `translateTypeAnnotation` is unused
const propertyType = indexSignatures[0].typeAnnotation;
const valueType = translateTypeAnnotation(
hasteModuleName,
propertyType,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
);
// no need to do further checking
return emitDictionary(nullable, valueType);
}
}
return translateObjectTypeAnnotation(
hasteModuleName,
typeScriptTypeAnnotation,
nullable,
typeAnnotation.members,
typeResolutionStatus,
[],
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
);
}
case 'TSEnumDeclaration': {
return typeEnumResolution(
typeAnnotation,
typeResolutionStatus,
nullable,
hasteModuleName,
enumMap,
parser,
);
}
case 'TSFunctionType': {
return emitFunction(
nullable,
hasteModuleName,
typeAnnotation,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
translateTypeAnnotation,
parser,
);
}
case 'TSUnionType': {
return emitUnion(nullable, hasteModuleName, typeAnnotation, parser);
}
default: {
const commonType = emitCommonTypes(
hasteModuleName,
types,
typeAnnotation,
aliasMap,
enumMap,
tryParse,
cxxOnly,
nullable,
parser,
);
if (!commonType) {
throw new UnsupportedTypeAnnotationParserError(
hasteModuleName,
typeAnnotation,
parser.language(),
);
}
return commonType;
}
}
}
module.exports = {
typeScriptTranslateTypeAnnotation: translateTypeAnnotation,
};

View File

@@ -0,0 +1,203 @@
/**
* 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 getValueFromTypes(value, types) {
switch (value.type) {
case 'TSTypeReference':
if (types[value.typeName.name]) {
return getValueFromTypes(types[value.typeName.name], types);
} else {
return value;
}
case 'TSTypeAliasDeclaration':
return getValueFromTypes(value.typeAnnotation, types);
default:
return value;
}
}
function isNull(t) {
return t.type === 'TSNullKeyword' || t.type === 'TSUndefinedKeyword';
}
function isNullOrVoid(t) {
return isNull(t) || t.type === 'TSVoidKeyword';
}
function couldBeNumericLiteral(type) {
return type === 'Literal' || type === 'NumericLiteral';
}
function couldBeSimpleLiteral(type) {
return (
couldBeNumericLiteral(type) ||
type === 'StringLiteral' ||
type === 'BooleanLiteral'
);
}
function evaluateLiteral(literalNode) {
const valueType = literalNode.type;
if (valueType === 'TSLiteralType') {
const literal = literalNode.literal;
if (couldBeSimpleLiteral(literal.type)) {
if (
typeof literal.value === 'string' ||
typeof literal.value === 'number' ||
typeof literal.value === 'boolean'
) {
return literal.value;
}
} else if (
literal.type === 'UnaryExpression' &&
literal.operator === '-' &&
couldBeNumericLiteral(literal.argument.type) &&
typeof literal.argument.value === 'number'
) {
return -literal.argument.value;
}
} else if (isNull(literalNode)) {
return null;
}
throw new Error(
'The default value in WithDefault must be string, number, boolean or null .',
);
}
function handleUnionAndParen(type, result, knownTypes) {
switch (type.type) {
case 'TSParenthesizedType': {
handleUnionAndParen(type.typeAnnotation, result, knownTypes);
break;
}
case 'TSUnionType': {
// the order is important
// result.optional must be set first
for (const t of type.types) {
if (isNullOrVoid(t)) {
result.optional = true;
}
}
for (const t of type.types) {
if (!isNullOrVoid(t)) {
handleUnionAndParen(t, result, knownTypes);
}
}
break;
}
case 'TSTypeReference':
if (type.typeName.name === 'Readonly') {
handleUnionAndParen(type.typeParameters.params[0], result, knownTypes);
} else if (type.typeName.name === 'WithDefault') {
if (result.optional) {
throw new Error(
'WithDefault<> is optional and does not need to be marked as optional. Please remove the union of undefined and/or null',
);
}
if (type.typeParameters.params.length !== 2) {
throw new Error(
'WithDefault requires two parameters: type and default value.',
);
}
if (result.defaultValue !== undefined) {
throw new Error(
'Multiple WithDefault is not allowed nested or in a union type.',
);
}
result.optional = true;
result.defaultValue = evaluateLiteral(type.typeParameters.params[1]);
handleUnionAndParen(type.typeParameters.params[0], result, knownTypes);
} else if (!knownTypes) {
result.unions.push(type);
} else {
const resolvedType = getValueFromTypes(type, knownTypes);
if (
resolvedType.type === 'TSTypeReference' &&
resolvedType.typeName.name === type.typeName.name
) {
result.unions.push(type);
} else {
handleUnionAndParen(resolvedType, result, knownTypes);
}
}
break;
default:
result.unions.push(type);
}
}
function parseTopLevelType(type, knownTypes) {
let result = {
unions: [],
optional: false,
};
handleUnionAndParen(type, result, knownTypes);
if (result.unions.length === 0) {
throw new Error('Union type could not be just null or undefined.');
} else if (result.unions.length === 1) {
return {
type: result.unions[0],
optional: result.optional,
defaultValue: result.defaultValue,
};
} else {
return {
type: {
type: 'TSUnionType',
types: result.unions,
},
optional: result.optional,
defaultValue: result.defaultValue,
};
}
}
function handleIntersectionAndParen(type, result, knownTypes) {
switch (type.type) {
case 'TSParenthesizedType': {
handleIntersectionAndParen(type.typeAnnotation, result, knownTypes);
break;
}
case 'TSIntersectionType': {
for (const t of type.types) {
handleIntersectionAndParen(t, result, knownTypes);
}
break;
}
case 'TSTypeReference':
if (type.typeName.name === 'Readonly') {
handleIntersectionAndParen(
type.typeParameters.params[0],
result,
knownTypes,
);
} else if (type.typeName.name === 'WithDefault') {
throw new Error('WithDefault<> is now allowed in intersection types.');
} else if (!knownTypes) {
result.push(type);
} else {
const resolvedType = getValueFromTypes(type, knownTypes);
if (
resolvedType.type === 'TSTypeReference' &&
resolvedType.typeName.name === type.typeName.name
) {
result.push(type);
} else {
handleIntersectionAndParen(resolvedType, result, knownTypes);
}
}
break;
default:
result.push(type);
}
}
function flattenIntersectionType(type, knownTypes) {
const result = [];
handleIntersectionAndParen(type, result, knownTypes);
return result;
}
module.exports = {
parseTopLevelType,
flattenIntersectionType,
};

View File

@@ -0,0 +1,243 @@
/**
* 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 {TypeDeclarationMap} from '../utils';
export type LegalDefaultValues = string | number | boolean | null;
type TopLevelTypeInternal = {
unions: Array<$FlowFixMe>,
optional: boolean,
defaultValue?: LegalDefaultValues,
};
export type TopLevelType = {
type: $FlowFixMe,
optional: boolean,
defaultValue?: LegalDefaultValues,
};
function getValueFromTypes(
value: $FlowFixMe,
types: TypeDeclarationMap,
): $FlowFixMe {
switch (value.type) {
case 'TSTypeReference':
if (types[value.typeName.name]) {
return getValueFromTypes(types[value.typeName.name], types);
} else {
return value;
}
case 'TSTypeAliasDeclaration':
return getValueFromTypes(value.typeAnnotation, types);
default:
return value;
}
}
function isNull(t: $FlowFixMe) {
return t.type === 'TSNullKeyword' || t.type === 'TSUndefinedKeyword';
}
function isNullOrVoid(t: $FlowFixMe) {
return isNull(t) || t.type === 'TSVoidKeyword';
}
function couldBeNumericLiteral(type: string) {
return type === 'Literal' || type === 'NumericLiteral';
}
function couldBeSimpleLiteral(type: string) {
return (
couldBeNumericLiteral(type) ||
type === 'StringLiteral' ||
type === 'BooleanLiteral'
);
}
function evaluateLiteral(
literalNode: $FlowFixMe,
): string | number | boolean | null {
const valueType = literalNode.type;
if (valueType === 'TSLiteralType') {
const literal = literalNode.literal;
if (couldBeSimpleLiteral(literal.type)) {
if (
typeof literal.value === 'string' ||
typeof literal.value === 'number' ||
typeof literal.value === 'boolean'
) {
return literal.value;
}
} else if (
literal.type === 'UnaryExpression' &&
literal.operator === '-' &&
couldBeNumericLiteral(literal.argument.type) &&
typeof literal.argument.value === 'number'
) {
return -literal.argument.value;
}
} else if (isNull(literalNode)) {
return null;
}
throw new Error(
'The default value in WithDefault must be string, number, boolean or null .',
);
}
function handleUnionAndParen(
type: $FlowFixMe,
result: TopLevelTypeInternal,
knownTypes?: TypeDeclarationMap,
): void {
switch (type.type) {
case 'TSParenthesizedType': {
handleUnionAndParen(type.typeAnnotation, result, knownTypes);
break;
}
case 'TSUnionType': {
// the order is important
// result.optional must be set first
for (const t of type.types) {
if (isNullOrVoid(t)) {
result.optional = true;
}
}
for (const t of type.types) {
if (!isNullOrVoid(t)) {
handleUnionAndParen(t, result, knownTypes);
}
}
break;
}
case 'TSTypeReference':
if (type.typeName.name === 'Readonly') {
handleUnionAndParen(type.typeParameters.params[0], result, knownTypes);
} else if (type.typeName.name === 'WithDefault') {
if (result.optional) {
throw new Error(
'WithDefault<> is optional and does not need to be marked as optional. Please remove the union of undefined and/or null',
);
}
if (type.typeParameters.params.length !== 2) {
throw new Error(
'WithDefault requires two parameters: type and default value.',
);
}
if (result.defaultValue !== undefined) {
throw new Error(
'Multiple WithDefault is not allowed nested or in a union type.',
);
}
result.optional = true;
result.defaultValue = evaluateLiteral(type.typeParameters.params[1]);
handleUnionAndParen(type.typeParameters.params[0], result, knownTypes);
} else if (!knownTypes) {
result.unions.push(type);
} else {
const resolvedType = getValueFromTypes(type, knownTypes);
if (
resolvedType.type === 'TSTypeReference' &&
resolvedType.typeName.name === type.typeName.name
) {
result.unions.push(type);
} else {
handleUnionAndParen(resolvedType, result, knownTypes);
}
}
break;
default:
result.unions.push(type);
}
}
function parseTopLevelType(
type: $FlowFixMe,
knownTypes?: TypeDeclarationMap,
): TopLevelType {
let result: TopLevelTypeInternal = {unions: [], optional: false};
handleUnionAndParen(type, result, knownTypes);
if (result.unions.length === 0) {
throw new Error('Union type could not be just null or undefined.');
} else if (result.unions.length === 1) {
return {
type: result.unions[0],
optional: result.optional,
defaultValue: result.defaultValue,
};
} else {
return {
type: {type: 'TSUnionType', types: result.unions},
optional: result.optional,
defaultValue: result.defaultValue,
};
}
}
function handleIntersectionAndParen(
type: $FlowFixMe,
result: Array<$FlowFixMe>,
knownTypes?: TypeDeclarationMap,
): void {
switch (type.type) {
case 'TSParenthesizedType': {
handleIntersectionAndParen(type.typeAnnotation, result, knownTypes);
break;
}
case 'TSIntersectionType': {
for (const t of type.types) {
handleIntersectionAndParen(t, result, knownTypes);
}
break;
}
case 'TSTypeReference':
if (type.typeName.name === 'Readonly') {
handleIntersectionAndParen(
type.typeParameters.params[0],
result,
knownTypes,
);
} else if (type.typeName.name === 'WithDefault') {
throw new Error('WithDefault<> is now allowed in intersection types.');
} else if (!knownTypes) {
result.push(type);
} else {
const resolvedType = getValueFromTypes(type, knownTypes);
if (
resolvedType.type === 'TSTypeReference' &&
resolvedType.typeName.name === type.typeName.name
) {
result.push(type);
} else {
handleIntersectionAndParen(resolvedType, result, knownTypes);
}
}
break;
default:
result.push(type);
}
}
function flattenIntersectionType(
type: $FlowFixMe,
knownTypes?: TypeDeclarationMap,
): Array<$FlowFixMe> {
const result: Array<$FlowFixMe> = [];
handleIntersectionAndParen(type, result, knownTypes);
return result;
}
module.exports = {
parseTopLevelType,
flattenIntersectionType,
};

View File

@@ -0,0 +1,17 @@
/**
* 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.
*/
import type { Parser } from '../parser';
import type { SchemaType } from '../../CodegenSchema';
import type { ParserType } from '../errors';
export declare class TypeScriptParser implements Parser {
language(): ParserType;
parseFile(filename: string): SchemaType;
parseString(contents: string, filename?: string): SchemaType;
parseModuleFixture(filename: string): SchemaType;
}

View File

@@ -0,0 +1,532 @@
/**
* 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 _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);
}
const _require = require('../errors'),
UnsupportedObjectPropertyTypeAnnotationParserError =
_require.UnsupportedObjectPropertyTypeAnnotationParserError;
const _require2 = require('../parsers-commons.js'),
buildModuleSchema = _require2.buildModuleSchema,
buildPropSchema = _require2.buildPropSchema,
buildSchema = _require2.buildSchema,
extendsForProp = _require2.extendsForProp,
handleGenericTypeAnnotation = _require2.handleGenericTypeAnnotation;
const _require3 = require('../parsers-primitives'),
Visitor = _require3.Visitor;
const _require4 = require('../schema.js'),
wrapComponentSchema = _require4.wrapComponentSchema;
const _require5 = require('./components'),
buildComponentSchema = _require5.buildComponentSchema;
const _require6 = require('./components/componentsUtils'),
flattenProperties = _require6.flattenProperties,
getSchemaInfo = _require6.getSchemaInfo,
getTypeAnnotation = _require6.getTypeAnnotation;
const _require7 = require('./modules'),
typeScriptTranslateTypeAnnotation =
_require7.typeScriptTranslateTypeAnnotation;
const _require8 = require('./parseTopLevelType'),
parseTopLevelType = _require8.parseTopLevelType;
// $FlowFixMe[untyped-import] Use flow-types for @babel/parser
const babelParser = require('@babel/parser');
const fs = require('fs');
const invariant = require('invariant');
class TypeScriptParser {
constructor() {
_defineProperty(
this,
'typeParameterInstantiation',
'TSTypeParameterInstantiation',
);
_defineProperty(this, 'typeAlias', 'TSTypeAliasDeclaration');
_defineProperty(this, 'enumDeclaration', 'TSEnumDeclaration');
_defineProperty(this, 'interfaceDeclaration', 'TSInterfaceDeclaration');
_defineProperty(this, 'nullLiteralTypeAnnotation', 'TSNullKeyword');
_defineProperty(
this,
'undefinedLiteralTypeAnnotation',
'TSUndefinedKeyword',
);
}
isProperty(property) {
return property.type === 'TSPropertySignature';
}
getKeyName(property, hasteModuleName) {
if (!this.isProperty(property)) {
throw new UnsupportedObjectPropertyTypeAnnotationParserError(
hasteModuleName,
property,
property.type,
this.language(),
);
}
return property.key.name;
}
language() {
return 'TypeScript';
}
getTypeAnnotationName(typeAnnotation) {
var _typeAnnotation$typeN;
return typeAnnotation === null || typeAnnotation === void 0
? void 0
: (_typeAnnotation$typeN = typeAnnotation.typeName) === null ||
_typeAnnotation$typeN === void 0
? void 0
: _typeAnnotation$typeN.name;
}
checkIfInvalidModule(typeArguments) {
return (
typeArguments.type !== 'TSTypeParameterInstantiation' ||
typeArguments.params.length !== 1 ||
typeArguments.params[0].type !== 'TSTypeReference' ||
typeArguments.params[0].typeName.name !== 'Spec'
);
}
remapUnionTypeAnnotationMemberNames(membersTypes) {
const remapLiteral = item => {
return item.literal
? item.literal.type
.replace('NumericLiteral', 'NumberTypeAnnotation')
.replace('StringLiteral', 'StringTypeAnnotation')
: 'ObjectTypeAnnotation';
};
return [...new Set(membersTypes.map(remapLiteral))];
}
parseFile(filename) {
const contents = fs.readFileSync(filename, 'utf8');
return this.parseString(contents, filename);
}
parseString(contents, filename) {
return buildSchema(
contents,
filename,
wrapComponentSchema,
buildComponentSchema,
buildModuleSchema,
Visitor,
this,
typeScriptTranslateTypeAnnotation,
);
}
parseModuleFixture(filename) {
const contents = fs.readFileSync(filename, 'utf8');
return this.parseString(contents, 'path/NativeSampleTurboModule.ts');
}
getAst(contents, filename) {
return babelParser.parse(contents, {
sourceType: 'module',
plugins: ['typescript'],
}).program;
}
getFunctionTypeAnnotationParameters(functionTypeAnnotation) {
return functionTypeAnnotation.parameters;
}
getFunctionNameFromParameter(parameter) {
return parameter.typeAnnotation;
}
getParameterName(parameter) {
return parameter.name;
}
getParameterTypeAnnotation(parameter) {
return parameter.typeAnnotation.typeAnnotation;
}
getFunctionTypeAnnotationReturnType(functionTypeAnnotation) {
return functionTypeAnnotation.typeAnnotation.typeAnnotation;
}
parseEnumMembersType(typeAnnotation) {
var _typeAnnotation$membe;
const enumInitializer =
(_typeAnnotation$membe = typeAnnotation.members[0]) === null ||
_typeAnnotation$membe === void 0
? void 0
: _typeAnnotation$membe.initializer;
const enumMembersType =
!enumInitializer || enumInitializer.type === 'StringLiteral'
? 'StringTypeAnnotation'
: enumInitializer.type === 'NumericLiteral'
? 'NumberTypeAnnotation'
: null;
if (!enumMembersType) {
throw new Error(
'Enum values must be either blank, number, or string values.',
);
}
return enumMembersType;
}
validateEnumMembersSupported(typeAnnotation, enumMembersType) {
if (!typeAnnotation.members || typeAnnotation.members.length === 0) {
throw new Error('Enums should have at least one member.');
}
const enumInitializerType =
enumMembersType === 'StringTypeAnnotation'
? 'StringLiteral'
: enumMembersType === 'NumberTypeAnnotation'
? 'NumericLiteral'
: null;
typeAnnotation.members.forEach(member => {
var _member$initializer$t, _member$initializer;
if (
((_member$initializer$t =
(_member$initializer = member.initializer) === null ||
_member$initializer === void 0
? void 0
: _member$initializer.type) !== null &&
_member$initializer$t !== void 0
? _member$initializer$t
: 'StringLiteral') !== enumInitializerType
) {
throw new Error(
'Enum values can not be mixed. They all must be either blank, number, or string values.',
);
}
});
}
parseEnumMembers(typeAnnotation) {
return typeAnnotation.members.map(member => {
var _member$initializer$v, _member$initializer2;
return {
name: member.id.name,
value:
(_member$initializer$v =
(_member$initializer2 = member.initializer) === null ||
_member$initializer2 === void 0
? void 0
: _member$initializer2.value) !== null &&
_member$initializer$v !== void 0
? _member$initializer$v
: member.id.name,
};
});
}
isModuleInterface(node) {
var _node$extends;
return (
node.type === 'TSInterfaceDeclaration' &&
((_node$extends = node.extends) === null || _node$extends === void 0
? void 0
: _node$extends.length) === 1 &&
node.extends[0].type === 'TSExpressionWithTypeArguments' &&
node.extends[0].expression.name === 'TurboModule'
);
}
isGenericTypeAnnotation(type) {
return type === 'TSTypeReference';
}
extractAnnotatedElement(typeAnnotation, types) {
return types[typeAnnotation.typeParameters.params[0].typeName.name];
}
/**
* TODO(T108222691): Use flow-types for @babel/parser
*/
getTypes(ast) {
return ast.body.reduce((types, node) => {
switch (node.type) {
case 'ExportNamedDeclaration': {
if (node.declaration) {
switch (node.declaration.type) {
case 'TSTypeAliasDeclaration':
case 'TSInterfaceDeclaration':
case 'TSEnumDeclaration': {
types[node.declaration.id.name] = node.declaration;
break;
}
}
}
break;
}
case 'TSTypeAliasDeclaration':
case 'TSInterfaceDeclaration':
case 'TSEnumDeclaration': {
types[node.id.name] = node;
break;
}
}
return types;
}, {});
}
callExpressionTypeParameters(callExpression) {
return callExpression.typeParameters || null;
}
computePartialProperties(
properties,
hasteModuleName,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
) {
return properties.map(prop => {
return {
name: prop.key.name,
optional: true,
typeAnnotation: typeScriptTranslateTypeAnnotation(
hasteModuleName,
prop.typeAnnotation.typeAnnotation,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
this,
),
};
});
}
functionTypeAnnotation(propertyValueType) {
return (
propertyValueType === 'TSFunctionType' ||
propertyValueType === 'TSMethodSignature'
);
}
getTypeArgumentParamsFromDeclaration(declaration) {
return declaration.typeParameters.params;
}
// This FlowFixMe is supposed to refer to typeArgumentParams and funcArgumentParams of generated AST.
getNativeComponentType(typeArgumentParams, funcArgumentParams) {
return {
propsTypeName: typeArgumentParams[0].typeName.name,
componentName: funcArgumentParams[0].value,
};
}
getAnnotatedElementProperties(annotatedElement) {
return annotatedElement.typeAnnotation.members;
}
bodyProperties(typeAlias) {
return typeAlias.body.body;
}
convertKeywordToTypeAnnotation(keyword) {
switch (keyword) {
case 'TSBooleanKeyword':
return 'BooleanTypeAnnotation';
case 'TSNumberKeyword':
return 'NumberTypeAnnotation';
case 'TSVoidKeyword':
return 'VoidTypeAnnotation';
case 'TSStringKeyword':
return 'StringTypeAnnotation';
case 'TSUnknownKeyword':
return 'MixedTypeAnnotation';
}
return keyword;
}
argumentForProp(prop) {
return prop.expression;
}
nameForArgument(prop) {
return prop.expression.name;
}
isOptionalProperty(property) {
return property.optional || false;
}
getGetSchemaInfoFN() {
return getSchemaInfo;
}
getTypeAnnotationFromProperty(property) {
return property.typeAnnotation.typeAnnotation;
}
getGetTypeAnnotationFN() {
return getTypeAnnotation;
}
getResolvedTypeAnnotation(
// TODO(T108222691): Use flow-types for @babel/parser
typeAnnotation,
types,
parser,
) {
invariant(
typeAnnotation != null,
'resolveTypeAnnotation(): typeAnnotation cannot be null',
);
let node =
typeAnnotation.type === 'TSTypeAnnotation'
? typeAnnotation.typeAnnotation
: typeAnnotation;
let nullable = false;
let typeResolutionStatus = {
successful: false,
};
for (;;) {
const topLevelType = parseTopLevelType(node);
nullable = nullable || topLevelType.optional;
node = topLevelType.type;
if (node.type !== 'TSTypeReference') {
break;
}
const typeAnnotationName = this.getTypeAnnotationName(node);
const resolvedTypeAnnotation = types[typeAnnotationName];
if (resolvedTypeAnnotation == null) {
break;
}
const _handleGenericTypeAnn = handleGenericTypeAnnotation(
node,
resolvedTypeAnnotation,
this,
),
typeAnnotationNode = _handleGenericTypeAnn.typeAnnotation,
status = _handleGenericTypeAnn.typeResolutionStatus;
typeResolutionStatus = status;
node = typeAnnotationNode;
}
return {
nullable: nullable,
typeAnnotation: node,
typeResolutionStatus,
};
}
getResolveTypeAnnotationFN() {
return (typeAnnotation, types, parser) => {
return this.getResolvedTypeAnnotation(typeAnnotation, types, parser);
};
}
isEvent(typeAnnotation) {
if (typeAnnotation.type !== 'TSTypeReference') {
return false;
}
const eventNames = new Set(['BubblingEventHandler', 'DirectEventHandler']);
return eventNames.has(this.getTypeAnnotationName(typeAnnotation));
}
isProp(name, typeAnnotation) {
if (typeAnnotation.type !== 'TSTypeReference') {
return true;
}
const isStyle =
name === 'style' &&
typeAnnotation.type === 'GenericTypeAnnotation' &&
this.getTypeAnnotationName(typeAnnotation) === 'ViewStyleProp';
return !isStyle;
}
getProps(typeDefinition, types) {
const extendsProps = [];
const componentPropAsts = [];
const remaining = [];
for (const prop of typeDefinition) {
// find extends
if (prop.type === 'TSExpressionWithTypeArguments') {
const extend = extendsForProp(prop, types, this);
if (extend) {
extendsProps.push(extend);
continue;
}
}
remaining.push(prop);
}
// find events and props
for (const prop of flattenProperties(remaining, types, this)) {
const topLevelType = parseTopLevelType(
prop.typeAnnotation.typeAnnotation,
types,
);
if (
prop.type === 'TSPropertySignature' &&
!this.isEvent(topLevelType.type) &&
this.isProp(prop.key.name, prop)
) {
componentPropAsts.push(prop);
}
}
return {
props: componentPropAsts
.map(property => buildPropSchema(property, types, this))
.filter(Boolean),
extendsProps,
};
}
getProperties(typeName, types) {
const alias = types[typeName];
if (!alias) {
throw new Error(
`Failed to find definition for "${typeName}", please check that you have a valid codegen typescript file`,
);
}
const aliasKind =
alias.type === 'TSInterfaceDeclaration' ? 'interface' : 'type';
try {
if (aliasKind === 'interface') {
var _alias$extends;
return [
...((_alias$extends = alias.extends) !== null &&
_alias$extends !== void 0
? _alias$extends
: []),
...alias.body.body,
];
}
return (
alias.typeAnnotation.members ||
alias.typeAnnotation.typeParameters.params[0].members ||
alias.typeAnnotation.typeParameters.params
);
} catch (e) {
throw new Error(
`Failed to find ${aliasKind} definition for "${typeName}", please check that you have a valid codegen typescript file`,
);
}
}
nextNodeForTypeAlias(typeAnnotation) {
return typeAnnotation.typeAnnotation;
}
nextNodeForEnum(typeAnnotation) {
return typeAnnotation;
}
genericTypeAnnotationErrorMessage(typeAnnotation) {
return `A non GenericTypeAnnotation must be a type declaration ('${this.typeAlias}'), an interface ('${this.interfaceDeclaration}'), or enum ('${this.enumDeclaration}'). Instead, got the unsupported ${typeAnnotation.type}.`;
}
extractTypeFromTypeAnnotation(typeAnnotation) {
return typeAnnotation.type === 'TSTypeReference'
? typeAnnotation.typeName.name
: typeAnnotation.type;
}
getObjectProperties(typeAnnotation) {
return typeAnnotation.members;
}
getLiteralValue(option) {
return option.literal.value;
}
getPaperTopLevelNameDeprecated(typeAnnotation) {
return typeAnnotation.typeParameters.params.length > 1
? typeAnnotation.typeParameters.params[1].literal.value
: null;
}
}
module.exports = {
TypeScriptParser,
};

View File

@@ -0,0 +1,570 @@
/**
* 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 {
ExtendsPropsShape,
NamedShape,
NativeModuleAliasMap,
NativeModuleEnumMap,
NativeModuleEnumMembers,
NativeModuleEnumMemberType,
NativeModuleParamTypeAnnotation,
Nullable,
PropTypeAnnotation,
SchemaType,
UnionTypeAnnotationMemberType,
} from '../../CodegenSchema';
import type {ParserType} from '../errors';
import type {
GetSchemaInfoFN,
GetTypeAnnotationFN,
Parser,
ResolveTypeAnnotationFN,
} from '../parser';
import type {
ParserErrorCapturer,
PropAST,
TypeDeclarationMap,
TypeResolutionStatus,
} from '../utils';
const {
UnsupportedObjectPropertyTypeAnnotationParserError,
} = require('../errors');
const {
buildModuleSchema,
buildPropSchema,
buildSchema,
extendsForProp,
handleGenericTypeAnnotation,
} = require('../parsers-commons.js');
const {Visitor} = require('../parsers-primitives');
const {wrapComponentSchema} = require('../schema.js');
const {buildComponentSchema} = require('./components');
const {
flattenProperties,
getSchemaInfo,
getTypeAnnotation,
} = require('./components/componentsUtils');
const {typeScriptTranslateTypeAnnotation} = require('./modules');
const {parseTopLevelType} = require('./parseTopLevelType');
// $FlowFixMe[untyped-import] Use flow-types for @babel/parser
const babelParser = require('@babel/parser');
const fs = require('fs');
const invariant = require('invariant');
class TypeScriptParser implements Parser {
typeParameterInstantiation: string = 'TSTypeParameterInstantiation';
typeAlias: string = 'TSTypeAliasDeclaration';
enumDeclaration: string = 'TSEnumDeclaration';
interfaceDeclaration: string = 'TSInterfaceDeclaration';
nullLiteralTypeAnnotation: string = 'TSNullKeyword';
undefinedLiteralTypeAnnotation: string = 'TSUndefinedKeyword';
isProperty(property: $FlowFixMe): boolean {
return property.type === 'TSPropertySignature';
}
getKeyName(property: $FlowFixMe, hasteModuleName: string): string {
if (!this.isProperty(property)) {
throw new UnsupportedObjectPropertyTypeAnnotationParserError(
hasteModuleName,
property,
property.type,
this.language(),
);
}
return property.key.name;
}
language(): ParserType {
return 'TypeScript';
}
getTypeAnnotationName(typeAnnotation: $FlowFixMe): string {
return typeAnnotation?.typeName?.name;
}
checkIfInvalidModule(typeArguments: $FlowFixMe): boolean {
return (
typeArguments.type !== 'TSTypeParameterInstantiation' ||
typeArguments.params.length !== 1 ||
typeArguments.params[0].type !== 'TSTypeReference' ||
typeArguments.params[0].typeName.name !== 'Spec'
);
}
remapUnionTypeAnnotationMemberNames(
membersTypes: $FlowFixMe[],
): UnionTypeAnnotationMemberType[] {
const remapLiteral = (item: $FlowFixMe) => {
return item.literal
? item.literal.type
.replace('NumericLiteral', 'NumberTypeAnnotation')
.replace('StringLiteral', 'StringTypeAnnotation')
: 'ObjectTypeAnnotation';
};
return [...new Set(membersTypes.map(remapLiteral))];
}
parseFile(filename: string): SchemaType {
const contents = fs.readFileSync(filename, 'utf8');
return this.parseString(contents, filename);
}
parseString(contents: string, filename: ?string): SchemaType {
return buildSchema(
contents,
filename,
wrapComponentSchema,
buildComponentSchema,
buildModuleSchema,
Visitor,
this,
typeScriptTranslateTypeAnnotation,
);
}
parseModuleFixture(filename: string): SchemaType {
const contents = fs.readFileSync(filename, 'utf8');
return this.parseString(contents, 'path/NativeSampleTurboModule.ts');
}
getAst(contents: string, filename?: ?string): $FlowFixMe {
return babelParser.parse(contents, {
sourceType: 'module',
plugins: ['typescript'],
}).program;
}
getFunctionTypeAnnotationParameters(
functionTypeAnnotation: $FlowFixMe,
): $ReadOnlyArray<$FlowFixMe> {
return functionTypeAnnotation.parameters;
}
getFunctionNameFromParameter(
parameter: NamedShape<Nullable<NativeModuleParamTypeAnnotation>>,
): $FlowFixMe {
return parameter.typeAnnotation;
}
getParameterName(parameter: $FlowFixMe): string {
return parameter.name;
}
getParameterTypeAnnotation(parameter: $FlowFixMe): $FlowFixMe {
return parameter.typeAnnotation.typeAnnotation;
}
getFunctionTypeAnnotationReturnType(
functionTypeAnnotation: $FlowFixMe,
): $FlowFixMe {
return functionTypeAnnotation.typeAnnotation.typeAnnotation;
}
parseEnumMembersType(typeAnnotation: $FlowFixMe): NativeModuleEnumMemberType {
const enumInitializer = typeAnnotation.members[0]?.initializer;
const enumMembersType: ?NativeModuleEnumMemberType =
!enumInitializer || enumInitializer.type === 'StringLiteral'
? 'StringTypeAnnotation'
: enumInitializer.type === 'NumericLiteral'
? 'NumberTypeAnnotation'
: null;
if (!enumMembersType) {
throw new Error(
'Enum values must be either blank, number, or string values.',
);
}
return enumMembersType;
}
validateEnumMembersSupported(
typeAnnotation: $FlowFixMe,
enumMembersType: NativeModuleEnumMemberType,
): void {
if (!typeAnnotation.members || typeAnnotation.members.length === 0) {
throw new Error('Enums should have at least one member.');
}
const enumInitializerType =
enumMembersType === 'StringTypeAnnotation'
? 'StringLiteral'
: enumMembersType === 'NumberTypeAnnotation'
? 'NumericLiteral'
: null;
typeAnnotation.members.forEach(member => {
if (
(member.initializer?.type ?? 'StringLiteral') !== enumInitializerType
) {
throw new Error(
'Enum values can not be mixed. They all must be either blank, number, or string values.',
);
}
});
}
parseEnumMembers(typeAnnotation: $FlowFixMe): NativeModuleEnumMembers {
return typeAnnotation.members.map(member => ({
name: member.id.name,
value: member.initializer?.value ?? member.id.name,
}));
}
isModuleInterface(node: $FlowFixMe): boolean {
return (
node.type === 'TSInterfaceDeclaration' &&
node.extends?.length === 1 &&
node.extends[0].type === 'TSExpressionWithTypeArguments' &&
node.extends[0].expression.name === 'TurboModule'
);
}
isGenericTypeAnnotation(type: $FlowFixMe): boolean {
return type === 'TSTypeReference';
}
extractAnnotatedElement(
typeAnnotation: $FlowFixMe,
types: TypeDeclarationMap,
): $FlowFixMe {
return types[typeAnnotation.typeParameters.params[0].typeName.name];
}
/**
* TODO(T108222691): Use flow-types for @babel/parser
*/
getTypes(ast: $FlowFixMe): TypeDeclarationMap {
return ast.body.reduce((types, node) => {
switch (node.type) {
case 'ExportNamedDeclaration': {
if (node.declaration) {
switch (node.declaration.type) {
case 'TSTypeAliasDeclaration':
case 'TSInterfaceDeclaration':
case 'TSEnumDeclaration': {
types[node.declaration.id.name] = node.declaration;
break;
}
}
}
break;
}
case 'TSTypeAliasDeclaration':
case 'TSInterfaceDeclaration':
case 'TSEnumDeclaration': {
types[node.id.name] = node;
break;
}
}
return types;
}, {});
}
callExpressionTypeParameters(callExpression: $FlowFixMe): $FlowFixMe | null {
return callExpression.typeParameters || null;
}
computePartialProperties(
properties: Array<$FlowFixMe>,
hasteModuleName: string,
types: TypeDeclarationMap,
aliasMap: {...NativeModuleAliasMap},
enumMap: {...NativeModuleEnumMap},
tryParse: ParserErrorCapturer,
cxxOnly: boolean,
): Array<$FlowFixMe> {
return properties.map(prop => {
return {
name: prop.key.name,
optional: true,
typeAnnotation: typeScriptTranslateTypeAnnotation(
hasteModuleName,
prop.typeAnnotation.typeAnnotation,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
this,
),
};
});
}
functionTypeAnnotation(propertyValueType: string): boolean {
return (
propertyValueType === 'TSFunctionType' ||
propertyValueType === 'TSMethodSignature'
);
}
getTypeArgumentParamsFromDeclaration(declaration: $FlowFixMe): $FlowFixMe {
return declaration.typeParameters.params;
}
// This FlowFixMe is supposed to refer to typeArgumentParams and funcArgumentParams of generated AST.
getNativeComponentType(
typeArgumentParams: $FlowFixMe,
funcArgumentParams: $FlowFixMe,
): {[string]: string} {
return {
propsTypeName: typeArgumentParams[0].typeName.name,
componentName: funcArgumentParams[0].value,
};
}
getAnnotatedElementProperties(annotatedElement: $FlowFixMe): $FlowFixMe {
return annotatedElement.typeAnnotation.members;
}
bodyProperties(typeAlias: TypeDeclarationMap): $ReadOnlyArray<$FlowFixMe> {
return typeAlias.body.body;
}
convertKeywordToTypeAnnotation(keyword: string): string {
switch (keyword) {
case 'TSBooleanKeyword':
return 'BooleanTypeAnnotation';
case 'TSNumberKeyword':
return 'NumberTypeAnnotation';
case 'TSVoidKeyword':
return 'VoidTypeAnnotation';
case 'TSStringKeyword':
return 'StringTypeAnnotation';
case 'TSUnknownKeyword':
return 'MixedTypeAnnotation';
}
return keyword;
}
argumentForProp(prop: PropAST): $FlowFixMe {
return prop.expression;
}
nameForArgument(prop: PropAST): $FlowFixMe {
return prop.expression.name;
}
isOptionalProperty(property: $FlowFixMe): boolean {
return property.optional || false;
}
getGetSchemaInfoFN(): GetSchemaInfoFN {
return getSchemaInfo;
}
getTypeAnnotationFromProperty(property: PropAST): $FlowFixMe {
return property.typeAnnotation.typeAnnotation;
}
getGetTypeAnnotationFN(): GetTypeAnnotationFN {
return getTypeAnnotation;
}
getResolvedTypeAnnotation(
// TODO(T108222691): Use flow-types for @babel/parser
typeAnnotation: $FlowFixMe,
types: TypeDeclarationMap,
parser: Parser,
): {
nullable: boolean,
typeAnnotation: $FlowFixMe,
typeResolutionStatus: TypeResolutionStatus,
} {
invariant(
typeAnnotation != null,
'resolveTypeAnnotation(): typeAnnotation cannot be null',
);
let node =
typeAnnotation.type === 'TSTypeAnnotation'
? typeAnnotation.typeAnnotation
: typeAnnotation;
let nullable = false;
let typeResolutionStatus: TypeResolutionStatus = {
successful: false,
};
for (;;) {
const topLevelType = parseTopLevelType(node);
nullable = nullable || topLevelType.optional;
node = topLevelType.type;
if (node.type !== 'TSTypeReference') {
break;
}
const typeAnnotationName = this.getTypeAnnotationName(node);
const resolvedTypeAnnotation = types[typeAnnotationName];
if (resolvedTypeAnnotation == null) {
break;
}
const {typeAnnotation: typeAnnotationNode, typeResolutionStatus: status} =
handleGenericTypeAnnotation(node, resolvedTypeAnnotation, this);
typeResolutionStatus = status;
node = typeAnnotationNode;
}
return {
nullable: nullable,
typeAnnotation: node,
typeResolutionStatus,
};
}
getResolveTypeAnnotationFN(): ResolveTypeAnnotationFN {
return (
typeAnnotation: $FlowFixMe,
types: TypeDeclarationMap,
parser: Parser,
) => {
return this.getResolvedTypeAnnotation(typeAnnotation, types, parser);
};
}
isEvent(typeAnnotation: $FlowFixMe): boolean {
if (typeAnnotation.type !== 'TSTypeReference') {
return false;
}
const eventNames = new Set(['BubblingEventHandler', 'DirectEventHandler']);
return eventNames.has(this.getTypeAnnotationName(typeAnnotation));
}
isProp(name: string, typeAnnotation: $FlowFixMe): boolean {
if (typeAnnotation.type !== 'TSTypeReference') {
return true;
}
const isStyle =
name === 'style' &&
typeAnnotation.type === 'GenericTypeAnnotation' &&
this.getTypeAnnotationName(typeAnnotation) === 'ViewStyleProp';
return !isStyle;
}
getProps(
typeDefinition: $ReadOnlyArray<PropAST>,
types: TypeDeclarationMap,
): {
props: $ReadOnlyArray<NamedShape<PropTypeAnnotation>>,
extendsProps: $ReadOnlyArray<ExtendsPropsShape>,
} {
const extendsProps: Array<ExtendsPropsShape> = [];
const componentPropAsts: Array<PropAST> = [];
const remaining: Array<PropAST> = [];
for (const prop of typeDefinition) {
// find extends
if (prop.type === 'TSExpressionWithTypeArguments') {
const extend = extendsForProp(prop, types, this);
if (extend) {
extendsProps.push(extend);
continue;
}
}
remaining.push(prop);
}
// find events and props
for (const prop of flattenProperties(remaining, types, this)) {
const topLevelType = parseTopLevelType(
prop.typeAnnotation.typeAnnotation,
types,
);
if (
prop.type === 'TSPropertySignature' &&
!this.isEvent(topLevelType.type) &&
this.isProp(prop.key.name, prop)
) {
componentPropAsts.push(prop);
}
}
return {
props: componentPropAsts
.map(property => buildPropSchema(property, types, this))
.filter(Boolean),
extendsProps,
};
}
getProperties(typeName: string, types: TypeDeclarationMap): $FlowFixMe {
const alias = types[typeName];
if (!alias) {
throw new Error(
`Failed to find definition for "${typeName}", please check that you have a valid codegen typescript file`,
);
}
const aliasKind =
alias.type === 'TSInterfaceDeclaration' ? 'interface' : 'type';
try {
if (aliasKind === 'interface') {
return [...(alias.extends ?? []), ...alias.body.body];
}
return (
alias.typeAnnotation.members ||
alias.typeAnnotation.typeParameters.params[0].members ||
alias.typeAnnotation.typeParameters.params
);
} catch (e) {
throw new Error(
`Failed to find ${aliasKind} definition for "${typeName}", please check that you have a valid codegen typescript file`,
);
}
}
nextNodeForTypeAlias(typeAnnotation: $FlowFixMe): $FlowFixMe {
return typeAnnotation.typeAnnotation;
}
nextNodeForEnum(typeAnnotation: $FlowFixMe): $FlowFixMe {
return typeAnnotation;
}
genericTypeAnnotationErrorMessage(typeAnnotation: $FlowFixMe): string {
return `A non GenericTypeAnnotation must be a type declaration ('${this.typeAlias}'), an interface ('${this.interfaceDeclaration}'), or enum ('${this.enumDeclaration}'). Instead, got the unsupported ${typeAnnotation.type}.`;
}
extractTypeFromTypeAnnotation(typeAnnotation: $FlowFixMe): string {
return typeAnnotation.type === 'TSTypeReference'
? typeAnnotation.typeName.name
: typeAnnotation.type;
}
getObjectProperties(typeAnnotation: $FlowFixMe): $FlowFixMe {
return typeAnnotation.members;
}
getLiteralValue(option: $FlowFixMe): $FlowFixMe {
return option.literal.value;
}
getPaperTopLevelNameDeprecated(typeAnnotation: $FlowFixMe): $FlowFixMe {
return typeAnnotation.typeParameters.params.length > 1
? typeAnnotation.typeParameters.params[1].literal.value
: null;
}
}
module.exports = {
TypeScriptParser,
};

View File

@@ -0,0 +1,157 @@
/**
* 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';
const _require = require('./errors'),
ParserError = _require.ParserError;
const path = require('path');
function extractNativeModuleName(filename) {
// this should drop everything after the file name. For Example it will drop:
// .android.js, .android.ts, .android.tsx, .ios.js, .ios.ts, .ios.tsx, .js, .ts, .tsx
return path.basename(filename).split('.')[0];
}
function createParserErrorCapturer() {
// $FlowFixMe[missing-empty-array-annot]
const errors = [];
function guard(fn) {
try {
return fn();
} catch (error) {
if (!(error instanceof ParserError)) {
throw error;
}
// $FlowFixMe[incompatible-call]
errors.push(error);
return null;
}
}
// $FlowFixMe[incompatible-return]
return [errors, guard];
}
function verifyPlatforms(hasteModuleName, moduleName) {
let cxxOnly = false;
const excludedPlatforms = new Set();
const namesToValidate = [moduleName, hasteModuleName];
namesToValidate.forEach(name => {
if (name.endsWith('Android')) {
excludedPlatforms.add('iOS');
return;
}
if (name.endsWith('IOS')) {
excludedPlatforms.add('android');
return;
}
if (name.endsWith('Windows')) {
excludedPlatforms.add('iOS');
excludedPlatforms.add('android');
return;
}
if (name.endsWith('Cxx')) {
cxxOnly = true;
excludedPlatforms.add('iOS');
excludedPlatforms.add('android');
return;
}
});
return {
cxxOnly,
excludedPlatforms: Array.from(excludedPlatforms),
};
}
// TODO(T108222691): Use flow-types for @babel/parser
function visit(astNode, visitor) {
const queue = [astNode];
while (queue.length !== 0) {
let item = queue.shift();
if (!(typeof item === 'object' && item != null)) {
continue;
}
if (
typeof item.type === 'string' &&
typeof visitor[item.type] === 'function'
) {
// Don't visit any children
visitor[item.type](item);
} else if (Array.isArray(item)) {
queue.push(...item);
} else {
queue.push(...Object.values(item));
}
}
}
function getConfigType(
// TODO(T71778680): Flow-type this node.
ast,
Visitor,
) {
let infoMap = {
isComponent: false,
isModule: false,
};
visit(ast, Visitor(infoMap));
const isModule = infoMap.isModule,
isComponent = infoMap.isComponent;
if (isModule && isComponent) {
throw new Error(
'Found type extending "TurboModule" and exported "codegenNativeComponent" declaration in one file. Split them into separated files.',
);
}
if (isModule) {
return 'module';
} else if (isComponent) {
return 'component';
} else {
return 'none';
}
}
// TODO(T71778680): Flow-type ASTNodes.
function isModuleRegistryCall(node) {
if (node.type !== 'CallExpression') {
return false;
}
const callExpression = node;
if (callExpression.callee.type !== 'MemberExpression') {
return false;
}
const memberExpression = callExpression.callee;
if (
!(
memberExpression.object.type === 'Identifier' &&
memberExpression.object.name === 'TurboModuleRegistry'
)
) {
return false;
}
if (
!(
memberExpression.property.type === 'Identifier' &&
(memberExpression.property.name === 'get' ||
memberExpression.property.name === 'getEnforcing')
)
) {
return false;
}
if (memberExpression.computed) {
return false;
}
return true;
}
module.exports = {
getConfigType,
extractNativeModuleName,
createParserErrorCapturer,
verifyPlatforms,
visit,
isModuleRegistryCall,
};

View File

@@ -0,0 +1,213 @@
/**
* 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';
const {ParserError} = require('./errors');
const path = require('path');
export type TypeDeclarationMap = {[declarationName: string]: $FlowFixMe};
export type TypeResolutionStatus =
| $ReadOnly<{
type: 'alias' | 'enum',
successful: true,
name: string,
}>
| $ReadOnly<{
successful: false,
}>;
function extractNativeModuleName(filename: string): string {
// this should drop everything after the file name. For Example it will drop:
// .android.js, .android.ts, .android.tsx, .ios.js, .ios.ts, .ios.tsx, .js, .ts, .tsx
return path.basename(filename).split('.')[0];
}
export type ParserErrorCapturer = <T>(fn: () => T) => ?T;
// $FlowFixMe[unclear-type] there's no flowtype for ASTs
export type PropAST = Object;
// $FlowFixMe[unclear-type] there's no flowtype for ASTs
export type ASTNode = Object;
function createParserErrorCapturer(): [
Array<ParserError>,
ParserErrorCapturer,
] {
// $FlowFixMe[missing-empty-array-annot]
const errors = [];
function guard<T>(fn: () => T): ?T {
try {
return fn();
} catch (error) {
if (!(error instanceof ParserError)) {
throw error;
}
// $FlowFixMe[incompatible-call]
errors.push(error);
return null;
}
}
// $FlowFixMe[incompatible-return]
return [errors, guard];
}
function verifyPlatforms(
hasteModuleName: string,
moduleName: string,
): $ReadOnly<{
cxxOnly: boolean,
excludedPlatforms: Array<'iOS' | 'android'>,
}> {
let cxxOnly = false;
const excludedPlatforms = new Set<'iOS' | 'android'>();
const namesToValidate = [moduleName, hasteModuleName];
namesToValidate.forEach(name => {
if (name.endsWith('Android')) {
excludedPlatforms.add('iOS');
return;
}
if (name.endsWith('IOS')) {
excludedPlatforms.add('android');
return;
}
if (name.endsWith('Windows')) {
excludedPlatforms.add('iOS');
excludedPlatforms.add('android');
return;
}
if (name.endsWith('Cxx')) {
cxxOnly = true;
excludedPlatforms.add('iOS');
excludedPlatforms.add('android');
return;
}
});
return {
cxxOnly,
excludedPlatforms: Array.from(excludedPlatforms),
};
}
// TODO(T108222691): Use flow-types for @babel/parser
function visit(
astNode: $FlowFixMe,
visitor: {
[type: string]: (node: $FlowFixMe) => void,
},
) {
const queue = [astNode];
while (queue.length !== 0) {
let item = queue.shift();
if (!(typeof item === 'object' && item != null)) {
continue;
}
if (
typeof item.type === 'string' &&
typeof visitor[item.type] === 'function'
) {
// Don't visit any children
visitor[item.type](item);
} else if (Array.isArray(item)) {
queue.push(...item);
} else {
queue.push(...Object.values(item));
}
}
}
function getConfigType(
// TODO(T71778680): Flow-type this node.
ast: $FlowFixMe,
Visitor: ({isComponent: boolean, isModule: boolean}) => {
[type: string]: (node: $FlowFixMe) => void,
},
): 'module' | 'component' | 'none' {
let infoMap = {
isComponent: false,
isModule: false,
};
visit(ast, Visitor(infoMap));
const {isModule, isComponent} = infoMap;
if (isModule && isComponent) {
throw new Error(
'Found type extending "TurboModule" and exported "codegenNativeComponent" declaration in one file. Split them into separated files.',
);
}
if (isModule) {
return 'module';
} else if (isComponent) {
return 'component';
} else {
return 'none';
}
}
// TODO(T71778680): Flow-type ASTNodes.
function isModuleRegistryCall(node: $FlowFixMe): boolean {
if (node.type !== 'CallExpression') {
return false;
}
const callExpression = node;
if (callExpression.callee.type !== 'MemberExpression') {
return false;
}
const memberExpression = callExpression.callee;
if (
!(
memberExpression.object.type === 'Identifier' &&
memberExpression.object.name === 'TurboModuleRegistry'
)
) {
return false;
}
if (
!(
memberExpression.property.type === 'Identifier' &&
(memberExpression.property.name === 'get' ||
memberExpression.property.name === 'getEnforcing')
)
) {
return false;
}
if (memberExpression.computed) {
return false;
}
return true;
}
module.exports = {
getConfigType,
extractNativeModuleName,
createParserErrorCapturer,
verifyPlatforms,
visit,
isModuleRegistryCall,
};