From 4eeeb57ccd07d03a3ea32cb9bb427cb166a411e7 Mon Sep 17 00:00:00 2001 From: Antoine Doubovetzky Date: Mon, 10 Oct 2022 21:32:52 +0200 Subject: [PATCH 1/4] :recycle: (codegen) extract assertGenericTypeAnnotationHasExactlyOneTypeParameter to parsers-commons --- .../parsers/__tests__/parsers-commons-test.js | 94 +++++++++++++++++++ .../src/parsers/errors.js | 4 +- .../src/parsers/flow/modules/index.js | 39 ++------ .../src/parsers/parsers-commons.js | 40 ++++++++ .../src/parsers/typescript/modules/index.js | 39 ++------ 5 files changed, 151 insertions(+), 65 deletions(-) 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 991198a1e331..950a8c75404b 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 @@ -11,6 +11,9 @@ 'use-strict'; +import {IncorrectlyParameterizedGenericParserError} from '../errors'; +import {assertGenericTypeAnnotationHasExactlyOneTypeParameter} from '../parsers-commons'; + const {wrapNullable, unwrapNullable} = require('../parsers-commons.js'); describe('wrapNullable', () => { @@ -78,3 +81,94 @@ describe('unwrapNullable', () => { }); }); }); + +describe('assertGenericTypeAnnotationHasExactlyOneTypeParameter', () => { + it('throws an IncorrectlyParameterizedGenericParserError if typeParameters is null', () => { + const moduleName = 'testModuleName'; + const typeAnnotation = { + typeParameters: null, + id: { + name: 'typeAnnotationName', + }, + }; + expect(() => + assertGenericTypeAnnotationHasExactlyOneTypeParameter( + moduleName, + typeAnnotation, + 'Flow', + ), + ).toThrow(IncorrectlyParameterizedGenericParserError); + }); + + it("throws an error if typeAnnotation.typeParameters.type doesn't have the correct value depending on language", () => { + const moduleName = 'testModuleName'; + const flowTypeAnnotation = { + typeParameters: { + type: 'TypeParameterInstantiation', + }, + id: { + name: 'typeAnnotationName', + }, + }; + expect(() => + assertGenericTypeAnnotationHasExactlyOneTypeParameter( + moduleName, + flowTypeAnnotation, + 'Flow', + ), + ).toThrow(Error); + + const typeScriptTypeAnnotation = { + typeParameters: { + type: 'TypeParameterInstantiation', + }, + typeName: { + name: 'typeAnnotationName', + }, + }; + expect(() => + assertGenericTypeAnnotationHasExactlyOneTypeParameter( + moduleName, + typeScriptTypeAnnotation, + 'TypeScript', + ), + ).toThrow(Error); + }); + + it("throws an IncorrectlyParameterizedGenericParserError if typeParameters don't have 1 exactly parameter", () => { + const moduleName = 'testModuleName'; + const typeAnnotationWithTwoParams = { + typeParameters: { + params: [1, 2], + type: 'TypeParameterInstantiation', + }, + id: { + name: 'typeAnnotationName', + }, + }; + expect(() => + assertGenericTypeAnnotationHasExactlyOneTypeParameter( + moduleName, + typeAnnotationWithTwoParams, + 'Flow', + ), + ).toThrow(IncorrectlyParameterizedGenericParserError); + + const typeAnnotationWithNoParams = { + typeParameters: { + params: [], + type: 'TypeParameterInstantiation', + }, + id: { + name: 'typeAnnotationName', + }, + }; + expect(() => + assertGenericTypeAnnotationHasExactlyOneTypeParameter( + moduleName, + typeAnnotationWithNoParams, + 'Flow', + ), + ).toThrow(IncorrectlyParameterizedGenericParserError); + }); +}); diff --git a/packages/react-native-codegen/src/parsers/errors.js b/packages/react-native-codegen/src/parsers/errors.js index dee4483ea63d..e563b35998e0 100644 --- a/packages/react-native-codegen/src/parsers/errors.js +++ b/packages/react-native-codegen/src/parsers/errors.js @@ -4,7 +4,7 @@ * 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 + * @flow strict * @format */ @@ -12,7 +12,7 @@ const invariant = require('invariant'); -type ParserType = 'Flow' | 'TypeScript'; +export type ParserType = 'Flow' | 'TypeScript'; class ParserError extends Error { nodes: $ReadOnlyArray<$FlowFixMe>; 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 75d0a6013fd8..5e43d757829d 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/index.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/index.js @@ -32,7 +32,11 @@ const { visit, isModuleRegistryCall, } = require('../utils.js'); -const {unwrapNullable, wrapNullable} = require('../../parsers-commons'); +const { + unwrapNullable, + wrapNullable, + assertGenericTypeAnnotationHasExactlyOneTypeParameter, +} = require('../../parsers-commons'); const { emitBoolean, emitDouble, @@ -42,7 +46,6 @@ const { typeAliasResolution, } = require('../../parsers-primitives'); const { - IncorrectlyParameterizedGenericParserError, MisnamedModuleInterfaceParserError, ModuleInterfaceNotFoundParserError, MoreThanOneModuleInterfaceParserError, @@ -96,6 +99,7 @@ function translateTypeAnnotation( assertGenericTypeAnnotationHasExactlyOneTypeParameter( hasteModuleName, typeAnnotation, + language, ); return wrapNullable(nullable, { @@ -107,6 +111,7 @@ function translateTypeAnnotation( assertGenericTypeAnnotationHasExactlyOneTypeParameter( hasteModuleName, typeAnnotation, + language, ); try { @@ -182,6 +187,7 @@ function translateTypeAnnotation( assertGenericTypeAnnotationHasExactlyOneTypeParameter( hasteModuleName, typeAnnotation, + language, ); const [paramType, isParamNullable] = unwrapNullable( @@ -411,35 +417,6 @@ function translateTypeAnnotation( } } -function assertGenericTypeAnnotationHasExactlyOneTypeParameter( - moduleName: string, - /** - * TODO(T71778680): This is a GenericTypeAnnotation. Flow type this node - */ - typeAnnotation: $FlowFixMe, -) { - if (typeAnnotation.typeParameters == null) { - throw new IncorrectlyParameterizedGenericParserError( - moduleName, - typeAnnotation, - language, - ); - } - - invariant( - typeAnnotation.typeParameters.type === 'TypeParameterInstantiation', - "assertGenericTypeAnnotationHasExactlyOneTypeParameter: Type parameters must be an AST node of type 'TypeParameterInstantiation'", - ); - - if (typeAnnotation.typeParameters.params.length !== 1) { - throw new IncorrectlyParameterizedGenericParserError( - moduleName, - typeAnnotation, - language, - ); - } -} - function translateFunctionTypeAnnotation( hasteModuleName: string, // TODO(T71778680): This is a FunctionTypeAnnotation. Type this. diff --git a/packages/react-native-codegen/src/parsers/parsers-commons.js b/packages/react-native-codegen/src/parsers/parsers-commons.js index d77fbfacd724..7441177b2fd2 100644 --- a/packages/react-native-codegen/src/parsers/parsers-commons.js +++ b/packages/react-native-codegen/src/parsers/parsers-commons.js @@ -16,6 +16,10 @@ import type { NativeModuleTypeAnnotation, Nullable, } from '../CodegenSchema.js'; +const {IncorrectlyParameterizedGenericParserError} = require('./errors'); +import type {ParserType} from './errors'; + +const invariant = require('invariant'); function wrapModuleSchema( nativeModuleSchema: NativeModuleSchema, @@ -52,8 +56,44 @@ function wrapNullable<+T: NativeModuleTypeAnnotation>( }; } +function assertGenericTypeAnnotationHasExactlyOneTypeParameter( + moduleName: string, + /** + * TODO(T108222691): Use flow-types for @babel/parser + */ + typeAnnotation: $FlowFixMe, + language: ParserType, +) { + if (typeAnnotation.typeParameters == null) { + throw new IncorrectlyParameterizedGenericParserError( + moduleName, + typeAnnotation, + language, + ); + } + + const typeAnnotationType = + language === 'TypeScript' + ? 'TSTypeParameterInstantiation' + : 'TypeParameterInstantiation'; + + invariant( + typeAnnotation.typeParameters.type === typeAnnotationType, + `assertGenericTypeAnnotationHasExactlyOneTypeParameter: Type parameters must be an AST node of type '${typeAnnotationType}'`, + ); + + if (typeAnnotation.typeParameters.params.length !== 1) { + throw new IncorrectlyParameterizedGenericParserError( + moduleName, + typeAnnotation, + language, + ); + } +} + module.exports = { wrapModuleSchema, unwrapNullable, wrapNullable, + assertGenericTypeAnnotationHasExactlyOneTypeParameter, }; 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 1747aa5f3704..5104b1176912 100644 --- a/packages/react-native-codegen/src/parsers/typescript/modules/index.js +++ b/packages/react-native-codegen/src/parsers/typescript/modules/index.js @@ -32,7 +32,11 @@ const { visit, isModuleRegistryCall, } = require('../utils.js'); -const {unwrapNullable, wrapNullable} = require('../../parsers-commons'); +const { + unwrapNullable, + wrapNullable, + assertGenericTypeAnnotationHasExactlyOneTypeParameter, +} = require('../../parsers-commons'); const { emitBoolean, emitDouble, @@ -42,7 +46,6 @@ const { typeAliasResolution, } = require('../../parsers-primitives'); const { - IncorrectlyParameterizedGenericParserError, MisnamedModuleInterfaceParserError, ModuleInterfaceNotFoundParserError, MoreThanOneModuleInterfaceParserError, @@ -65,7 +68,6 @@ const { IncorrectModuleRegistryCallArgumentTypeParserError, } = require('../../errors.js'); -const invariant = require('invariant'); const language = 'TypeScript'; function nullGuard(fn: () => T): ?T { @@ -208,6 +210,7 @@ function translateTypeAnnotation( assertGenericTypeAnnotationHasExactlyOneTypeParameter( hasteModuleName, typeAnnotation, + language, ); return wrapNullable(nullable, { @@ -219,6 +222,7 @@ function translateTypeAnnotation( assertGenericTypeAnnotationHasExactlyOneTypeParameter( hasteModuleName, typeAnnotation, + language, ); return translateArrayTypeAnnotation( @@ -447,35 +451,6 @@ function translateTypeAnnotation( } } -function assertGenericTypeAnnotationHasExactlyOneTypeParameter( - moduleName: string, - /** - * TODO(T108222691): Use flow-types for @babel/parser - */ - typeAnnotation: $FlowFixMe, -) { - if (typeAnnotation.typeParameters == null) { - throw new IncorrectlyParameterizedGenericParserError( - moduleName, - typeAnnotation, - language, - ); - } - - invariant( - typeAnnotation.typeParameters.type === 'TSTypeParameterInstantiation', - "assertGenericTypeAnnotationHasExactlyOneTypeParameter: Type parameters must be an AST node of type 'TSTypeParameterInstantiation'", - ); - - if (typeAnnotation.typeParameters.params.length !== 1) { - throw new IncorrectlyParameterizedGenericParserError( - moduleName, - typeAnnotation, - language, - ); - } -} - function translateFunctionTypeAnnotation( hasteModuleName: string, // TODO(T108222691): Use flow-types for @babel/parser From e9a1d8898b83297761973d581293dfb8a4a4b860 Mon Sep 17 00:00:00 2001 From: Antoine Doubovetzky Date: Tue, 11 Oct 2022 09:39:30 +0200 Subject: [PATCH 2/4] :fire: (codegen) remove unused invariant import --- packages/react-native-codegen/src/parsers/flow/modules/index.js | 1 - 1 file changed, 1 deletion(-) 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 5e43d757829d..a07356611118 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/index.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/index.js @@ -68,7 +68,6 @@ const { IncorrectModuleRegistryCallArgumentTypeParserError, } = require('../../errors.js'); -const invariant = require('invariant'); const language = 'Flow'; function nullGuard(fn: () => T): ?T { From b1d3b347ed72e60d8c060b87c51c9f3c2415520c Mon Sep 17 00:00:00 2001 From: Antoine Doubovetzky Date: Tue, 11 Oct 2022 12:37:39 +0200 Subject: [PATCH 3/4] :recycle: (codegen) split test cases in parsers-commons-test --- .../src/parsers/__tests__/parsers-commons-test.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 950a8c75404b..2f1cc69b78c0 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 @@ -135,7 +135,7 @@ describe('assertGenericTypeAnnotationHasExactlyOneTypeParameter', () => { ).toThrow(Error); }); - it("throws an IncorrectlyParameterizedGenericParserError if typeParameters don't have 1 exactly parameter", () => { + it('throws an IncorrectlyParameterizedGenericParserError if TypeAnnotation has more than one type parameter', () => { const moduleName = 'testModuleName'; const typeAnnotationWithTwoParams = { typeParameters: { @@ -153,7 +153,10 @@ describe('assertGenericTypeAnnotationHasExactlyOneTypeParameter', () => { 'Flow', ), ).toThrow(IncorrectlyParameterizedGenericParserError); + }); + it('throws an IncorrectlyParameterizedGenericParserError if TypeAnnotation has no type parameter', () => { + const moduleName = 'testModuleName'; const typeAnnotationWithNoParams = { typeParameters: { params: [], From 1ce4c9021f2d461be05a6ce156ca6d983744c72a Mon Sep 17 00:00:00 2001 From: Antoine Doubovetzky Date: Mon, 10 Oct 2022 23:08:13 +0200 Subject: [PATCH 4/4] :recycle: (codegen) extract emitPromise function to parsers-primitives --- .../__tests__/parsers-primitives-test.js | 71 +++++++++++++++++++ .../src/parsers/flow/modules/index.js | 8 +-- .../src/parsers/parsers-primitives.js | 25 ++++++- .../src/parsers/typescript/modules/index.js | 8 +-- 4 files changed, 101 insertions(+), 11 deletions(-) diff --git a/packages/react-native-codegen/src/parsers/__tests__/parsers-primitives-test.js b/packages/react-native-codegen/src/parsers/__tests__/parsers-primitives-test.js index 40ec6106add7..8d22808443cd 100644 --- a/packages/react-native-codegen/src/parsers/__tests__/parsers-primitives-test.js +++ b/packages/react-native-codegen/src/parsers/__tests__/parsers-primitives-test.js @@ -18,6 +18,7 @@ const { emitInt32, emitRootTag, typeAliasResolution, + emitPromise, } = require('../parsers-primitives.js'); describe('emitBoolean', () => { @@ -244,3 +245,73 @@ describe('typeAliasResolution', () => { }); }); }); + +describe('emitPromise', () => { + const moduleName = 'testModuleName'; + const language = 'Flow'; + describe("when typeAnnotation doesn't have exactly one typeParameter", () => { + const typeAnnotation = { + typeParameters: { + params: [1, 2], + type: 'TypeParameterInstantiation', + }, + id: { + name: 'typeAnnotationName', + }, + }; + it('throws an IncorrectlyParameterizedGenericParserError error', () => { + const nullable = false; + expect(() => + emitPromise(moduleName, typeAnnotation, language, nullable), + ).toThrow(); + }); + }); + + describe("when typeAnnotation doesn't has exactly one typeParameter", () => { + const typeAnnotation = { + typeParameters: { + params: [1], + type: 'TypeParameterInstantiation', + }, + id: { + name: 'typeAnnotationName', + }, + }; + + describe('when nullable is true', () => { + const nullable = true; + it('returns nullable type annotation', () => { + const result = emitPromise( + moduleName, + typeAnnotation, + language, + nullable, + ); + const expected = { + type: 'NullableTypeAnnotation', + typeAnnotation: { + type: 'PromiseTypeAnnotation', + }, + }; + + expect(result).toEqual(expected); + }); + }); + describe('when nullable is false', () => { + const nullable = false; + it('returns non nullable type annotation', () => { + const result = emitPromise( + moduleName, + typeAnnotation, + language, + nullable, + ); + const expected = { + type: 'PromiseTypeAnnotation', + }; + + expect(result).toEqual(expected); + }); + }); + }); +}); 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 a07356611118..8ffd9db16230 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/index.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/index.js @@ -44,6 +44,7 @@ const { emitInt32, emitRootTag, typeAliasResolution, + emitPromise, } = require('../../parsers-primitives'); const { MisnamedModuleInterfaceParserError, @@ -95,15 +96,12 @@ function translateTypeAnnotation( return emitRootTag(nullable); } case 'Promise': { - assertGenericTypeAnnotationHasExactlyOneTypeParameter( + return emitPromise( hasteModuleName, typeAnnotation, language, + nullable, ); - - return wrapNullable(nullable, { - type: 'PromiseTypeAnnotation', - }); } case 'Array': case '$ReadOnlyArray': { diff --git a/packages/react-native-codegen/src/parsers/parsers-primitives.js b/packages/react-native-codegen/src/parsers/parsers-primitives.js index b949ed535adf..b497d1398572 100644 --- a/packages/react-native-codegen/src/parsers/parsers-primitives.js +++ b/packages/react-native-codegen/src/parsers/parsers-primitives.js @@ -21,10 +21,15 @@ import type { Int32TypeAnnotation, ReservedTypeAnnotation, ObjectTypeAnnotation, + NativeModulePromiseTypeAnnotation, } from '../CodegenSchema'; +import type {ParserType} from './errors'; import type {TypeAliasResolutionStatus} from './utils'; -const {wrapNullable} = require('./parsers-commons'); +const { + wrapNullable, + assertGenericTypeAnnotationHasExactlyOneTypeParameter, +} = require('./parsers-commons'); function emitBoolean(nullable: boolean): Nullable { return wrapNullable(nullable, { @@ -113,6 +118,23 @@ function typeAliasResolution( }); } +function emitPromise( + hasteModuleName: string, + typeAnnotation: $FlowFixMe, + language: ParserType, + nullable: boolean, +): Nullable { + assertGenericTypeAnnotationHasExactlyOneTypeParameter( + hasteModuleName, + typeAnnotation, + language, + ); + + return wrapNullable(nullable, { + type: 'PromiseTypeAnnotation', + }); +} + module.exports = { emitBoolean, emitDouble, @@ -120,4 +142,5 @@ module.exports = { emitNumber, emitRootTag, typeAliasResolution, + emitPromise, }; 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 5104b1176912..c6dbbb9ef7f3 100644 --- a/packages/react-native-codegen/src/parsers/typescript/modules/index.js +++ b/packages/react-native-codegen/src/parsers/typescript/modules/index.js @@ -44,6 +44,7 @@ const { emitInt32, emitRootTag, typeAliasResolution, + emitPromise, } = require('../../parsers-primitives'); const { MisnamedModuleInterfaceParserError, @@ -207,15 +208,12 @@ function translateTypeAnnotation( return emitRootTag(nullable); } case 'Promise': { - assertGenericTypeAnnotationHasExactlyOneTypeParameter( + return emitPromise( hasteModuleName, typeAnnotation, language, + nullable, ); - - return wrapNullable(nullable, { - type: 'PromiseTypeAnnotation', - }); } case 'Array': case 'ReadonlyArray': {