Skip to content

Commit 5ff01bc

Browse files
MaeIgfacebook-github-bot
authored andcommitted
Extract getCommandTypeNameAndOptionsExpression from component folders into parsers-common (#36640)
Summary: This PR aims to remove the duplicated logic in [flow|typescript]/components/index.js files to move it in parsers-commons. It is a task of #34872: > [Codegen 98 - assigned to MaeIg] Extract the namedExports.map(statement => ([Flow](https://github.com/facebook/react-native/blob/main/packages/react-native-codegen/src/parsers/flow/components/index.js#L76-L108), [TS](https://github.com/facebook/react-native/blob/main/packages/react-native-codegen/src/parsers/typescript/components/index.js#L77-L109)) function in parser-commons, so that it accept a Parser parameter to unify the behaviors between flow and typescript. The Parser object needs to be enriched with all the methods to extract the required information from the Node, if they are not there yet. ## Changelog <!-- Help reviewers and the release process by writing your own changelog entry. Pick one each for the category and type tags: [ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message For more details, see: https://reactnative.dev/contributing/changelogs-in-pull-requests --> [Internal] [Changed] - Extract getCommandTypeNameAndOptionsExpression from component folders into parsers-common Pull Request resolved: #36640 Test Plan: yarn flow: <img width="151" alt="image" src="https://user-images.githubusercontent.com/40902940/227719831-1a67f588-3bb2-48d7-8a43-4c5c8972155e.png"> yarn lint: <img width="499" alt="image" src="https://user-images.githubusercontent.com/40902940/227719839-e5b591c3-9f3a-4da4-b3a5-67275c58584f.png"> yarn test <img width="389" alt="image" src="https://user-images.githubusercontent.com/40902940/227719849-f3adfc4a-9215-4b1a-8807-c01801c54628.png"> Reviewed By: cipolleschi Differential Revision: D44416032 Pulled By: rshest fbshipit-source-id: eb682834d3da7a89661612667d9fc1df99ff3df0
1 parent 92b8981 commit 5ff01bc

File tree

9 files changed

+217
-68
lines changed

9 files changed

+217
-68
lines changed

packages/react-native-codegen/src/parsers/__tests__/parsers-commons-test.js

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
createComponentConfig,
2424
getCommandOptions,
2525
getOptions,
26+
getCommandTypeNameAndOptionsExpression,
2627
} from '../parsers-commons';
2728
import type {ParserType} from '../errors';
2829

@@ -1409,3 +1410,130 @@ describe('getOptions', () => {
14091410
expect(getOptions(optionsExpression)).toEqual(expectedOptions);
14101411
});
14111412
});
1413+
1414+
describe('getCommandTypeNameAndOptionsExpression', () => {
1415+
it("returns undefined when namedExport isn't well formatted", () => {
1416+
expect(
1417+
getCommandTypeNameAndOptionsExpression(null, flowParser),
1418+
).toBeUndefined();
1419+
1420+
expect(
1421+
getCommandTypeNameAndOptionsExpression(undefined, flowParser),
1422+
).toBeUndefined();
1423+
1424+
expect(
1425+
getCommandTypeNameAndOptionsExpression({}, flowParser),
1426+
).toBeUndefined();
1427+
});
1428+
1429+
it('returns undefined when the called expression name is not codegenNativeCommands', () => {
1430+
const namedExportMock = {
1431+
declaration: {
1432+
declarations: [
1433+
{
1434+
init: {
1435+
callee: {
1436+
name: 'notCodegenNativeCommands',
1437+
},
1438+
},
1439+
},
1440+
],
1441+
},
1442+
};
1443+
1444+
expect(
1445+
getCommandTypeNameAndOptionsExpression(namedExportMock, flowParser),
1446+
).toBeUndefined();
1447+
});
1448+
1449+
it("throws when the called expression doesn't have 1 argument", () => {
1450+
const namedExportMock = {
1451+
declaration: {
1452+
declarations: [
1453+
{
1454+
init: {
1455+
callee: {
1456+
name: 'codegenNativeCommands',
1457+
},
1458+
arguments: [],
1459+
},
1460+
},
1461+
],
1462+
},
1463+
};
1464+
1465+
expect(() =>
1466+
getCommandTypeNameAndOptionsExpression(namedExportMock, flowParser),
1467+
).toThrow(
1468+
new Error(
1469+
'codegenNativeCommands must be passed options including the supported commands',
1470+
),
1471+
);
1472+
});
1473+
1474+
it('throws when the type of the argument is not a generic type annotation', () => {
1475+
const namedExportMock = {
1476+
declaration: {
1477+
declarations: [
1478+
{
1479+
init: {
1480+
callee: {
1481+
name: 'codegenNativeCommands',
1482+
},
1483+
arguments: [{}],
1484+
typeArguments: {params: [{type: 'StringTypeAnnotation'}]},
1485+
},
1486+
},
1487+
],
1488+
},
1489+
};
1490+
1491+
expect(() =>
1492+
getCommandTypeNameAndOptionsExpression(namedExportMock, flowParser),
1493+
).toThrow(
1494+
new Error(
1495+
"codegenNativeCommands doesn't support inline definitions. Specify a file local type alias",
1496+
),
1497+
);
1498+
});
1499+
1500+
it('returns the command TypeName and options expression when the named export is valid', () => {
1501+
const commandTypeName = 'MyCommandType';
1502+
const commandOptionsExpression = {
1503+
type: 'ObjectExpression',
1504+
properties: [],
1505+
};
1506+
1507+
const namedExportMock = {
1508+
declaration: {
1509+
declarations: [
1510+
{
1511+
init: {
1512+
callee: {
1513+
name: 'codegenNativeCommands',
1514+
},
1515+
arguments: [commandOptionsExpression],
1516+
typeArguments: {
1517+
params: [
1518+
{
1519+
type: 'GenericTypeAnnotation',
1520+
id: {
1521+
name: commandTypeName,
1522+
},
1523+
},
1524+
],
1525+
},
1526+
},
1527+
},
1528+
],
1529+
},
1530+
};
1531+
1532+
expect(
1533+
getCommandTypeNameAndOptionsExpression(namedExportMock, flowParser),
1534+
).toStrictEqual({
1535+
commandTypeName,
1536+
commandOptionsExpression,
1537+
});
1538+
});
1539+
});

