Prepare a skeleton for native modules schema json generating

Summary: It's a skeleton for preparing schema json from native modules. Right not it does nothing, but it should considered as a kind of workplace for further work

Reviewed By: TheSavior, cpojer, RSNara

Differential Revision: D16107216

fbshipit-source-id: 12717d015e7409d980ef16cccd81330fa978b0b1
This commit is contained in:
Michał Osadnik
2019-07-04 05:41:16 -07:00
committed by Facebook Github Bot
parent 7a6bb389df
commit 11ff5670a9
7 changed files with 266 additions and 27 deletions
+9
View File
@@ -128,6 +128,12 @@ export type PropTypeShape = $ReadOnly<{|
typeAnnotation: PropTypeTypeAnnotation,
|}>;
export type MethodTypeShape = $ReadOnly<{||}>;
export type NativeModuleShape = $ReadOnly<{|
properties: $ReadOnlyArray<MethodTypeShape>,
|}>;
export type EventTypeShape = $ReadOnly<{|
name: string,
bubblingType: 'direct' | 'bubble',
@@ -179,6 +185,9 @@ export type SchemaType = $ReadOnly<{|
components?: $ReadOnly<{
[component: string]: ComponentShape,
}>,
nativeModules?: $ReadOnly<{
[nativeModule: string]: NativeModuleShape,
}>,
|}>,
}>,
|}>;
@@ -10,6 +10,52 @@
'use strict';
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 TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT = `
/**
* 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 default TurboModuleRegistry.getEnforcing<Spec1>('SampleTurboModule1');
export default TurboModuleRegistry.getEnforcing<Spec2>('SampleTurboModule2');
`;
const COMMANDS_DEFINED_INLINE = `
/**
* Copyright (c) Facebook, Inc. and its affiliates.
@@ -154,6 +200,8 @@ export default codegenNativeComponent<ModuleProps>('Module');
`;
module.exports = {
TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT,
INCORRECT_NATIVE_MODULES,
COMMANDS_DEFINED_INLINE,
COMMANDS_DEFINED_MULTIPLE_TIMES,
COMMANDS_DEFINED_WITHOUT_REF,
@@ -10,6 +10,30 @@
'use strict';
const EMPTY_NATIVE_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 {
// mo methods
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const EVENT_DEFINITION = `
boolean_required: boolean,
boolean_optional_key?: boolean,
@@ -550,6 +574,7 @@ export default codegenNativeComponent<ModuleProps>('Module');
`;
module.exports = {
EMPTY_NATIVE_MODULE,
ALL_PROP_TYPES_NO_EVENTS,
ARRAY_PROP_TYPES_NO_EVENTS,
ONE_OF_EACH_PROP_EVENT_DEFAULT_AND_OPTIONS,
@@ -6,8 +6,12 @@ exports[`RN Codegen Flow Parser Fails with error message COMMANDS_DEFINED_MULTIP
exports[`RN Codegen Flow Parser Fails with error message COMMANDS_DEFINED_WITHOUT_REF 1`] = `"The first argument of method hotspotUpdate must be of type React.Ref<>"`;
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 NULLABLE_WITH_DEFAULT 1`] = `"WithDefault<> is optional and does not need to be marked as optional. Please remove the ? annotation in front of it."`;
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 can generate fixture ALL_PROP_TYPES_NO_EVENTS 1`] = `
Object {
"modules": Object {
@@ -856,6 +860,20 @@ Object {
}
`;
exports[`RN Codegen Flow Parser can generate fixture EMPTY_NATIVE_MODULE 1`] = `
Object {
"modules": Object {
"SampleTurboModule": Object {
"nativeModules": Object {
"SampleTurboModule": Object {
"properties": Array [],
},
},
},
},
}
`;
exports[`RN Codegen Flow Parser can generate fixture EVENTS_DEFINED_AS_NULL_INLINE 1`] = `
Object {
"modules": Object {
+89 -24
View File
@@ -11,6 +11,7 @@
'use strict';
import type {SchemaType} from '../../CodegenSchema.js';
import type {SchemaBuilderConfig} from './schema.js';
// $FlowFixMe there's no flowtype flow-parser
const flowParser = require('flow-parser');
const fs = require('fs');
@@ -20,8 +21,9 @@ const {getEvents} = require('./events');
const {getProps} = require('./props');
const {getOptions} = require('./options');
const {getExtendsProps} = require('./extends');
const {getMethods} = require('./methods');
function findConfig(ast) {
function findComponentConfig(ast) {
const foundConfigs = [];
const defaultExports = ast.body.filter(
@@ -148,36 +150,99 @@ function getCommandProperties(commandTypeName, types) {
}
}
function processString(contents: string) {
function getConfigType(ast): '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.');
}
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';
}
}
throw new Error(
`Default export for module specified incorrectly. It should containts
either "TurboModuleRegistry.getEnforcing" or "codegenNativeComponent".`,
);
}
function getModuleProperties(types, interfaceName) {
if (types[interfaceName] && types[interfaceName].body) {
return types[interfaceName].body.properties;
}
throw new Error(
`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 processString(contents: string): SchemaBuilderConfig {
const ast = flowParser.parse(contents);
const types = getTypes(ast);
const {
componentName,
propsTypeName,
commandTypeName,
optionsExpression,
} = findConfig(ast);
const propProperties = getPropProperties(propsTypeName, types);
const commandProperties = getCommandProperties(commandTypeName, types);
const configType = getConfigType(ast);
const extendsProps = getExtendsProps(propProperties);
const options = getOptions(optionsExpression);
if (configType === 'component') {
const {
componentName,
propsTypeName,
commandTypeName,
optionsExpression,
} = findComponentConfig(ast);
const props = getProps(propProperties);
const events = getEvents(propProperties, types);
const commands = getCommands(commandProperties);
const propProperties = getPropProperties(propsTypeName, types);
const commandProperties = getCommandProperties(commandTypeName, types);
return {
filename: componentName,
componentName,
options,
extendsProps,
events,
props,
commands,
};
const extendsProps = getExtendsProps(propProperties);
const options = getOptions(optionsExpression);
const props = getProps(propProperties);
const events = getEvents(propProperties, types);
const commands = getCommands(commandProperties);
return {
configType,
filename: componentName,
componentName,
options,
extendsProps,
events,
props,
commands,
};
} else {
const {interfaceName, moduleName} = findModuleConfig(ast);
const moduleProperties = getModuleProperties(types, interfaceName);
const properties = getMethods(moduleProperties);
return {configType, properties, filename: moduleName, moduleName};
}
}
function parseFile(filename: string): ?SchemaType {
@@ -0,0 +1,33 @@
/**
* 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 strict-local
* @format
*/
'use strict';
import type {MethodTypeShape} from '../../CodegenSchema.js';
function buildMethodSchema(property) {
return undefined;
}
// $FlowFixMe there's no flowtype for ASTs
type PropAST = Object;
function getMethods(
typeDefinition: $ReadOnlyArray<PropAST>,
): $ReadOnlyArray<MethodTypeShape> {
return typeDefinition
.filter(property => property.type === 'ObjectTypeProperty')
.map(buildMethodSchema)
.filter(Boolean);
}
module.exports = {
getMethods,
};
+44 -3
View File
@@ -19,7 +19,11 @@ import type {
OptionsShape,
} from '../../CodegenSchema.js';
type SchemaBuilderConfig = $ReadOnly<{|
export type SchemaBuilderConfig =
| $ReadOnly<{|configType: 'module', ...NativeModuleSchemaBuilderConfig|}>
| $ReadOnly<{|configType: 'component', ...ComponentSchemaBuilderConfig|}>;
type ComponentSchemaBuilderConfig = $ReadOnly<{|
filename: string,
componentName: string,
extendsProps: $ReadOnlyArray<ExtendsPropsShape>,
@@ -29,7 +33,26 @@ type SchemaBuilderConfig = $ReadOnly<{|
options?: ?OptionsShape,
|}>;
function buildSchema({
type NativeModuleSchemaBuilderConfig = $ReadOnly<{|
filename: string,
moduleName: string,
properties: $ReadOnlyArray<{||}>,
|}>;
function buildSchema(schemaInput: SchemaBuilderConfig): SchemaType {
if (schemaInput.configType === 'component') {
const {configType, ...componentSchemaInput} = schemaInput;
return buildComponentSchema(componentSchemaInput);
} else if (schemaInput.configType === 'module') {
const {configType, ...moduleSchemaInput} = schemaInput;
return buildNativeModuleSchema(moduleSchemaInput);
}
throw new Error(
'Expected schema type of module or component, received wrong schema type',
);
}
function buildComponentSchema({
filename,
componentName,
extendsProps,
@@ -37,7 +60,7 @@ function buildSchema({
props,
options,
commands,
}: SchemaBuilderConfig): SchemaType {
}: ComponentSchemaBuilderConfig): SchemaType {
return {
modules: {
[filename]: {
@@ -55,6 +78,24 @@ function buildSchema({
};
}
function buildNativeModuleSchema({
filename,
moduleName,
properties,
}: NativeModuleSchemaBuilderConfig): SchemaType {
return {
modules: {
[filename]: {
nativeModules: {
[moduleName]: {
properties,
},
},
},
},
};
}
module.exports = {
buildSchema,
};