From 114d5a8a1764a4dd6300a295d132e10779b132d7 Mon Sep 17 00:00:00 2001 From: Charles Dudley Date: Mon, 20 Dec 2021 14:15:57 -0800 Subject: [PATCH] TypeScript parser foundation Summary: These are utility functions that the TypeScript parser uses and are copied from and follows the same logic as the flow parser with some TypeScript specific changes. Also added dependency of `babel/parser` as the parsing engine we're using for TypeScript. Changelog: [General][Add] - Add foundation for WIP TypeScript parser for Codegen Reviewed By: RSNara Differential Revision: D33080527 fbshipit-source-id: d4bd515af549a41f07a2e3ee1a16b5ed678180b2 --- packages/react-native-codegen/package.json | 1 + .../src/parsers/flow/utils.js | 5 ++ .../src/parsers/typescript/utils.js | 67 ++++++++++++------- 3 files changed, 48 insertions(+), 25 deletions(-) diff --git a/packages/react-native-codegen/package.json b/packages/react-native-codegen/package.json index 445a17464b..7e75ffd7ed 100644 --- a/packages/react-native-codegen/package.json +++ b/packages/react-native-codegen/package.json @@ -18,6 +18,7 @@ "lib" ], "dependencies": { + "@babel/parser": "^7.14.0", "flow-parser": "^0.121.0", "jscodeshift": "^0.11.0", "nullthrows": "^1.1.1" diff --git a/packages/react-native-codegen/src/parsers/flow/utils.js b/packages/react-native-codegen/src/parsers/flow/utils.js index 70d4536a12..593e4642d1 100644 --- a/packages/react-native-codegen/src/parsers/flow/utils.js +++ b/packages/react-native-codegen/src/parsers/flow/utils.js @@ -197,6 +197,11 @@ function isModuleRegistryCall(node: $FlowFixMe): boolean { ) { return false; } + + if (memberExpression.computed) { + return false; + } + return true; } diff --git a/packages/react-native-codegen/src/parsers/typescript/utils.js b/packages/react-native-codegen/src/parsers/typescript/utils.js index 70d4536a12..9f80c8e11f 100644 --- a/packages/react-native-codegen/src/parsers/typescript/utils.js +++ b/packages/react-native-codegen/src/parsers/typescript/utils.js @@ -13,12 +13,7 @@ const {ParserError} = require('./errors'); /** - * 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 + * TODO(T108222691): Use flow-types for @babel/parser */ export type TypeDeclarationMap = {[declarationName: string]: $FlowFixMe}; @@ -26,22 +21,23 @@ function getTypes(ast: $FlowFixMe): TypeDeclarationMap { return ast.body.reduce((types, node) => { if (node.type === 'ExportNamedDeclaration' && node.exportKind === 'type') { if ( - node.declaration.type === 'TypeAlias' || - node.declaration.type === 'InterfaceDeclaration' + node.declaration.type === 'TSTypeAliasDeclaration' || + node.declaration.type === 'TSInterfaceDeclaration' ) { types[node.declaration.id.name] = node.declaration; } } else if ( - node.type === 'TypeAlias' || - node.type === 'InterfaceDeclaration' + node.type === 'TSTypeAliasDeclaration' || + node.type === 'TSInterfaceDeclaration' ) { types[node.id.name] = node; } + return types; }, {}); } -// $FlowFixMe[unclear-type] there's no flowtype for ASTs +// $FlowFixMe[unclear-type] Use flow-types for @babel/parser export type ASTNode = Object; const invariant = require('invariant'); @@ -56,7 +52,7 @@ type TypeAliasResolutionStatus = }>; function resolveTypeAnnotation( - // TODO(T71778680): This is an Flow TypeAnnotation. Flow-type this + // TODO(T108222691): Use flow-types for @babel/parser typeAnnotation: $FlowFixMe, types: TypeDeclarationMap, ): { @@ -69,32 +65,43 @@ function resolveTypeAnnotation( 'resolveTypeAnnotation(): typeAnnotation cannot be null', ); - let node = typeAnnotation; + let node = + typeAnnotation.type === 'TSTypeAnnotation' + ? typeAnnotation.typeAnnotation + : typeAnnotation; let nullable = false; let typeAliasResolutionStatus: TypeAliasResolutionStatus = { successful: false, }; for (;;) { - if (node.type === 'NullableTypeAnnotation') { + // Check for optional type in union e.g. T | null | void + if ( + node.type === 'TSUnionType' && + node.types.some( + t => t.type === 'TSNullKeyword' || t.type === 'TSVoidKeyword', + ) + ) { + node = node.types.filter( + t => t.type !== 'TSNullKeyword' && t.type !== 'TSVoidKeyword', + )[0]; nullable = true; - node = node.typeAnnotation; - } else if (node.type === 'GenericTypeAnnotation') { + } else if (node.type === 'TSTypeReference') { typeAliasResolutionStatus = { successful: true, - aliasName: node.id.name, + aliasName: node.typeName.name, }; - const resolvedTypeAnnotation = types[node.id.name]; + const resolvedTypeAnnotation = types[node.typeName.name]; if (resolvedTypeAnnotation == null) { break; } invariant( - resolvedTypeAnnotation.type === 'TypeAlias', - `GenericTypeAnnotation '${node.id.name}' must resolve to a TypeAlias. Instead, it resolved to a '${resolvedTypeAnnotation.type}'`, + resolvedTypeAnnotation.type === 'TSTypeAliasDeclaration', + `GenericTypeAnnotation '${node.typeName.name}' must resolve to a TSTypeAliasDeclaration. Instead, it resolved to a '${resolvedTypeAnnotation.type}'`, ); - node = resolvedTypeAnnotation.right; + node = resolvedTypeAnnotation.typeAnnotation; } else { break; } @@ -108,9 +115,14 @@ function resolveTypeAnnotation( } function getValueFromTypes(value: ASTNode, types: TypeDeclarationMap): ASTNode { - if (value.type === 'GenericTypeAnnotation' && types[value.id.name]) { - return getValueFromTypes(types[value.id.name].right, types); + if (value.type === 'TSTypeReference' && types[value.typeName.name]) { + return getValueFromTypes(types[value.typeName.name], types); } + + if (value.type === 'TSTypeAliasDeclaration') { + return value.typeAnnotation; + } + return value; } @@ -137,7 +149,7 @@ function createParserErrorCapturer(): [ return [errors, guard]; } -// TODO(T71778680): Flow-type ASTNodes. +// TODO(T108222691): Use flow-types for @babel/parser function visit( astNode: $FlowFixMe, visitor: { @@ -166,7 +178,7 @@ function visit( } } -// TODO(T71778680): Flow-type ASTNodes. +// TODO(T108222691): Use flow-types for @babel/parser function isModuleRegistryCall(node: $FlowFixMe): boolean { if (node.type !== 'CallExpression') { return false; @@ -197,6 +209,11 @@ function isModuleRegistryCall(node: $FlowFixMe): boolean { ) { return false; } + + if (memberExpression.computed) { + return false; + } + return true; }