Make parser return empty schema when parsing non-spec files

Summary:
Right now, running the codegen parser on regular JavaScript files causes it to throw an error:

https://www.internalfb.com/intern/diffusion/FBS/browsefile/master/xplat/js/react-native-github/packages/react-native-codegen/src/parsers/flow/index.js?commit=1e93f27aac8890f1731650fdcc38b702ae8874e2&lines=59-63%2C106-107

This makes the parser less usable, because you can't simply loop over a list of JavaScript files, and run codegen on them. You need to do some filtering beforehand, or surround each invocation of the parser with a try/catch.

Our codegen schema supports the idea of an empty schema:

```
{
  modules: {}
}
```

To improve the parser's ergonomics, this diff migrates the codegen parser over to returning the empty schema when it cannot detect a Component or NativeModule spec inside a JavaScript file.

Changelog: [Internal]

Reviewed By: JoshuaGross

Differential Revision: D26034052

fbshipit-source-id: c2f15aba9c0e052012396eaed2034537f5918e33
This commit is contained in:
Ramanpreet Nara
2021-01-23 02:02:57 -08:00
committed by Facebook GitHub Bot
parent 595b569ead
commit 3dab9a0d01
4 changed files with 62 additions and 44 deletions
+20 -9
View File
@@ -19,13 +19,17 @@ const {buildComponentSchema} = require('./components');
const {wrapComponentSchema} = require('./components/schema');
const {buildModuleSchema} = require('./modules');
const {wrapModuleSchema} = require('./modules/schema');
const {createParserErrorCapturer, visit} = require('./utils');
const {
createParserErrorCapturer,
visit,
isModuleRegistryCall,
} = require('./utils');
const invariant = require('invariant');
function getConfigType(
// TODO(T71778680): Flow-type this node.
ast: $FlowFixMe,
): 'module' | 'component' {
): 'module' | 'component' | 'none' {
let isComponent = false;
let isModule = false;
@@ -37,6 +41,10 @@ function getConfigType(
) {
isComponent = true;
}
if (isModuleRegistryCall(node)) {
isModule = true;
}
},
InterfaceExtends(node) {
if (node.id.name === 'TurboModule') {
@@ -56,15 +64,19 @@ function getConfigType(
} else if (isComponent) {
return 'component';
} else {
throw new Error(
'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.',
);
return 'none';
}
}
function buildSchema(contents: string, filename: ?string): SchemaType {
// Early return for non-Spec JavaScript files
if (
!contents.includes('codegenNativeComponent') &&
!contents.includes('TurboModule')
) {
return {modules: {}};
}
const ast = flowParser.parse(contents);
const configType = getConfigType(ast);
@@ -103,8 +115,7 @@ function buildSchema(contents: string, filename: ?string): SchemaType {
return wrapModuleSchema(schema, hasteModuleName);
}
default:
(configType: empty);
throw new Error(`Unsupported config type '${configType}'`);
return {modules: {}};
}
}
@@ -14,7 +14,7 @@ exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_UNN
exports[`RN Codegen Flow Parser Fails with error message TWO_NATIVE_EXTENDING_TURBO_MODULE 1`] = `"Module NativeSampleTurboModule: Every NativeModule spec file must declare exactly one NativeModule Flow interface. This file declares 2: 'Spec', and 'Spec2'. Please remove the extraneous Flow interface declarations."`;
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 Fails with error message TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT 1`] = `"Module NativeSampleTurboModule: No Flow interfaces extending TurboModule were detected in this NativeModule spec."`;
exports[`RN Codegen Flow Parser can generate fixture ANDROID_ONLY_NATIVE_MODULE 1`] = `
"{
@@ -26,7 +26,12 @@ import type {TypeDeclarationMap} from '../utils.js';
import type {ParserErrorCapturer} from '../utils';
import type {NativeModuleTypeAnnotation} from '../../../CodegenSchema.js';
const {resolveTypeAnnotation, getTypes, visit} = require('../utils.js');
const {
resolveTypeAnnotation,
getTypes,
visit,
isModuleRegistryCall,
} = require('../utils.js');
const {unwrapNullable, wrapNullable} = require('./utils');
const {
IncorrectlyParameterizedFlowGenericParserError,
@@ -528,39 +533,6 @@ function buildPropertySchema(
};
}
function isModuleRegistryCall(node) {
if (node.type !== 'CallExpression') {
return false;
}
const callExpression = node;
if (callExpression.callee.type !== 'MemberExpression') {
return false;
}
const memberExpression = callExpression.callee;
if (
!(
memberExpression.object.type === 'Identifier' &&
memberExpression.object.name === 'TurboModuleRegistry'
)
) {
return false;
}
if (
!(
memberExpression.property.type === 'Identifier' &&
(memberExpression.property.name === 'get' ||
memberExpression.property.name === 'getEnforcing')
)
) {
return false;
}
return true;
}
function isModuleInterface(node) {
return (
node.type === 'InterfaceDeclaration' &&
+35
View File
@@ -166,10 +166,45 @@ function visit(
}
}
// TODO(T71778680): Flow-type ASTNodes.
function isModuleRegistryCall(node: $FlowFixMe): boolean {
if (node.type !== 'CallExpression') {
return false;
}
const callExpression = node;
if (callExpression.callee.type !== 'MemberExpression') {
return false;
}
const memberExpression = callExpression.callee;
if (
!(
memberExpression.object.type === 'Identifier' &&
memberExpression.object.name === 'TurboModuleRegistry'
)
) {
return false;
}
if (
!(
memberExpression.property.type === 'Identifier' &&
(memberExpression.property.name === 'get' ||
memberExpression.property.name === 'getEnforcing')
)
) {
return false;
}
return true;
}
module.exports = {
getValueFromTypes,
resolveTypeAnnotation,
createParserErrorCapturer,
getTypes,
visit,
isModuleRegistryCall,
};