From cbd4ad43c027f77d6f5581e9ddcd2bfbb876bf46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Osadnik?= Date: Thu, 11 Jul 2019 09:26:15 -0700 Subject: [PATCH] Change module parser to consider extends instead of default exports Summary: Following out internal communication we decided to change logic of current parser to consider types which extend 'TurboModule' instead of looking at default exports. It also cassed for minor changes i n testing logic and updating snapshots. Reviewed By: rickhanlonii Differential Revision: D16200939 fbshipit-source-id: a21cbfc461647df2b9210c0eca4c2c95c285dfe1 --- .../src/parsers/flow/index.js | 75 ++++++++++++++----- .../modules/__test_fixtures__/failures.js | 53 ++++++------- .../__snapshots__/module-parser-test.js.snap | 31 ++++---- .../modules/__tests__/module-parser-test.js | 4 +- .../src/parsers/flow/modules/index.js | 35 ++++----- .../src/parsers/flow/modules/schema.js | 13 ++-- 6 files changed, 120 insertions(+), 91 deletions(-) diff --git a/packages/react-native-codegen/src/parsers/flow/index.js b/packages/react-native-codegen/src/parsers/flow/index.js index eb4ff339f8..d94c41c5f7 100644 --- a/packages/react-native-codegen/src/parsers/flow/index.js +++ b/packages/react-native-codegen/src/parsers/flow/index.js @@ -14,6 +14,7 @@ import type {SchemaType} from '../../CodegenSchema.js'; // $FlowFixMe there's no flowtype flow-parser const flowParser = require('flow-parser'); const fs = require('fs'); +const path = require('path'); const {buildModuleSchema} = require('./modules/schema'); const {buildComponentSchema} = require('./components/schema'); const {processComponent} = require('./components'); @@ -35,46 +36,79 @@ function getTypes(ast) { }, {}); } -function getConfigType(ast): 'module' | 'component' { +function getConfigType(ast, types): 'module' | 'component' { const defaultExports = ast.body.filter( node => node.type === 'ExportDefaultDeclaration', ); - if (defaultExports.length !== 1) { - throw new Error('File should contain only one default export.'); + const isComponent = + defaultExports[0] && + defaultExports[0].declaration && + defaultExports[0].declaration.callee && + defaultExports[0].declaration.callee.name === 'codegenNativeComponent'; + + const typesExtendingTurboModule = Object.keys(types) + .map(typeName => types[typeName]) + .filter( + type => + type.extends && + type.extends[0] && + type.extends[0].id.name === 'TurboModule', + ); + + if (typesExtendingTurboModule.length > 1) { + throw new Error( + 'Found two types extending "TurboModule" is one file. Split them into separated files.', + ); } - if (defaultExports[0].declaration && defaultExports[0].declaration.callee) { - const statement = defaultExports[0].declaration.callee; - if (statement.name === 'codegenNativeComponent') { - return 'component'; - } - if (statement.object && statement.object.name === 'TurboModuleRegistry') { - return 'module'; - } + + const isModule = typesExtendingTurboModule.length === 1; + + if (isModule && isComponent) { + throw new Error( + 'Found type extending "TurboModule" and exported "codegenNativeComponent" declaration in one file. Split them into separated files.', + ); + } + + if (isModule) { + return 'module'; + } else if (isComponent) { + return 'component'; + } else { + throw new Error( + `Default export for module specified incorrectly. It should containts + either type extending "TurboModule" or "codegenNativeComponent".`, + ); } - throw new Error( - `Default export for module specified incorrectly. It should containts - either "TurboModuleRegistry.getEnforcing" or "codegenNativeComponent".`, - ); } -function buildSchema(contents: string): ?SchemaType { +function buildSchema(contents: string, filename: ?string): ?SchemaType { const ast = flowParser.parse(contents); - const configType = getConfigType(ast); - const types = getTypes(ast); + const configType = getConfigType(ast, types); + if (configType === 'component') { return buildComponentSchema(processComponent(ast, types)); } else { - return buildModuleSchema(processModule(ast, types)); + if (filename === undefined || filename === null) { + throw new Error('Filepath expected while parasing a module'); + } + const moduleName = path.basename(filename).slice(6, -3); + return buildModuleSchema(processModule(types), moduleName); } } function parseFile(filename: string): ?SchemaType { const contents = fs.readFileSync(filename, 'utf8'); - return buildSchema(contents); + return buildSchema(contents, filename); +} + +function parseModuleFixture(filename: string): ?SchemaType { + const contents = fs.readFileSync(filename, 'utf8'); + + return buildSchema(contents, 'path/NativeSampleTurboModule.js'); } function parseString(contents: string): ?SchemaType { @@ -83,5 +117,6 @@ function parseString(contents: string): ?SchemaType { module.exports = { parseFile, + parseModuleFixture, parseString, }; diff --git a/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/failures.js b/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/failures.js index e9f6a05dc5..b23a68ef55 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/failures.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/failures.js @@ -159,30 +159,6 @@ export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); `; -const INCORRECT_NATIVE_MODULES = ` -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - * @format - */ - -'use strict'; - -import type {TurboModule} from '../RCTExport'; -import * as TurboModuleRegistry from '../TurboModuleRegistry'; - -export interface SpecWithoutTypo extends TurboModule { - // no methods -} - -export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); - -`; - const NATIVE_MODULE_NULLABLE_BOOLEAN = ` /** * Copyright (c) Facebook, Inc. and its affiliates. @@ -251,6 +227,33 @@ import * as TurboModuleRegistry from '../TurboModuleRegistry'; export default TurboModuleRegistry.getEnforcing('SampleTurboModule1'); export default TurboModuleRegistry.getEnforcing('SampleTurboModule2'); +`; + +const TWO_NATIVE_EXTENDING_TURBO_MODULE = ` +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + */ + +'use strict'; + +import type {TurboModule} from '../RCTExport'; +import * as TurboModuleRegistry from '../TurboModuleRegistry'; + +export interface Spec extends TurboModule { + +getSth(a : ?number) => void +} + +export interface Spec2 extends TurboModule { + +getSth(a : ?number) => void +} + + `; module.exports = { @@ -263,5 +266,5 @@ module.exports = { NATIVE_MODULES_WITH_NOT_EXISTING_TYPE_AS_PARAM, NATIVE_MODULES_WITH_NOT_EXISTING_TYPE_AS_RETURN, NATIVE_MODULES_WITH_NOT_ONLY_METHODS, - INCORRECT_NATIVE_MODULES, + TWO_NATIVE_EXTENDING_TURBO_MODULE, }; diff --git a/packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-test.js.snap b/packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-test.js.snap index 00cbfa59dc..ef47e2c824 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-test.js.snap +++ b/packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-test.js.snap @@ -1,7 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`RN Codegen Flow Parser Fails with error message INCORRECT_NATIVE_MODULES 1`] = `"Interface properties for \\"SpecWithTypo has been specified incorrectly.\\""`; - exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULE_NULLABLE_BOOLEAN 1`] = `"Booleans and numbers cannot be nullable for param \\"a in method \\"getSth\\"."`; exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULE_NULLABLE_NUMBER 1`] = `"Booleans and numbers cannot be nullable for param \\"a in method \\"getSth\\"."`; @@ -18,12 +16,17 @@ exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_NOT exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_PROMISE_WITHOUT_TYPE 1`] = `"Unsupported return promise type for getBool: expected to find annotation for type of promise content"`; -exports[`RN Codegen Flow Parser Fails with error message TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT 1`] = `"File should contain only one default export."`; +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_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 can generate fixture EMPTY_NATIVE_MODULE 1`] = ` Object { "modules": Object { - "SampleTurboModule": Object { + "NativeSampleTurboModule": Object { "nativeModules": Object { "SampleTurboModule": Object { "properties": Array [], @@ -37,7 +40,7 @@ Object { exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_ARRAY_WITH_ALIAS 1`] = ` Object { "modules": Object { - "SampleTurboModule": Object { + "NativeSampleTurboModule": Object { "nativeModules": Object { "SampleTurboModule": Object { "properties": Array [ @@ -77,7 +80,7 @@ Object { exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_BASIC_ARRAY 1`] = ` Object { "modules": Object { - "SampleTurboModule": Object { + "NativeSampleTurboModule": Object { "nativeModules": Object { "SampleTurboModule": Object { "properties": Array [ @@ -117,7 +120,7 @@ Object { exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_BASIC_PARAM_TYPES 1`] = ` Object { "modules": Object { - "SampleTurboModule": Object { + "NativeSampleTurboModule": Object { "nativeModules": Object { "SampleTurboModule": Object { "properties": Array [ @@ -189,7 +192,7 @@ Object { exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_CALLBACK 1`] = ` Object { "modules": Object { - "SampleTurboModule": Object { + "NativeSampleTurboModule": Object { "nativeModules": Object { "SampleTurboModule": Object { "properties": Array [ @@ -248,7 +251,7 @@ Object { exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_COMPLEX_ARRAY 1`] = ` Object { "modules": Object { - "SampleTurboModule": Object { + "NativeSampleTurboModule": Object { "nativeModules": Object { "SampleTurboModule": Object { "properties": Array [ @@ -306,7 +309,7 @@ Object { exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_COMPLEX_OBJECTS 1`] = ` Object { "modules": Object { - "SampleTurboModule": Object { + "NativeSampleTurboModule": Object { "nativeModules": Object { "SampleTurboModule": Object { "properties": Array [ @@ -453,7 +456,7 @@ Object { exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_NULLABLE_PARAM 1`] = ` Object { "modules": Object { - "SampleTurboModule": Object { + "NativeSampleTurboModule": Object { "nativeModules": Object { "SampleTurboModule": Object { "properties": Array [ @@ -487,7 +490,7 @@ Object { exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_PROMISE 1`] = ` Object { "modules": Object { - "SampleTurboModule": Object { + "NativeSampleTurboModule": Object { "nativeModules": Object { "SampleTurboModule": Object { "properties": Array [ @@ -530,7 +533,7 @@ Object { exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_SIMPLE_OBJECT 1`] = ` Object { "modules": Object { - "SampleTurboModule": Object { + "NativeSampleTurboModule": Object { "nativeModules": Object { "SampleTurboModule": Object { "properties": Array [ @@ -564,7 +567,7 @@ Object { exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_WITH_ALIASES 1`] = ` Object { "modules": Object { - "SampleTurboModule": Object { + "NativeSampleTurboModule": Object { "nativeModules": Object { "SampleTurboModule": Object { "properties": Array [ diff --git a/packages/react-native-codegen/src/parsers/flow/modules/__tests__/module-parser-test.js b/packages/react-native-codegen/src/parsers/flow/modules/__tests__/module-parser-test.js index 3bd61be813..a06fe82e41 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/__tests__/module-parser-test.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/__tests__/module-parser-test.js @@ -23,7 +23,7 @@ describe('RN Codegen Flow Parser', () => { .sort() .forEach(fixtureName => { it(`can generate fixture ${fixtureName}`, () => { - expect(FlowParser.parseFile(fixtureName)).toMatchSnapshot(); + expect(FlowParser.parseModuleFixture(fixtureName)).toMatchSnapshot(); }); }); @@ -32,7 +32,7 @@ describe('RN Codegen Flow Parser', () => { .forEach(fixtureName => { it(`Fails with error message ${fixtureName}`, () => { expect(() => { - FlowParser.parseFile(fixtureName); + FlowParser.parseModuleFixture(fixtureName); }).toThrowErrorMatchingSnapshot(); }); }); diff --git a/packages/react-native-codegen/src/parsers/flow/modules/index.js b/packages/react-native-codegen/src/parsers/flow/modules/index.js index 9feafa34b7..b610dc30e4 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/index.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/index.js @@ -18,37 +18,28 @@ function getModuleProperties(types, interfaceName) { return types[interfaceName].body.properties; } throw new Error( - `Interface properties for "${interfaceName} has been specified incorrectly."`, + `Interface properties for "${interfaceName}" has been specified incorrectly.`, ); } -function findModuleConfig( - ast, -): $ReadOnly<{|moduleName: string, interfaceName: string|}> { - const defaultExport = ast.body.filter( - node => node.type === 'ExportDefaultDeclaration', - )[0]; - try { - const interfaceName = - defaultExport.declaration.typeArguments.params[0].id.name; - - const moduleName = defaultExport.declaration.arguments[0].value; - return {interfaceName, moduleName}; - } catch (e) { - throw new Error( - `Default export for module specified incorrectly. It should containts - either "TurboModuleRegistry.getEnforcing" or "codegenNativeComponent".`, - ); - } +function findInterfaceName(types) { + return Object.keys(types) + .map(typeName => types[typeName]) + .filter( + type => + type.extends && + type.extends[0] && + type.extends[0].id.name === 'TurboModule', + )[0].id.name; } // $FlowFixMe there's no flowtype for AST -function processModule(ast, types): NativeModuleSchemaBuilderConfig { - const {interfaceName, moduleName} = findModuleConfig(ast); +function processModule(types): NativeModuleSchemaBuilderConfig { + const interfaceName = findInterfaceName(types); const moduleProperties = getModuleProperties(types, interfaceName); const properties = getMethods(moduleProperties, types); - return {properties, filename: moduleName, moduleName}; + return {properties}; } module.exports = { diff --git a/packages/react-native-codegen/src/parsers/flow/modules/schema.js b/packages/react-native-codegen/src/parsers/flow/modules/schema.js index c66960f88b..51452ae27d 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/schema.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/schema.js @@ -13,19 +13,16 @@ import type {SchemaType, MethodTypeShape} from '../../../CodegenSchema.js'; export type NativeModuleSchemaBuilderConfig = $ReadOnly<{| - filename: string, - moduleName: string, properties: $ReadOnlyArray, |}>; -function buildModuleSchema({ - filename, - moduleName, - properties, -}: NativeModuleSchemaBuilderConfig): SchemaType { +function buildModuleSchema( + {properties}: NativeModuleSchemaBuilderConfig, + moduleName: string, +): SchemaType { return { modules: { - [filename]: { + [`Native${moduleName}`]: { nativeModules: { [moduleName]: { properties,