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
This commit is contained in:
Michał Osadnik 2019-07-11 09:26:15 -07:00 коммит произвёл Facebook Github Bot
Родитель 4d50f5f4b8
Коммит cbd4ad43c0
6 изменённых файлов: 120 добавлений и 91 удалений

Просмотреть файл

@ -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,
};

Просмотреть файл

@ -159,30 +159,6 @@ export default TurboModuleRegistry.getEnforcing<Spec>('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<SpecWithTypo>('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<Spec1>('SampleTurboModule1');
export default TurboModuleRegistry.getEnforcing<Spec2>('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,
};

Просмотреть файл

@ -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 [

Просмотреть файл

@ -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();
});
});

Просмотреть файл

@ -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 = {

Просмотреть файл

@ -13,19 +13,16 @@
import type {SchemaType, MethodTypeShape} from '../../../CodegenSchema.js';
export type NativeModuleSchemaBuilderConfig = $ReadOnly<{|
filename: string,
moduleName: string,
properties: $ReadOnlyArray<MethodTypeShape>,
|}>;
function buildModuleSchema({
filename,
moduleName,
properties,
}: NativeModuleSchemaBuilderConfig): SchemaType {
function buildModuleSchema(
{properties}: NativeModuleSchemaBuilderConfig,
moduleName: string,
): SchemaType {
return {
modules: {
[filename]: {
[`Native${moduleName}`]: {
nativeModules: {
[moduleName]: {
properties,