Skip to content

Commit

Permalink
Merge Parse-Module-Name anon fn of Flow & TS parsers' (#36297)
Browse files Browse the repository at this point in the history
Summary:
Part of Umbrella #34872
> [**Codegen 84** - assigned to Pranav-yadav] It depends on [Codegen 83] export the parseModuleName anonymous function (Flow, TypeScript) in a common parseModuleName function in the parsers-commons.js file.

- merged Parse Module-Name _**anon**_ fn of `Flow` & `TS` parsers; into a common `parseModuleName` fn in the `parsers-commons.js`
- added **tests** for `parseModuleName` fn from `parsers-commons.js`
- added `callExpressionTypeParameters` method to **_parsers_**
- added **tests** for `callExpressionTypeParameters` method of _parsers_
- used `parser.callExpressionTypeParameters` method in `parseModuleName` fn

PS: fixed merge conflicts several times :(

Overall :)

## Changelog

[INTERNAL] [CHANGED] - Merge Parse-Module-Name anon fn of `Flow` & `TS` and add `callExpressionTypeParameters` method to **_parsers_**

Pull Request resolved: #36297

Test Plan: - `yarn lint && yarn run flow && yarn test react-native-codegen`  ==> ✅

Reviewed By: rshest

Differential Revision: D43694563

Pulled By: cipolleschi

fbshipit-source-id: 99cf40ada0a567cd9ff91078f66fd4ac3684f7cc
  • Loading branch information
Pranav-yadav authored and facebook-github-bot committed Mar 2, 2023
1 parent b0863e1 commit 05454fa
Show file tree
Hide file tree
Showing 9 changed files with 379 additions and 133 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
unwrapNullable,
buildSchemaFromConfigType,
buildSchema,
parseModuleName,
} from '../parsers-commons';
import type {ParserType} from '../errors';

Expand All @@ -30,6 +31,11 @@ const {isModuleRegistryCall} = require('../utils.js');
const {
ParserError,
UnsupportedObjectPropertyTypeAnnotationParserError,
UnusedModuleInterfaceParserError,
MoreThanOneModuleRegistryCallsParserError,
IncorrectModuleRegistryCallArityParserError,
IncorrectModuleRegistryCallArgumentTypeParserError,
UntypedModuleRegistryCallParserError,
} = require('../errors');

import {MockedParser} from '../parserMock';
Expand Down Expand Up @@ -783,3 +789,221 @@ describe('buildSchema', () => {
});
});
});

