Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@

'use-strict';

import {IncorrectlyParameterizedGenericParserError} from '../errors';
import {assertGenericTypeAnnotationHasExactlyOneTypeParameter} from '../parsers-commons';

const {wrapNullable, unwrapNullable} = require('../parsers-commons.js');

describe('wrapNullable', () => {
Expand Down Expand Up @@ -78,3 +81,97 @@ 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 TypeAnnotation has more than one type parameter', () => {
const moduleName = 'testModuleName';
const typeAnnotationWithTwoParams = {
typeParameters: {
params: [1, 2],
type: 'TypeParameterInstantiation',
},
id: {
name: 'typeAnnotationName',
},
};
expect(() =>
assertGenericTypeAnnotationHasExactlyOneTypeParameter(
moduleName,
typeAnnotationWithTwoParams,
'Flow',
),
).toThrow(IncorrectlyParameterizedGenericParserError);
});

it('throws an IncorrectlyParameterizedGenericParserError if TypeAnnotation has no type parameter', () => {
const moduleName = 'testModuleName';
const typeAnnotationWithNoParams = {
typeParameters: {
params: [],
type: 'TypeParameterInstantiation',
},
id: {
name: 'typeAnnotationName',
},
};
expect(() =>
assertGenericTypeAnnotationHasExactlyOneTypeParameter(
moduleName,
typeAnnotationWithNoParams,
'Flow',
),
).toThrow(IncorrectlyParameterizedGenericParserError);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const {
emitInt32,
emitRootTag,
typeAliasResolution,
emitPromise,
} = require('../parsers-primitives.js');

describe('emitBoolean', () => {
Expand Down Expand Up @@ -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);
});
});
});
});
4 changes: 2 additions & 2 deletions packages/react-native-codegen/src/parsers/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
* 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
*/

'use strict';

const invariant = require('invariant');

type ParserType = 'Flow' | 'TypeScript';
export type ParserType = 'Flow' | 'TypeScript';

class ParserError extends Error {
nodes: $ReadOnlyArray<$FlowFixMe>;
Expand Down
48 changes: 11 additions & 37 deletions packages/react-native-codegen/src/parsers/flow/modules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,21 @@ const {
visit,
isModuleRegistryCall,
} = require('../utils.js');
const {unwrapNullable, wrapNullable} = require('../../parsers-commons');
const {
unwrapNullable,
wrapNullable,
assertGenericTypeAnnotationHasExactlyOneTypeParameter,
} = require('../../parsers-commons');
const {
emitBoolean,
emitDouble,
emitNumber,
emitInt32,
emitRootTag,
typeAliasResolution,
emitPromise,
} = require('../../parsers-primitives');
const {
IncorrectlyParameterizedGenericParserError,
MisnamedModuleInterfaceParserError,
ModuleInterfaceNotFoundParserError,
MoreThanOneModuleInterfaceParserError,
Expand All @@ -65,7 +69,6 @@ const {
IncorrectModuleRegistryCallArgumentTypeParserError,
} = require('../../errors.js');

const invariant = require('invariant');
const language = 'Flow';

function nullGuard<T>(fn: () => T): ?T {
Expand Down Expand Up @@ -93,20 +96,19 @@ function translateTypeAnnotation(
return emitRootTag(nullable);
}
case 'Promise': {
assertGenericTypeAnnotationHasExactlyOneTypeParameter(
return emitPromise(
hasteModuleName,
typeAnnotation,
language,
nullable,
);

return wrapNullable(nullable, {
type: 'PromiseTypeAnnotation',
});
}
case 'Array':
case '$ReadOnlyArray': {
assertGenericTypeAnnotationHasExactlyOneTypeParameter(
hasteModuleName,
typeAnnotation,
language,
);

try {
Expand Down Expand Up @@ -182,6 +184,7 @@ function translateTypeAnnotation(
assertGenericTypeAnnotationHasExactlyOneTypeParameter(
hasteModuleName,
typeAnnotation,
language,
);

const [paramType, isParamNullable] = unwrapNullable(
Expand Down Expand Up @@ -411,35 +414,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.
Expand Down
40 changes: 40 additions & 0 deletions packages/react-native-codegen/src/parsers/parsers-commons.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
};
Loading