mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
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 https://github.com/facebook/react-native/issues/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: https://github.com/facebook/react-native/pull/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
This commit is contained in:
committed by
Facebook GitHub Bot
parent
92b8981499
commit
5ff01bc1d2
@@ -23,6 +23,7 @@ import {
|
||||
createComponentConfig,
|
||||
getCommandOptions,
|
||||
getOptions,
|
||||
getCommandTypeNameAndOptionsExpression,
|
||||
} from '../parsers-commons';
|
||||
import type {ParserType} from '../errors';
|
||||
|
||||
@@ -1409,3 +1410,130 @@ describe('getOptions', () => {
|
||||
expect(getOptions(optionsExpression)).toEqual(expectedOptions);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCommandTypeNameAndOptionsExpression', () => {
|
||||
it("returns undefined when namedExport isn't well formatted", () => {
|
||||
expect(
|
||||
getCommandTypeNameAndOptionsExpression(null, flowParser),
|
||||
).toBeUndefined();
|
||||
|
||||
expect(
|
||||
getCommandTypeNameAndOptionsExpression(undefined, flowParser),
|
||||
).toBeUndefined();
|
||||
|
||||
expect(
|
||||
getCommandTypeNameAndOptionsExpression({}, flowParser),
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it('returns undefined when the called expression name is not codegenNativeCommands', () => {
|
||||
const namedExportMock = {
|
||||
declaration: {
|
||||
declarations: [
|
||||
{
|
||||
init: {
|
||||
callee: {
|
||||
name: 'notCodegenNativeCommands',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
expect(
|
||||
getCommandTypeNameAndOptionsExpression(namedExportMock, flowParser),
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it("throws when the called expression doesn't have 1 argument", () => {
|
||||
const namedExportMock = {
|
||||
declaration: {
|
||||
declarations: [
|
||||
{
|
||||
init: {
|
||||
callee: {
|
||||
name: 'codegenNativeCommands',
|
||||
},
|
||||
arguments: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
expect(() =>
|
||||
getCommandTypeNameAndOptionsExpression(namedExportMock, flowParser),
|
||||
).toThrow(
|
||||
new Error(
|
||||
'codegenNativeCommands must be passed options including the supported commands',
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it('throws when the type of the argument is not a generic type annotation', () => {
|
||||
const namedExportMock = {
|
||||
declaration: {
|
||||
declarations: [
|
||||
{
|
||||
init: {
|
||||
callee: {
|
||||
name: 'codegenNativeCommands',
|
||||
},
|
||||
arguments: [{}],
|
||||
typeArguments: {params: [{type: 'StringTypeAnnotation'}]},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
expect(() =>
|
||||
getCommandTypeNameAndOptionsExpression(namedExportMock, flowParser),
|
||||
).toThrow(
|
||||
new Error(
|
||||
"codegenNativeCommands doesn't support inline definitions. Specify a file local type alias",
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it('returns the command TypeName and options expression when the named export is valid', () => {
|
||||
const commandTypeName = 'MyCommandType';
|
||||
const commandOptionsExpression = {
|
||||
type: 'ObjectExpression',
|
||||
properties: [],
|
||||
};
|
||||
|
||||
const namedExportMock = {
|
||||
declaration: {
|
||||
declarations: [
|
||||
{
|
||||
init: {
|
||||
callee: {
|
||||
name: 'codegenNativeCommands',
|
||||
},
|
||||
arguments: [commandOptionsExpression],
|
||||
typeArguments: {
|
||||
params: [
|
||||
{
|
||||
type: 'GenericTypeAnnotation',
|
||||
id: {
|
||||
name: commandTypeName,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
expect(
|
||||
getCommandTypeNameAndOptionsExpression(namedExportMock, flowParser),
|
||||
).toStrictEqual({
|
||||
commandTypeName,
|
||||
commandOptionsExpression,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -90,6 +90,20 @@ describe('FlowParser', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('isGenericTypeAnnotation', () => {
|
||||
it('returns true if it is a generic type annotation', () => {
|
||||
expect(parser.isGenericTypeAnnotation('GenericTypeAnnotation')).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it('returns false if it is not a generic type annotation', () => {
|
||||
expect(parser.isGenericTypeAnnotation('StringTypeAnnotation')).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('callExpressionTypeParameters', () => {
|
||||
it('returns type arguments if it is a valid node', () => {
|
||||
const node = {
|
||||
@@ -328,6 +342,18 @@ describe('TypeScriptParser', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('isGenericTypeAnnotation', () => {
|
||||
it('returns true if it is a generic type annotation', () => {
|
||||
expect(parser.isGenericTypeAnnotation('TSTypeReference')).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false if it is not a generic type annotation', () => {
|
||||
expect(parser.isGenericTypeAnnotation('StringTypeAnnotation')).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('callExpressionTypeParameters', () => {
|
||||
it('returns type parameters if it is a valid node', () => {
|
||||
const node = {
|
||||
|
||||
@@ -25,6 +25,7 @@ const {
|
||||
findNativeComponentType,
|
||||
getCommandOptions,
|
||||
getOptions,
|
||||
getCommandTypeNameAndOptionsExpression,
|
||||
} = require('../../parsers-commons');
|
||||
|
||||
// $FlowFixMe[signature-verification-failure] there's no flowtype for AST
|
||||
@@ -53,40 +54,7 @@ function findComponentConfig(ast: $FlowFixMe, parser: Parser) {
|
||||
);
|
||||
|
||||
const commandsTypeNames = namedExports
|
||||
.map(statement => {
|
||||
let callExpression;
|
||||
let calleeName;
|
||||
try {
|
||||
callExpression = statement.declaration.declarations[0].init;
|
||||
calleeName = callExpression.callee.name;
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (calleeName !== 'codegenNativeCommands') {
|
||||
return;
|
||||
}
|
||||
|
||||
// const statement.declaration.declarations[0].init
|
||||
if (callExpression.arguments.length !== 1) {
|
||||
throw new Error(
|
||||
'codegenNativeCommands must be passed options including the supported commands',
|
||||
);
|
||||
}
|
||||
|
||||
const typeArgumentParam = callExpression.typeArguments.params[0];
|
||||
|
||||
if (typeArgumentParam.type !== 'GenericTypeAnnotation') {
|
||||
throw new Error(
|
||||
"codegenNativeCommands doesn't support inline definitions. Specify a file local type alias",
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
commandTypeName: typeArgumentParam.id.name,
|
||||
commandOptionsExpression: callExpression.arguments[0],
|
||||
};
|
||||
})
|
||||
.map(statement => getCommandTypeNameAndOptionsExpression(statement, parser))
|
||||
.filter(Boolean);
|
||||
|
||||
throwIfMoreThanOneCodegenNativecommands(commandsTypeNames);
|
||||
|
||||
@@ -214,6 +214,10 @@ class FlowParser implements Parser {
|
||||
);
|
||||
}
|
||||
|
||||
isGenericTypeAnnotation(type: $FlowFixMe): boolean {
|
||||
return type === 'GenericTypeAnnotation';
|
||||
}
|
||||
|
||||
extractAnnotatedElement(
|
||||
typeAnnotation: $FlowFixMe,
|
||||
types: TypeDeclarationMap,
|
||||
|
||||
@@ -161,6 +161,11 @@ export interface Parser {
|
||||
*/
|
||||
isModuleInterface(node: $FlowFixMe): boolean;
|
||||
|
||||
/**
|
||||
* Given a type name, it returns true if it is a generic type annotation
|
||||
*/
|
||||
isGenericTypeAnnotation(type: $FlowFixMe): boolean;
|
||||
|
||||
/**
|
||||
* Given a typeAnnotation, it returns the annotated element.
|
||||
* @parameter typeAnnotation: the annotation for a type.
|
||||
|
||||
@@ -172,6 +172,10 @@ export class MockedParser implements Parser {
|
||||
);
|
||||
}
|
||||
|
||||
isGenericTypeAnnotation(type: $FlowFixMe): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
extractAnnotatedElement(
|
||||
typeAnnotation: $FlowFixMe,
|
||||
types: TypeDeclarationMap,
|
||||
|
||||
@@ -769,6 +769,47 @@ function getOptions(optionsExpression: OptionsAST): ?OptionsShape {
|
||||
return foundOptions;
|
||||
}
|
||||
|
||||
function getCommandTypeNameAndOptionsExpression(
|
||||
namedExport: $FlowFixMe,
|
||||
parser: Parser,
|
||||
): {
|
||||
commandOptionsExpression: OptionsAST,
|
||||
commandTypeName: string,
|
||||
} | void {
|
||||
let callExpression;
|
||||
let calleeName;
|
||||
try {
|
||||
callExpression = namedExport.declaration.declarations[0].init;
|
||||
calleeName = callExpression.callee.name;
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (calleeName !== 'codegenNativeCommands') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (callExpression.arguments.length !== 1) {
|
||||
throw new Error(
|
||||
'codegenNativeCommands must be passed options including the supported commands',
|
||||
);
|
||||
}
|
||||
|
||||
const typeArgumentParam =
|
||||
parser.getTypeArgumentParamsFromDeclaration(callExpression)[0];
|
||||
|
||||
if (!parser.isGenericTypeAnnotation(typeArgumentParam.type)) {
|
||||
throw new Error(
|
||||
"codegenNativeCommands doesn't support inline definitions. Specify a file local type alias",
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
commandTypeName: parser.nameForGenericTypeAnnotation(typeArgumentParam),
|
||||
commandOptionsExpression: callExpression.arguments[0],
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
wrapModuleSchema,
|
||||
unwrapNullable,
|
||||
@@ -786,4 +827,5 @@ module.exports = {
|
||||
findNativeComponentType,
|
||||
getCommandOptions,
|
||||
getOptions,
|
||||
getCommandTypeNameAndOptionsExpression,
|
||||
};
|
||||
|
||||
@@ -26,6 +26,7 @@ const {
|
||||
findNativeComponentType,
|
||||
getCommandOptions,
|
||||
getOptions,
|
||||
getCommandTypeNameAndOptionsExpression,
|
||||
} = require('../../parsers-commons');
|
||||
|
||||
// $FlowFixMe[signature-verification-failure] TODO(T108222691): Use flow-types for @babel/parser
|
||||
@@ -54,40 +55,7 @@ function findComponentConfig(ast: $FlowFixMe, parser: Parser) {
|
||||
);
|
||||
|
||||
const commandsTypeNames = namedExports
|
||||
.map(statement => {
|
||||
let callExpression;
|
||||
let calleeName;
|
||||
try {
|
||||
callExpression = statement.declaration.declarations[0].init;
|
||||
calleeName = callExpression.callee.name;
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (calleeName !== 'codegenNativeCommands') {
|
||||
return;
|
||||
}
|
||||
|
||||
// const statement.declaration.declarations[0].init
|
||||
if (callExpression.arguments.length !== 1) {
|
||||
throw new Error(
|
||||
'codegenNativeCommands must be passed options including the supported commands',
|
||||
);
|
||||
}
|
||||
|
||||
const typeArgumentParam = callExpression.typeParameters.params[0];
|
||||
|
||||
if (typeArgumentParam.type !== 'TSTypeReference') {
|
||||
throw new Error(
|
||||
"codegenNativeCommands doesn't support inline definitions. Specify a file local type alias",
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
commandTypeName: typeArgumentParam.typeName.name,
|
||||
commandOptionsExpression: callExpression.arguments[0],
|
||||
};
|
||||
})
|
||||
.map(statement => getCommandTypeNameAndOptionsExpression(statement, parser))
|
||||
.filter(Boolean);
|
||||
|
||||
throwIfMoreThanOneCodegenNativecommands(commandsTypeNames);
|
||||
|
||||
@@ -210,6 +210,10 @@ class TypeScriptParser implements Parser {
|
||||
);
|
||||
}
|
||||
|
||||
isGenericTypeAnnotation(type: $FlowFixMe): boolean {
|
||||
return type === 'TSTypeReference';
|
||||
}
|
||||
|
||||
extractAnnotatedElement(
|
||||
typeAnnotation: $FlowFixMe,
|
||||
types: TypeDeclarationMap,
|
||||
|
||||
Reference in New Issue
Block a user