describe('parseModuleName', () => {
const hasteModuleName = 'testModuleName';
const emptyFlowAst = parser.getAst('');
const moduleSpecs = [{name: 'Spec'}];
const flowAstWithOneCallExpression = parser.getAst(
"export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');",
);

describe('throwIfUnusedModuleInterfaceParserError', () => {
it("throws an 'UnusedModuleInterfaceParserError' error if 'callExpressions' array is 'empty'", () => {
const expected = new UnusedModuleInterfaceParserError(
hasteModuleName,
moduleSpecs[0],
);

expect(() =>
parseModuleName(hasteModuleName, moduleSpecs[0], emptyFlowAst, parser),
).toThrow(expected);
});

it("doesn't throw an 'UnusedModuleInterfaceParserError' error if 'callExpressions' array is 'NOT empty'", () => {
expect(() =>
parseModuleName(
hasteModuleName,
moduleSpecs[0],
flowAstWithOneCallExpression,
parser,
),
).not.toThrow(UnusedModuleInterfaceParserError);
});
});

describe('throwIfMoreThanOneModuleRegistryCalls', () => {
it("throws an 'MoreThanOneModuleRegistryCallsParserError' error if 'callExpressions' array contains more than one 'callExpression'", () => {
const flowAstWithTwoCallExpressions = parser.getAst(
"export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule'); TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');",
);

const callExpressions: Array<$FlowFixMe> =
flowAstWithTwoCallExpressions.body;

const expected = new MoreThanOneModuleRegistryCallsParserError(
hasteModuleName,
callExpressions,
callExpressions.length,
);

expect(() =>
parseModuleName(
hasteModuleName,
moduleSpecs[0],
flowAstWithTwoCallExpressions,
parser,
),
).toThrow(expected);
});

it("doesn't throw an 'MoreThanOneModuleRegistryCallsParserError' error if 'callExpressions' array contains extactly one 'callExpression'", () => {
expect(() =>
parseModuleName(
hasteModuleName,
moduleSpecs[0],
flowAstWithOneCallExpression,
parser,
),
).not.toThrow(MoreThanOneModuleRegistryCallsParserError);
});
});

describe('throwIfWrongNumberOfCallExpressionArgs', () => {
it("throws an 'IncorrectModuleRegistryCallArityParserError' error if wrong number of call expression args is used", () => {
const flowAstWithZeroCallExpressionArgs = parser.getAst(
'export default TurboModuleRegistry.getEnforcing();',
);
const flowCallExpressionWithoutArgs =
flowAstWithZeroCallExpressionArgs.body[0].declaration;
const numberOfCallExpressionArgs =
flowCallExpressionWithoutArgs.arguments.length;
const flowCallExpressionWithoutArgsCallee =
flowCallExpressionWithoutArgs.callee.property.name;

const expected = new IncorrectModuleRegistryCallArityParserError(
hasteModuleName,
flowCallExpressionWithoutArgs,
flowCallExpressionWithoutArgsCallee,
numberOfCallExpressionArgs,
);

expect(() =>
parseModuleName(
hasteModuleName,
moduleSpecs[0],
flowAstWithZeroCallExpressionArgs,
parser,
),
).toThrow(expected);
});

it("doesn't throw an 'IncorrectModuleRegistryCallArityParserError' error if correct number of call expression args is used", () => {
expect(() =>
parseModuleName(
hasteModuleName,
moduleSpecs[0],
flowAstWithOneCallExpression,
parser,
),
).not.toThrow(IncorrectModuleRegistryCallArityParserError);
});
});

describe('throwIfIncorrectModuleRegistryCallArgument', () => {
it("throws an 'IncorrectModuleRegistryCallArgumentTypeParserError' error if call expression arg is NOT a string literal", () => {
const flowAstWithNonStringLiteralCallExpressionArg = parser.getAst(
'export default TurboModuleRegistry.getEnforcing(Spec);',
);
const flowCallExpression =
flowAstWithNonStringLiteralCallExpressionArg.body[0].declaration;
const flowCallExpressionCalllee = flowCallExpression.callee.property.name;
const flowCallExpressionArg = flowCallExpression.arguments[0];

const expected = new IncorrectModuleRegistryCallArgumentTypeParserError(
hasteModuleName,
flowCallExpressionArg,
flowCallExpressionCalllee,
flowCallExpressionArg.type,
);

expect(() =>
parseModuleName(
hasteModuleName,
moduleSpecs[0],
flowAstWithNonStringLiteralCallExpressionArg,
parser,
),
).toThrow(expected);
});

it("doesn't throw an 'IncorrectModuleRegistryCallArgumentTypeParserError' error if call expression arg is a string literal", () => {
expect(() =>
parseModuleName(
hasteModuleName,
moduleSpecs[0],
flowAstWithOneCallExpression,
parser,
),
).not.toThrow(IncorrectModuleRegistryCallArgumentTypeParserError);
});
});

describe('throwIfUntypedModule', () => {
it("throws an 'UntypedModuleRegistryCallParserError' error if call expression is untyped", () => {
const flowAstWithUntypedCallExpression = parser.getAst(
"export default TurboModuleRegistry.getEnforcing('SampleTurboModule');",
);
const flowCallExpression =
flowAstWithUntypedCallExpression.body[0].declaration;
const flowCallExpressionCallee = flowCallExpression.callee.property.name;
const moduleName = flowCallExpression.arguments[0].value;
const expected = new UntypedModuleRegistryCallParserError(
hasteModuleName,
flowCallExpression,
flowCallExpressionCallee,
moduleName,
);

expect(() =>
parseModuleName(
hasteModuleName,
moduleSpecs[0],
flowAstWithUntypedCallExpression,
parser,
),
).toThrow(expected);
});

it("doesn't throw an 'UntypedModuleRegistryCallParserError' error if call expression is typed", () => {
expect(() =>
parseModuleName(
hasteModuleName,
moduleSpecs[0],
flowAstWithOneCallExpression,
parser,
),
).not.toThrow(UntypedModuleRegistryCallParserError);
});
});

describe('when flow ast with valid module is passed', () => {
it("returns the correct ModuleName and doesn't throw any error", () => {
const moduleType = 'Spec';
const moduleName = 'SampleTurboModule';
const flowAstWithValidModule = parser.getAst(
`export default TurboModuleRegistry.getEnforcing<${moduleType}>('${moduleName}');`,
);

const expected = moduleName;

expect(
parseModuleName(
hasteModuleName,
moduleSpecs[0],
flowAstWithValidModule,
parser,
),
).toEqual(expected);

expect(() =>
parseModuleName(
hasteModuleName,
moduleSpecs[0],
flowAstWithValidModule,
parser,
),
).not.toThrow();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,27 @@ describe('FlowParser', () => {
expect(parser.isModuleInterface(node)).toBe(false);
});
});

describe('callExpressionTypeParameters', () => {
it('returns type arguments if it is a valid node', () => {
const node = {
type: 'CallExpression',
typeArguments: {
type: 'TypeParameterInstantiation',
params: [],
},
};
expect(parser.callExpressionTypeParameters(node)).toEqual({
type: 'TypeParameterInstantiation',
params: [],
});
});

it('returns null if it is a invalid node', () => {
const node = {};
expect(parser.callExpressionTypeParameters(node)).toBe(null);
});
});
});

describe('TypeScriptParser', () => {
Expand Down Expand Up @@ -160,4 +181,25 @@ describe('TypeScriptParser', () => {
expect(parser.isModuleInterface(node)).toBe(false);
});
});

describe('callExpressionTypeParameters', () => {
it('returns type parameters if it is a valid node', () => {
const node = {
type: 'CallExpression',
typeParameters: {
type: 'TypeParameterInstantiation',
params: [],
},
};
expect(parser.callExpressionTypeParameters(node)).toEqual({
type: 'TypeParameterInstantiation',
params: [],
});
});

it('returns null if it is a invalid node', () => {
const node = {};
expect(parser.callExpressionTypeParameters(node)).toBe(null);
});
});
});
76 changes: 10 additions & 66 deletions packages/react-native-codegen/src/parsers/flow/modules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@ import type {
import type {Parser} from '../../parser';
import type {ParserErrorCapturer, TypeDeclarationMap} from '../../utils';

const {visit, isModuleRegistryCall, verifyPlatforms} = require('../../utils');
const {verifyPlatforms} = require('../../utils');
const {resolveTypeAnnotation} = require('../utils');
const {
unwrapNullable,
wrapNullable,
assertGenericTypeAnnotationHasExactlyOneTypeParameter,
parseObjectProperty,
buildPropertySchema,
parseModuleName,
} = require('../../parsers-commons');
const {
emitArrayType,
Expand Down Expand Up @@ -62,12 +63,6 @@ const {
const {
throwIfModuleInterfaceNotFound,
throwIfModuleInterfaceIsMisnamed,
throwIfUnusedModuleInterfaceParserError,
throwIfWrongNumberOfCallExpressionArgs,
throwIfMoreThanOneModuleRegistryCalls,
throwIfIncorrectModuleRegistryCallTypeParameterParserError,
throwIfIncorrectModuleRegistryCallArgument,
throwIfUntypedModule,
throwIfMoreThanOneModuleInterfaceParserError,
throwIfPartialNotAnnotatingTypeParameter,
throwIfPartialWithMoreParameter,
Expand Down Expand Up @@ -362,65 +357,14 @@ function buildModuleSchema(
throwIfModuleInterfaceIsMisnamed(hasteModuleName, moduleSpec.id, language);

// Parse Module Name
const moduleName = ((): string => {
const callExpressions = [];
visit(ast, {
CallExpression(node) {
if (isModuleRegistryCall(node)) {
callExpressions.push(node);
}
},
});

throwIfUnusedModuleInterfaceParserError(
hasteModuleName,
moduleSpec,
callExpressions,
);

throwIfMoreThanOneModuleRegistryCalls(
hasteModuleName,
callExpressions,
callExpressions.length,
);

const [callExpression] = callExpressions;
const {typeArguments} = callExpression;
const methodName = callExpression.callee.property.name;

throwIfWrongNumberOfCallExpressionArgs(
hasteModuleName,
callExpression,
methodName,
callExpression.arguments.length,
);

throwIfIncorrectModuleRegistryCallArgument(
hasteModuleName,
callExpression.arguments[0],
methodName,
);

const $moduleName = callExpression.arguments[0].value;

throwIfUntypedModule(
typeArguments,
hasteModuleName,
callExpression,
methodName,
$moduleName,
);

throwIfIncorrectModuleRegistryCallTypeParameterParserError(
hasteModuleName,
typeArguments,
methodName,
$moduleName,
parser,
);

return $moduleName;
})();
// Also checks and throws error if:
// - Module Interface is Unused
// - More than 1 Module Registry Calls
// - Wrong number of Call Expression Args
// - Module Registry Call Args are Incorrect
// - Module is Untyped
// - Module Registry Call Type Parameter is Icorrect
const moduleName = parseModuleName(hasteModuleName, moduleSpec, ast, parser);

// Some module names use platform suffix to indicate platform-exclusive modules.
// Eventually this should be made explicit in the Flow type itself.
Expand Down
Loading

0 comments on commit 05454fa

Please sign in to comment.