Skip to content

Commit

Permalink
Change module parser to consider extends instead of default exports
Browse files Browse the repository at this point in the history
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 in testing logic and updating snapshots.

Reviewed By: rickhanlonii

Differential Revision: D16205707

fbshipit-source-id: c75cd4febf773ba5101e6b60ed1a921773246009
  • Loading branch information
osdnk authored and facebook-github-bot committed Jul 12, 2019
1 parent 9374b23 commit c20eaa5
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 82 deletions.
75 changes: 55 additions & 20 deletions packages/react-native-codegen/src/parsers/flow/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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 {
Expand All @@ -83,5 +117,6 @@ function parseString(contents: string): ?SchemaType {

module.exports = {
parseFile,
parseModuleFixture,
parseString,
};
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;

const INCORRECT_NATIVE_MODULES = `
const NATIVE_MODULE_NULLABLE_BOOLEAN = `
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
Expand All @@ -175,15 +175,15 @@ const INCORRECT_NATIVE_MODULES = `
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface SpecWithoutTypo extends TurboModule {
// no methods
export interface Spec extends TurboModule {
+getSth(a : ?boolean) => void
}
export default TurboModuleRegistry.getEnforcing<SpecWithTypo>('SampleTurboModule');
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;

const NATIVE_MODULE_NULLABLE_BOOLEAN = `
const NATIVE_MODULE_NULLABLE_NUMBER = `
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
Expand All @@ -200,14 +200,14 @@ import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getSth(a : ?boolean) => void
+getSth(a : ?number) => void
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;

const NATIVE_MODULE_NULLABLE_NUMBER = `
const TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT = `
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
Expand All @@ -223,15 +223,13 @@ const NATIVE_MODULE_NULLABLE_NUMBER = `
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getSth(a : ?number) => void
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
export default TurboModuleRegistry.getEnforcing<Spec1>('SampleTurboModule1');
export default TurboModuleRegistry.getEnforcing<Spec2>('SampleTurboModule2');
`;

const TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT = `
const TWO_NATIVE_EXTENDING_TURBO_MODULE = `
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
Expand All @@ -247,9 +245,14 @@ const TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT = `
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
}
export default TurboModuleRegistry.getEnforcing<Spec1>('SampleTurboModule1');
export default TurboModuleRegistry.getEnforcing<Spec2>('SampleTurboModule2');
`;

Expand All @@ -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,
};
Original file line number Diff line number Diff line change
@@ -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\\"."`;
Expand All @@ -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 [],
Expand All @@ -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 [
Expand Down Expand Up @@ -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 [
Expand Down Expand Up @@ -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 [
Expand Down Expand Up @@ -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 [
Expand Down Expand Up @@ -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 [
Expand Down Expand Up @@ -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 [
Expand Down Expand Up @@ -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 [
Expand Down Expand Up @@ -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 [
Expand Down Expand Up @@ -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 [
Expand Down Expand Up @@ -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 [
Expand Down Expand Up @@ -639,7 +642,7 @@ Object {
exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_WITH_FLOAT_AND_INT32 1`] = `
Object {
"modules": Object {
"SampleTurboModule": Object {
"NativeSampleTurboModule": Object {
"nativeModules": Object {
"SampleTurboModule": Object {
"properties": Array [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
});

Expand All @@ -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();
});
});
Expand Down
35 changes: 13 additions & 22 deletions packages/react-native-codegen/src/parsers/flow/modules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
Loading

0 comments on commit c20eaa5

Please sign in to comment.