packages/react-native-codegen/src/parsers/__tests__/parsers-test.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,20 @@ describe('FlowParser', () => {
9090
});
9191
});
9292

93+
describe('isGenericTypeAnnotation', () => {
94+
it('returns true if it is a generic type annotation', () => {
95+
expect(parser.isGenericTypeAnnotation('GenericTypeAnnotation')).toBe(
96+
true,
97+
);
98+
});
99+
100+
it('returns false if it is not a generic type annotation', () => {
101+
expect(parser.isGenericTypeAnnotation('StringTypeAnnotation')).toBe(
102+
false,
103+
);
104+
});
105+
});
106+
93107
describe('callExpressionTypeParameters', () => {
94108
it('returns type arguments if it is a valid node', () => {
95109
const node = {
@@ -328,6 +342,18 @@ describe('TypeScriptParser', () => {
328342
});
329343
});
330344

345+
describe('isGenericTypeAnnotation', () => {
346+
it('returns true if it is a generic type annotation', () => {
347+
expect(parser.isGenericTypeAnnotation('TSTypeReference')).toBe(true);
348+
});
349+
350+
it('returns false if it is not a generic type annotation', () => {
351+
expect(parser.isGenericTypeAnnotation('StringTypeAnnotation')).toBe(
352+
false,
353+
);
354+
});
355+
});
356+
331357
describe('callExpressionTypeParameters', () => {
332358
it('returns type parameters if it is a valid node', () => {
333359
const node = {

packages/react-native-codegen/src/parsers/flow/components/index.js

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const {
2525
findNativeComponentType,
2626
getCommandOptions,
2727
getOptions,
28+
getCommandTypeNameAndOptionsExpression,
2829
} = require('../../parsers-commons');
2930

3031
// $FlowFixMe[signature-verification-failure] there's no flowtype for AST
@@ -53,40 +54,7 @@ function findComponentConfig(ast: $FlowFixMe, parser: Parser) {
5354
);
5455

5556
const commandsTypeNames = namedExports
56-
.map(statement => {
57-
let callExpression;
58-
let calleeName;
59-
try {
60-
callExpression = statement.declaration.declarations[0].init;
61-
calleeName = callExpression.callee.name;
62-
} catch (e) {
63-
return;
64-
}
65-
66-
if (calleeName !== 'codegenNativeCommands') {
67-
return;
68-
}
69-
70-
// const statement.declaration.declarations[0].init
71-
if (callExpression.arguments.length !== 1) {
72-
throw new Error(
73-
'codegenNativeCommands must be passed options including the supported commands',
74-
);
75-
}
76-
77-
const typeArgumentParam = callExpression.typeArguments.params[0];
78-
79-
if (typeArgumentParam.type !== 'GenericTypeAnnotation') {
80-
throw new Error(
81-
"codegenNativeCommands doesn't support inline definitions. Specify a file local type alias",
82-
);
83-
}
84-
85-
return {
86-
commandTypeName: typeArgumentParam.id.name,
87-
commandOptionsExpression: callExpression.arguments[0],
88-
};
89-
})
57+
.map(statement => getCommandTypeNameAndOptionsExpression(statement, parser))
9058
.filter(Boolean);
9159

9260
throwIfMoreThanOneCodegenNativecommands(commandsTypeNames);

packages/react-native-codegen/src/parsers/flow/parser.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,10 @@ class FlowParser implements Parser {
214214
);
215215
}
216216

217+
isGenericTypeAnnotation(type: $FlowFixMe): boolean {
218+
return type === 'GenericTypeAnnotation';
219+
}
220+
217221
extractAnnotatedElement(
218222
typeAnnotation: $FlowFixMe,
219223
types: TypeDeclarationMap,

packages/react-native-codegen/src/parsers/parser.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,11 @@ export interface Parser {
161161
*/
162162
isModuleInterface(node: $FlowFixMe): boolean;
163163

164+
/**
165+
* Given a type name, it returns true if it is a generic type annotation
166+
*/
167+
isGenericTypeAnnotation(type: $FlowFixMe): boolean;
168+
164169
/**
165170
* Given a typeAnnotation, it returns the annotated element.
166171
* @parameter typeAnnotation: the annotation for a type.

packages/react-native-codegen/src/parsers/parserMock.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ export class MockedParser implements Parser {
172172
);
173173
}
174174

175+
isGenericTypeAnnotation(type: $FlowFixMe): boolean {
176+
return true;
177+
}
178+
175179
extractAnnotatedElement(
176180
typeAnnotation: $FlowFixMe,
177181
types: TypeDeclarationMap,

packages/react-native-codegen/src/parsers/parsers-commons.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,47 @@ function getOptions(optionsExpression: OptionsAST): ?OptionsShape {
769769
return foundOptions;
770770
}
771771

772+
function getCommandTypeNameAndOptionsExpression(
773+
namedExport: $FlowFixMe,
774+
parser: Parser,
775+
): {
776+
commandOptionsExpression: OptionsAST,
777+
commandTypeName: string,
778+
} | void {
779+
let callExpression;
780+
let calleeName;
781+
try {
782+
callExpression = namedExport.declaration.declarations[0].init;
783+
calleeName = callExpression.callee.name;
784+
} catch (e) {
785+
return;
786+
}
787+
788+
if (calleeName !== 'codegenNativeCommands') {
789+
return;
790+
}
791+
792+
if (callExpression.arguments.length !== 1) {
793+
throw new Error(
794+
'codegenNativeCommands must be passed options including the supported commands',
795+
);
796+
}
797+
798+
const typeArgumentParam =
799+
parser.getTypeArgumentParamsFromDeclaration(callExpression)[0];
800+
801+
if (!parser.isGenericTypeAnnotation(typeArgumentParam.type)) {
802+
throw new Error(
803+
"codegenNativeCommands doesn't support inline definitions. Specify a file local type alias",
804+
);
805+
}
806+
807+
return {
808+
commandTypeName: parser.nameForGenericTypeAnnotation(typeArgumentParam),
809+
commandOptionsExpression: callExpression.arguments[0],
810+
};
811+
}
812+
772813
module.exports = {
773814
wrapModuleSchema,
774815
unwrapNullable,
@@ -786,4 +827,5 @@ module.exports = {
786827
findNativeComponentType,
787828
getCommandOptions,
788829
getOptions,
830+
getCommandTypeNameAndOptionsExpression,
789831
};

packages/react-native-codegen/src/parsers/typescript/components/index.js

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const {
2626
findNativeComponentType,
2727
getCommandOptions,
2828
getOptions,
29+
getCommandTypeNameAndOptionsExpression,
2930
} = require('../../parsers-commons');
3031

3132
// $FlowFixMe[signature-verification-failure] TODO(T108222691): Use flow-types for @babel/parser
@@ -54,40 +55,7 @@ function findComponentConfig(ast: $FlowFixMe, parser: Parser) {
5455
);
5556

5657
const commandsTypeNames = namedExports
57-
.map(statement => {
58-
let callExpression;
59-
let calleeName;
60-
try {
61-
callExpression = statement.declaration.declarations[0].init;
62-
calleeName = callExpression.callee.name;
63-
} catch (e) {
64-
return;
65-
}
66-
67-
if (calleeName !== 'codegenNativeCommands') {
68-
return;
69-
}
70-
71-
// const statement.declaration.declarations[0].init
72-
if (callExpression.arguments.length !== 1) {
73-
throw new Error(
74-
'codegenNativeCommands must be passed options including the supported commands',
75-
);
76-
}
77-
78-
const typeArgumentParam = callExpression.typeParameters.params[0];
79-
80-
if (typeArgumentParam.type !== 'TSTypeReference') {
81-
throw new Error(
82-
"codegenNativeCommands doesn't support inline definitions. Specify a file local type alias",
83-
);
84-
}
85-
86-
return {
87-
commandTypeName: typeArgumentParam.typeName.name,
88-
commandOptionsExpression: callExpression.arguments[0],
89-
};
90-
})
58+
.map(statement => getCommandTypeNameAndOptionsExpression(statement, parser))
9159
.filter(Boolean);
9260

9361
throwIfMoreThanOneCodegenNativecommands(commandsTypeNames);

packages/react-native-codegen/src/parsers/typescript/parser.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,10 @@ class TypeScriptParser implements Parser {
210210
);
211211
}
212212

213+
isGenericTypeAnnotation(type: $FlowFixMe): boolean {
214+
return type === 'TSTypeReference';
215+
}
216+
213217
extractAnnotatedElement(
214218
typeAnnotation: $FlowFixMe,
215219
types: TypeDeclarationMap,

0 commit comments

Comments
 (0)