Clean up Module/Component detection logic in Flow parser

Summary:
Just broke down getConfigType into two separate functions: `isModule` and `isComponent`.
- Cleaned up `isComponent`, to check for the the AST node types.
- Re-implemented `isModule`
- Improved error messages.

Changelog: [Internal]

Reviewed By: mdvacca

Differential Revision: D23121896

fbshipit-source-id: 3df2b2e334c4cea8eabe2e73ecb9f1f1217e7be4
This commit is contained in:
Ramanpreet Nara
2020-09-29 14:39:37 -07:00
committed by Facebook GitHub Bot
parent 1435d654d7
commit 22d6e04da1
2 changed files with 57 additions and 37 deletions
+55 -32
View File
@@ -26,7 +26,9 @@ const {processModule} = require('./modules');
* generates them, and flow-parser is not type-safe. In the future, we should find
* a way to get these types from our flow parser library.
*/
function getTypes(ast): {[declarationName: string]: $FlowFixMe} {
type TypeDeclarations = {|[declarationName: string]: $FlowFixMe|};
function getTypes(ast): TypeDeclarations {
return ast.body.reduce((types, node) => {
if (node.type === 'ExportNamedDeclaration' && node.exportKind === 'type') {
if (
@@ -45,58 +47,79 @@ function getTypes(ast): {[declarationName: string]: $FlowFixMe} {
}, {});
}
function getConfigType(ast, types): 'module' | 'component' {
function isComponent(ast) {
const defaultExports = ast.body.filter(
node => node.type === 'ExportDefaultDeclaration',
);
let isComponent = false;
if (defaultExports.length > 0) {
let declaration = defaultExports[0].declaration;
// codegenNativeComponent can be nested inside a cast
// expression so we need to go one level deeper
if (declaration.type === 'TypeCastExpression') {
declaration = declaration.expression;
}
isComponent =
declaration &&
declaration.callee &&
declaration.callee.name === 'codegenNativeComponent';
if (defaultExports.length === 0) {
return false;
}
const typesExtendingTurboModule = Object.keys(types)
.map(typeName => types[typeName])
.filter(
type =>
type.extends &&
type.extends[0] &&
type.extends[0].id.name === 'TurboModule',
);
let declaration = defaultExports[0].declaration;
// codegenNativeComponent can be nested inside a cast
// expression so we need to go one level deeper
if (declaration.type === 'TypeCastExpression') {
declaration = declaration.expression;
}
if (typesExtendingTurboModule.length > 1) {
if (declaration.type !== 'CallExpression') {
return false;
}
return (
declaration.callee.type === 'Identifier' &&
declaration.callee.name === 'codegenNativeComponent'
);
}
function isModule(types: TypeDeclarations) {
const declaredModuleNames: Array<string> = Object.keys(types).filter(
(typeName: string) => {
const declaration = types[typeName];
return (
declaration.type === 'InterfaceDeclaration' &&
declaration.extends.length === 1 &&
declaration.extends[0].type === 'InterfaceExtends' &&
declaration.extends[0].id.name === 'TurboModule'
);
},
);
if (declaredModuleNames.length === 0) {
return false;
}
if (declaredModuleNames.length > 1) {
throw new Error(
'Found two types extending "TurboModule" is one file. Split them into separated files.',
'File contains declarations of more than one module: ' +
declaredModuleNames.join(', ') +
'. Please declare exactly one module in this file.',
);
}
const isModule = typesExtendingTurboModule.length === 1;
return true;
}
if (isModule && isComponent) {
function getConfigType(ast, types: TypeDeclarations): 'module' | 'component' {
const isConfigAComponent = isComponent(ast);
const isConfigAModule = isModule(types);
if (isConfigAModule && isConfigAComponent) {
throw new Error(
'Found type extending "TurboModule" and exported "codegenNativeComponent" declaration in one file. Split them into separated files.',
);
}
if (isModule) {
if (isConfigAModule) {
return 'module';
} else if (isComponent) {
} else if (isConfigAComponent) {
return 'component';
} else {
throw new Error(
`Default export for module specified incorrectly. It should containts
either type extending "TurboModule" or "codegenNativeComponent".`,
'File neither contains a module declaration, nor a component declaration. ' +
'For module declarations, please make sure your file has an InterfaceDeclaration extending TurboModule. ' +
'For component declarations, please make sure your file has a default export calling the codegenNativeComponent<Props>(...) macro.',
);
}
}
@@ -12,12 +12,9 @@ exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_REA
exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_UNNAMED_PARAMS 1`] = `"Unsupported type for getBool. Please provide a name for every parameter."`;
exports[`RN Codegen Flow Parser Fails with error message TWO_NATIVE_EXTENDING_TURBO_MODULE 1`] = `"Found two types extending \\"TurboModule\\" is one file. Split them into separated files."`;
exports[`RN Codegen Flow Parser Fails with error message TWO_NATIVE_EXTENDING_TURBO_MODULE 1`] = `"File contains declarations of more than one module: Spec, Spec2. Please declare exactly one module in this file."`;
exports[`RN Codegen Flow Parser Fails with error message TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT 1`] = `
"Default export for module specified incorrectly. It should containts
either type extending \\"TurboModule\\" or \\"codegenNativeComponent\\"."
`;
exports[`RN Codegen Flow Parser Fails with error message TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT 1`] = `"File neither contains a module declaration, nor a component declaration. For module declarations, please make sure your file has an InterfaceDeclaration extending TurboModule. For component declarations, please make sure your file has a default export calling the codegenNativeComponent<Props>(...) macro."`;
exports[`RN Codegen Flow Parser can generate fixture EMPTY_NATIVE_MODULE 1`] = `
Object {