mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
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:
committed by
Facebook Github Bot
parent
7a6bb389df
commit
11ff5670a9
@@ -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,
|
||||
|
||||
+18
@@ -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
@@ -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
@@ -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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user