diff --git a/packages/react-native-codegen/src/parsers/__tests__/parsers-commons-test.js b/packages/react-native-codegen/src/parsers/__tests__/parsers-commons-test.js index 4704e445f75651..047bb00d8a545f 100644 --- a/packages/react-native-codegen/src/parsers/__tests__/parsers-commons-test.js +++ b/packages/react-native-codegen/src/parsers/__tests__/parsers-commons-test.js @@ -58,7 +58,6 @@ const flowParser = new FlowParser(); const {flowTranslateTypeAnnotation} = require('../flow/modules/index'); const typeScriptTranslateTypeAnnotation = require('../typescript/modules/index'); -const {resolveTypeAnnotation} = require('../flow/utils'); beforeEach(() => { jest.clearAllMocks(); @@ -409,7 +408,7 @@ describe('buildSchemaFromConfigType', () => { (_ast, _parser) => componentSchemaMock, ); const buildModuleSchemaMock = jest.fn( - (_0, _1, _2, _3, _4, _5) => moduleSchemaMock, + (_0, _1, _2, _3, _4) => moduleSchemaMock, ); const buildSchemaFromConfigTypeHelper = ( @@ -424,7 +423,6 @@ describe('buildSchemaFromConfigType', () => { buildComponentSchemaMock, buildModuleSchemaMock, parser, - resolveTypeAnnotation, flowTranslateTypeAnnotation, ); @@ -507,7 +505,6 @@ describe('buildSchemaFromConfigType', () => { astMock, expect.any(Function), parser, - resolveTypeAnnotation, flowTranslateTypeAnnotation, ); @@ -679,7 +676,6 @@ describe('buildSchema', () => { buildModuleSchema, Visitor, parser, - resolveTypeAnnotation, flowTranslateTypeAnnotation, ); @@ -713,7 +709,6 @@ describe('buildSchema', () => { buildModuleSchema, Visitor, flowParser, - resolveTypeAnnotation, flowTranslateTypeAnnotation, ); @@ -768,7 +763,6 @@ describe('buildSchema', () => { buildModuleSchema, Visitor, flowParser, - resolveTypeAnnotation, flowTranslateTypeAnnotation, ); @@ -1064,7 +1058,6 @@ describe('buildModuleSchema', () => { ast, tryParse, flowParser, - resolveTypeAnnotation, flowTranslateTypeAnnotation, ), ).toThrow(expected); @@ -1079,7 +1072,6 @@ describe('buildModuleSchema', () => { ast, tryParse, flowParser, - resolveTypeAnnotation, flowTranslateTypeAnnotation, ), ).not.toThrow(); @@ -1116,7 +1108,6 @@ describe('buildModuleSchema', () => { ast, tryParse, flowParser, - resolveTypeAnnotation, flowTranslateTypeAnnotation, ), ).toThrow(expected); @@ -1131,7 +1122,6 @@ describe('buildModuleSchema', () => { ast, tryParse, flowParser, - resolveTypeAnnotation, flowTranslateTypeAnnotation, ), ).not.toThrow(); @@ -1171,7 +1161,6 @@ describe('buildModuleSchema', () => { ast, tryParse, flowParser, - resolveTypeAnnotation, flowTranslateTypeAnnotation, ), ).toThrow(expected); @@ -1186,7 +1175,6 @@ describe('buildModuleSchema', () => { ast, tryParse, flowParser, - resolveTypeAnnotation, flowTranslateTypeAnnotation, ), ).not.toThrow(); @@ -1229,7 +1217,6 @@ describe('buildModuleSchema', () => { ast, tryParse, flowParser, - resolveTypeAnnotation, flowTranslateTypeAnnotation, ); diff --git a/packages/react-native-codegen/src/parsers/flow/modules/index.js b/packages/react-native-codegen/src/parsers/flow/modules/index.js index 75352fea2bf67e..1859a59666b92c 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/index.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/index.js @@ -22,7 +22,6 @@ import type { import type {Parser} from '../../parser'; import type {ParserErrorCapturer, TypeDeclarationMap} from '../../utils'; -const {resolveTypeAnnotation} = require('../utils'); const { unwrapNullable, wrapNullable, @@ -60,7 +59,7 @@ function translateTypeAnnotation( parser: Parser, ): Nullable { const {nullable, typeAnnotation, typeResolutionStatus} = - resolveTypeAnnotation(flowTypeAnnotation, types); + parser.getResolvedTypeAnnotation(flowTypeAnnotation, types); switch (typeAnnotation.type) { case 'GenericTypeAnnotation': { diff --git a/packages/react-native-codegen/src/parsers/flow/parser.js b/packages/react-native-codegen/src/parsers/flow/parser.js index fdec2c5cb729c2..e026fa11d06063 100644 --- a/packages/react-native-codegen/src/parsers/flow/parser.js +++ b/packages/react-native-codegen/src/parsers/flow/parser.js @@ -23,7 +23,13 @@ import type { } from '../../CodegenSchema'; import type {ParserType} from '../errors'; import type {Parser} from '../parser'; -import type {ParserErrorCapturer, TypeDeclarationMap, PropAST} from '../utils'; +import type { + ParserErrorCapturer, + TypeDeclarationMap, + PropAST, + TypeResolutionStatus, +} from '../utils'; +const invariant = require('invariant'); const {flowTranslateTypeAnnotation} = require('./modules'); @@ -35,7 +41,6 @@ const {Visitor} = require('../parsers-primitives'); const {buildComponentSchema} = require('./components'); const {wrapComponentSchema} = require('../schema.js'); const {buildModuleSchema} = require('../parsers-commons.js'); -const {resolveTypeAnnotation} = require('./utils'); const fs = require('fs'); @@ -106,7 +111,6 @@ class FlowParser implements Parser { buildModuleSchema, Visitor, this, - resolveTypeAnnotation, flowTranslateTypeAnnotation, ); } @@ -343,6 +347,75 @@ class FlowParser implements Parser { property.value.type === 'NullableTypeAnnotation' || property.optional ); } + + 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, + }; + } } module.exports = { diff --git a/packages/react-native-codegen/src/parsers/flow/utils.js b/packages/react-native-codegen/src/parsers/flow/utils.js index 3692cf16f89b7c..6359188fb77f74 100644 --- a/packages/react-native-codegen/src/parsers/flow/utils.js +++ b/packages/react-native-codegen/src/parsers/flow/utils.js @@ -10,83 +10,11 @@ 'use strict'; -import type {TypeResolutionStatus, TypeDeclarationMap} from '../utils'; +import type {TypeDeclarationMap} from '../utils'; // $FlowFixMe[unclear-type] there's no flowtype for ASTs export type ASTNode = Object; -const invariant = require('invariant'); - -function resolveTypeAnnotation( - // TODO(T71778680): This is an Flow TypeAnnotation. Flow-type this - 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, - }; -} - function getValueFromTypes(value: ASTNode, types: TypeDeclarationMap): ASTNode { if (value.type === 'GenericTypeAnnotation' && types[value.id.name]) { return getValueFromTypes(types[value.id.name].right, types); @@ -96,5 +24,4 @@ function getValueFromTypes(value: ASTNode, types: TypeDeclarationMap): ASTNode { module.exports = { getValueFromTypes, - resolveTypeAnnotation, }; diff --git a/packages/react-native-codegen/src/parsers/parser.js b/packages/react-native-codegen/src/parsers/parser.js index ea0325aff51f2f..930571ade46e2a 100644 --- a/packages/react-native-codegen/src/parsers/parser.js +++ b/packages/react-native-codegen/src/parsers/parser.js @@ -22,7 +22,12 @@ import type { NativeModuleEnumMap, } from '../CodegenSchema'; import type {ParserType} from './errors'; -import type {ParserErrorCapturer, TypeDeclarationMap, PropAST} from './utils'; +import type { + ParserErrorCapturer, + TypeDeclarationMap, + PropAST, + TypeResolutionStatus, +} from './utils'; /** * This is the main interface for Parsers of various languages. @@ -276,4 +281,13 @@ export interface Parser { * @returns: a boolean specifying if the Property is optional */ isOptionalProperty(property: $FlowFixMe): boolean; + + getResolvedTypeAnnotation( + typeAnnotation: $FlowFixMe, + types: TypeDeclarationMap, + ): { + nullable: boolean, + typeAnnotation: $FlowFixMe, + typeResolutionStatus: TypeResolutionStatus, + }; } diff --git a/packages/react-native-codegen/src/parsers/parserMock.js b/packages/react-native-codegen/src/parsers/parserMock.js index d9c78e7efd135d..216d552e4198ee 100644 --- a/packages/react-native-codegen/src/parsers/parserMock.js +++ b/packages/react-native-codegen/src/parsers/parserMock.js @@ -23,7 +23,13 @@ import type { NativeModuleAliasMap, NativeModuleEnumMap, } from '../CodegenSchema'; -import type {ParserErrorCapturer, PropAST, TypeDeclarationMap} from './utils'; +import type { + ParserErrorCapturer, + PropAST, + TypeDeclarationMap, + TypeResolutionStatus, +} from './utils'; +import invariant from 'invariant'; // $FlowFixMe[untyped-import] there's no flowtype flow-parser const flowParser = require('flow-parser'); @@ -255,4 +261,73 @@ export class MockedParser implements Parser { isOptionalProperty(property: $FlowFixMe): boolean { return property.optional || 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, + }; + } } diff --git a/packages/react-native-codegen/src/parsers/parsers-commons.js b/packages/react-native-codegen/src/parsers/parsers-commons.js index b03b2bd9190c44..c01bd1e743af85 100644 --- a/packages/react-native-codegen/src/parsers/parsers-commons.js +++ b/packages/react-native-codegen/src/parsers/parsers-commons.js @@ -321,7 +321,6 @@ function buildPropertySchema( enumMap: {...NativeModuleEnumMap}, tryParse: ParserErrorCapturer, cxxOnly: boolean, - resolveTypeAnnotation: $FlowFixMe, translateTypeAnnotation: $FlowFixMe, parser: Parser, ): NativeModulePropertyShape { @@ -336,7 +335,10 @@ function buildPropertySchema( : property.typeAnnotation; } - ({nullable, typeAnnotation: value} = resolveTypeAnnotation(value, types)); + ({nullable, typeAnnotation: value} = parser.getResolvedTypeAnnotation( + value, + types, + )); throwIfModuleTypeIsUnsupported( hasteModuleName, @@ -380,11 +382,9 @@ function buildSchemaFromConfigType( ast: $FlowFixMe, tryParse: ParserErrorCapturer, parser: Parser, - resolveTypeAnnotation: $FlowFixMe, translateTypeAnnotation: $FlowFixMe, ) => NativeModuleSchema, parser: Parser, - resolveTypeAnnotation: $FlowFixMe, translateTypeAnnotation: $FlowFixMe, ): SchemaType { switch (configType) { @@ -405,7 +405,6 @@ function buildSchemaFromConfigType( ast, tryParse, parser, - resolveTypeAnnotation, translateTypeAnnotation, ), ); @@ -447,14 +446,12 @@ function buildSchema( ast: $FlowFixMe, tryParse: ParserErrorCapturer, parser: Parser, - resolveTypeAnnotation: $FlowFixMe, translateTypeAnnotation: $FlowFixMe, ) => NativeModuleSchema, Visitor: ({isComponent: boolean, isModule: boolean}) => { [type: string]: (node: $FlowFixMe) => void, }, parser: Parser, - resolveTypeAnnotation: $FlowFixMe, translateTypeAnnotation: $FlowFixMe, ): SchemaType { // Early return for non-Spec JavaScript files @@ -476,7 +473,6 @@ function buildSchema( buildComponentSchema, buildModuleSchema, parser, - resolveTypeAnnotation, translateTypeAnnotation, ); } @@ -571,7 +567,6 @@ const buildModuleSchema = ( ast: $FlowFixMe, tryParse: ParserErrorCapturer, parser: Parser, - resolveTypeAnnotation: $FlowFixMe, translateTypeAnnotation: $FlowFixMe, ): NativeModuleSchema => { const language = parser.language(); @@ -639,7 +634,6 @@ const buildModuleSchema = ( enumMap, tryParse, cxxOnly, - resolveTypeAnnotation, translateTypeAnnotation, parser, ), diff --git a/packages/react-native-codegen/src/parsers/typescript/modules/index.js b/packages/react-native-codegen/src/parsers/typescript/modules/index.js index 6123cf4321a736..1defa0347a96d3 100644 --- a/packages/react-native-codegen/src/parsers/typescript/modules/index.js +++ b/packages/react-native-codegen/src/parsers/typescript/modules/index.js @@ -28,8 +28,6 @@ import type { const {flattenIntersectionType} = require('../parseTopLevelType'); const {flattenProperties} = require('../components/componentsUtils'); -const {resolveTypeAnnotation} = require('../utils'); - const {parseObjectProperty} = require('../../parsers-commons'); const { @@ -190,7 +188,7 @@ function translateTypeAnnotation( parser: Parser, ): Nullable { const {nullable, typeAnnotation, typeResolutionStatus} = - resolveTypeAnnotation(typeScriptTypeAnnotation, types); + parser.getResolvedTypeAnnotation(typeScriptTypeAnnotation, types); switch (typeAnnotation.type) { case 'TSArrayType': { diff --git a/packages/react-native-codegen/src/parsers/typescript/parser.js b/packages/react-native-codegen/src/parsers/typescript/parser.js index f583f594834eaa..fa6ced9ba1d38a 100644 --- a/packages/react-native-codegen/src/parsers/typescript/parser.js +++ b/packages/react-native-codegen/src/parsers/typescript/parser.js @@ -23,7 +23,14 @@ import type { } from '../../CodegenSchema'; import type {ParserType} from '../errors'; import type {Parser} from '../parser'; -import type {ParserErrorCapturer, TypeDeclarationMap, PropAST} from '../utils'; +import type { + ParserErrorCapturer, + TypeDeclarationMap, + PropAST, + TypeResolutionStatus, +} from '../utils'; + +const invariant = require('invariant'); const {typeScriptTranslateTypeAnnotation} = require('./modules'); @@ -35,7 +42,7 @@ const {Visitor} = require('../parsers-primitives'); const {buildComponentSchema} = require('./components'); const {wrapComponentSchema} = require('../schema.js'); const {buildModuleSchema} = require('../parsers-commons.js'); -const {resolveTypeAnnotation} = require('./utils'); +const {parseTopLevelType} = require('./parseTopLevelType'); const fs = require('fs'); @@ -108,7 +115,6 @@ class TypeScriptParser implements Parser { buildModuleSchema, Visitor, this, - resolveTypeAnnotation, typeScriptTranslateTypeAnnotation, ); } @@ -340,6 +346,86 @@ class TypeScriptParser implements Parser { isOptionalProperty(property: $FlowFixMe): boolean { return property.optional || false; } + + getResolvedTypeAnnotation( + // TODO(T108222691): Use flow-types for @babel/parser + typeAnnotation: $FlowFixMe, + types: TypeDeclarationMap, + ): { + 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 resolvedTypeAnnotation = types[node.typeName.name]; + if (resolvedTypeAnnotation == null) { + break; + } + + switch (resolvedTypeAnnotation.type) { + case 'TSTypeAliasDeclaration': { + typeResolutionStatus = { + successful: true, + type: 'alias', + name: node.typeName.name, + }; + node = resolvedTypeAnnotation.typeAnnotation; + break; + } + case 'TSInterfaceDeclaration': { + typeResolutionStatus = { + successful: true, + type: 'alias', + name: node.typeName.name, + }; + node = resolvedTypeAnnotation; + break; + } + case 'TSEnumDeclaration': { + typeResolutionStatus = { + successful: true, + type: 'enum', + name: node.typeName.name, + }; + node = resolvedTypeAnnotation; + break; + } + default: { + throw new TypeError( + `A non GenericTypeAnnotation must be a type declaration ('TSTypeAliasDeclaration'), an interface ('TSInterfaceDeclaration'), or enum ('TSEnumDeclaration'). Instead, got the unsupported ${resolvedTypeAnnotation.type}.`, + ); + } + } + } + + return { + nullable: nullable, + typeAnnotation: node, + typeResolutionStatus, + }; + } } module.exports = { diff --git a/packages/react-native-codegen/src/parsers/typescript/utils.js b/packages/react-native-codegen/src/parsers/typescript/utils.js index ffb7145d620852..5fe88cd8b288b4 100644 --- a/packages/react-native-codegen/src/parsers/typescript/utils.js +++ b/packages/react-native-codegen/src/parsers/typescript/utils.js @@ -10,95 +10,5 @@ 'use strict'; -import type {TypeResolutionStatus, TypeDeclarationMap} from '../utils'; - -const {parseTopLevelType} = require('./parseTopLevelType'); - // $FlowFixMe[unclear-type] Use flow-types for @babel/parser export type ASTNode = Object; - -const invariant = require('invariant'); - -function resolveTypeAnnotation( - // TODO(T108222691): Use flow-types for @babel/parser - typeAnnotation: $FlowFixMe, - types: TypeDeclarationMap, -): { - 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 resolvedTypeAnnotation = types[node.typeName.name]; - if (resolvedTypeAnnotation == null) { - break; - } - - switch (resolvedTypeAnnotation.type) { - case 'TSTypeAliasDeclaration': { - typeResolutionStatus = { - successful: true, - type: 'alias', - name: node.typeName.name, - }; - node = resolvedTypeAnnotation.typeAnnotation; - break; - } - case 'TSInterfaceDeclaration': { - typeResolutionStatus = { - successful: true, - type: 'alias', - name: node.typeName.name, - }; - node = resolvedTypeAnnotation; - break; - } - case 'TSEnumDeclaration': { - typeResolutionStatus = { - successful: true, - type: 'enum', - name: node.typeName.name, - }; - node = resolvedTypeAnnotation; - break; - } - default: { - throw new TypeError( - `A non GenericTypeAnnotation must be a type declaration ('TSTypeAliasDeclaration'), an interface ('TSInterfaceDeclaration'), or enum ('TSEnumDeclaration'). Instead, got the unsupported ${resolvedTypeAnnotation.type}.`, - ); - } - } - } - - return { - nullable: nullable, - typeAnnotation: node, - typeResolutionStatus, - }; -} - -module.exports = { - resolveTypeAnnotation, -};