mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Summary: This PR is task 74 from https://github.com/facebook/react-native/issues/34872: > Move getTypes functions from utils.js to specific Parsers. Right now we have two Parser classes that takes care of the language specific details and two utils files that contains similar logic. We would like to move everything under the Parsers classes for better OOP architecture and to encourage code-reuse. ## Changelog <!-- Help reviewers and the release process by writing your own changelog entry. Pick one each for the category and type tags: [ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message For more details, see: https://reactnative.dev/contributing/changelogs-in-pull-requests --> [Internal] [Changed] - Replace getTypes functions with parser specific methods Pull Request resolved: https://github.com/facebook/react-native/pull/36225 Test Plan: I tested using Jest and Flow commands. Reviewed By: rshest Differential Revision: D43453454 Pulled By: cipolleschi fbshipit-source-id: 0eebcb55e1af3319e2c35bb462980046329a2c09
261 lines
7.2 KiB
JavaScript
261 lines
7.2 KiB
JavaScript
/**
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow strict
|
|
* @format
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
import type {
|
|
UnionTypeAnnotationMemberType,
|
|
SchemaType,
|
|
NamedShape,
|
|
Nullable,
|
|
NativeModuleParamTypeAnnotation,
|
|
NativeModuleEnumMemberType,
|
|
NativeModuleEnumMembers,
|
|
} from '../../CodegenSchema';
|
|
import type {ParserType} from '../errors';
|
|
import type {Parser} from '../parser';
|
|
import type {TypeDeclarationMap} from '../utils';
|
|
|
|
// $FlowFixMe[untyped-import] there's no flowtype flow-parser
|
|
const flowParser = require('flow-parser');
|
|
|
|
const {buildSchema} = require('../parsers-commons');
|
|
const {Visitor} = require('./Visitor');
|
|
const {buildComponentSchema} = require('./components');
|
|
const {wrapComponentSchema} = require('../schema.js');
|
|
const {buildModuleSchema} = require('./modules');
|
|
|
|
const fs = require('fs');
|
|
|
|
const {
|
|
UnsupportedObjectPropertyTypeAnnotationParserError,
|
|
} = require('../errors');
|
|
|
|
class FlowParser implements Parser {
|
|
typeParameterInstantiation: string = 'TypeParameterInstantiation';
|
|
|
|
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';
|
|
}
|
|
|
|
nameForGenericTypeAnnotation(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,
|
|
);
|
|
}
|
|
|
|
parseModuleFixture(filename: string): SchemaType {
|
|
const contents = fs.readFileSync(filename, 'utf8');
|
|
|
|
return this.parseString(contents, 'path/NativeSampleTurboModule.js');
|
|
}
|
|
|
|
getAst(contents: string): $FlowFixMe {
|
|
return flowParser.parse(contents, {
|
|
enums: true,
|
|
});
|
|
}
|
|
|
|
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'
|
|
);
|
|
}
|
|
|
|
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;
|
|
}, {});
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
FlowParser,
|
|
};
|