mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Update View Config generator to create command methods
Summary:
Flow types like this:
```
interface NativeCommands {
+hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void;
}
export const Commands = codegenNativeCommands<NativeCommands>();
```
get turned into this:
```
export const Commands = {
hotspotUpdate(viewRef: React.Ref<'RCTView'>, x: number, y: number) {
UIManager.dispatchViewCommand(
findNodeHandle(viewRef),
UIManager.getViewManagerConfig('RCTView').Commands.hotspotUpdate,
[x, y]
);
}
}
```
Reviewed By: rickhanlonii
Differential Revision: D15953126
fbshipit-source-id: edbb91056347d021dd0683391c903b76f3d1c33f
This commit is contained in:
committed by
Facebook Github Bot
parent
9ad60131ba
commit
2bd503285e
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @format
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
function codegenNativeCommands<T>(): T {
|
||||
return (({}: any): T);
|
||||
}
|
||||
|
||||
export default codegenNativeCommands;
|
||||
@@ -16,9 +16,13 @@ export default 'Not a view config'
|
||||
`;
|
||||
|
||||
const FULL_NATIVE_COMPONENT = `
|
||||
// @flow
|
||||
|
||||
const codegenNativeCommands = require('codegenNativeCommands');
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
|
||||
import type {
|
||||
Int32,
|
||||
BubblingEvent,
|
||||
DirectEvent,
|
||||
WithDefault,
|
||||
@@ -26,6 +30,11 @@ import type {
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
|
||||
interface NativeCommands {
|
||||
+hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void;
|
||||
+scrollTo: (viewRef: React.Ref<'RCTView'>, y: Int32, animated: boolean) => void;
|
||||
}
|
||||
|
||||
type ModuleProps = $ReadOnly<{|
|
||||
...ViewProps,
|
||||
|
||||
@@ -37,6 +46,8 @@ type ModuleProps = $ReadOnly<{|
|
||||
onBubblingEventDefinedInlineNull: (event: BubblingEvent<null>) => void,
|
||||
|}>;
|
||||
|
||||
export const Commands = codegenNativeCommands<NativeCommands>();
|
||||
|
||||
export default codegenNativeComponent<ModuleProps>('Module', {
|
||||
interfaceOnly: true,
|
||||
paperComponentName: 'RCTModule',
|
||||
|
||||
+29
-3
@@ -1,10 +1,17 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Babel plugin inline view configs can inline config for FullNativeComponent.js 1`] = `
|
||||
"const codegenNativeComponent = require('codegenNativeComponent');
|
||||
"// @flow
|
||||
const codegenNativeCommands = require('codegenNativeCommands');
|
||||
|
||||
import type { BubblingEvent, DirectEvent, WithDefault } from 'CodegenFlowtypes';
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
|
||||
import type { Int32, BubblingEvent, DirectEvent, WithDefault } from 'CodegenFlowtypes';
|
||||
import type { ViewProps } from 'ViewPropTypes';
|
||||
interface NativeCommands {
|
||||
+hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void,
|
||||
+scrollTo: (viewRef: React.Ref<'RCTView'>, y: Int32, animated: boolean) => void,
|
||||
}
|
||||
type ModuleProps = $ReadOnly<{| ...ViewProps,
|
||||
// Props
|
||||
boolean_default_true_optional_both?: ?WithDefault<boolean, true>,
|
||||
@@ -12,9 +19,18 @@ type ModuleProps = $ReadOnly<{| ...ViewProps,
|
||||
onDirectEventDefinedInlineNull: (event: DirectEvent<null>) => void,
|
||||
onBubblingEventDefinedInlineNull: (event: BubblingEvent<null>) => void,
|
||||
|}>;
|
||||
export const Commands = codegenNativeCommands<NativeCommands>();
|
||||
|
||||
const registerGeneratedViewConfig = require('registerGeneratedViewConfig');
|
||||
|
||||
const {
|
||||
UIManager
|
||||
} = require(\\"react-native\\");
|
||||
|
||||
const {
|
||||
findNodeHandle
|
||||
} = require(\\"react-native\\");
|
||||
|
||||
const ModuleViewConfig = {
|
||||
uiViewClassName: 'RCTModule',
|
||||
bubblingEventTypes: {
|
||||
@@ -39,7 +55,17 @@ const ModuleViewConfig = {
|
||||
let nativeComponentName = 'RCTModule';
|
||||
registerGeneratedViewConfig(nativeComponentName, ModuleViewConfig);
|
||||
export const __INTERNAL_VIEW_CONFIG = ModuleViewConfig;
|
||||
export default nativeComponentName;"
|
||||
export default nativeComponentName;
|
||||
export const Commands = {
|
||||
hotspotUpdate(ref, x, y) {
|
||||
UIManager.dispatchViewCommand(findNodeHandle(ref), UIManager.getViewManagerConfig(\\"RCTModule\\").Commands.hotspotUpdate, [x, y]);
|
||||
},
|
||||
|
||||
scrollTo(ref, y, animated) {
|
||||
UIManager.dispatchViewCommand(findNodeHandle(ref), UIManager.getViewManagerConfig(\\"RCTModule\\").Commands.scrollTo, [y, animated]);
|
||||
}
|
||||
|
||||
};"
|
||||
`;
|
||||
|
||||
exports[`Babel plugin inline view configs can inline config for NotANativeComponent.js 1`] = `
|
||||
|
||||
@@ -34,6 +34,10 @@ const template = `
|
||||
::_COMPONENT_CONFIG_::
|
||||
`;
|
||||
|
||||
// We use this to add to a set. Need to make sure we aren't importing
|
||||
// this multiple times.
|
||||
const UIMANAGER_IMPORT = 'const {UIManager} = require("react-native")';
|
||||
|
||||
function getReactDiffProcessValue(typeAnnotation) {
|
||||
switch (typeAnnotation.type) {
|
||||
case 'BooleanTypeAnnotation':
|
||||
@@ -239,6 +243,69 @@ function buildViewConfig(
|
||||
return j.objectExpression(properties);
|
||||
}
|
||||
|
||||
function buildCommands(
|
||||
schema: SchemaType,
|
||||
componentName: string,
|
||||
component,
|
||||
imports,
|
||||
) {
|
||||
const commands = component.commands;
|
||||
|
||||
if (commands.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
imports.add(UIMANAGER_IMPORT);
|
||||
imports.add('const {findNodeHandle} = require("react-native")');
|
||||
|
||||
const properties = commands.map(command => {
|
||||
const commandName = command.name;
|
||||
const params = command.typeAnnotation.params;
|
||||
|
||||
const componentNameLiteral = j.literal(componentName);
|
||||
const commandNameIdentifier = j.identifier(commandName);
|
||||
const arrayParams = j.arrayExpression(
|
||||
params.map(param => {
|
||||
return j.identifier(param.name);
|
||||
}),
|
||||
);
|
||||
|
||||
const expression = j.template.expression`
|
||||
UIManager.dispatchViewCommand(
|
||||
findNodeHandle(ref),
|
||||
UIManager.getViewManagerConfig(${componentNameLiteral}).Commands.${commandNameIdentifier},
|
||||
${arrayParams}
|
||||
)
|
||||
`;
|
||||
|
||||
const functionParams = params.map(param => {
|
||||
return j.identifier(param.name);
|
||||
});
|
||||
|
||||
const property = j.property(
|
||||
'init',
|
||||
commandNameIdentifier,
|
||||
j.functionExpression(
|
||||
null,
|
||||
[j.identifier('ref'), ...functionParams],
|
||||
j.blockStatement([j.expressionStatement(expression)]),
|
||||
),
|
||||
);
|
||||
property.method = true;
|
||||
|
||||
return property;
|
||||
});
|
||||
|
||||
return j.exportNamedDeclaration(
|
||||
j.variableDeclaration('const', [
|
||||
j.variableDeclarator(
|
||||
j.identifier('Commands'),
|
||||
j.objectExpression(properties),
|
||||
),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
generate(libraryName: string, schema: SchemaType): FilesOutput {
|
||||
try {
|
||||
@@ -262,7 +329,7 @@ module.exports = {
|
||||
: componentName;
|
||||
|
||||
if (component.paperComponentNameDeprecated) {
|
||||
imports.add('const {UIManager} = require("react-native")');
|
||||
imports.add(UIMANAGER_IMPORT);
|
||||
}
|
||||
|
||||
const deprecatedCheckBlock = component.paperComponentNameDeprecated
|
||||
@@ -282,8 +349,9 @@ module.exports = {
|
||||
)
|
||||
.replace(/::_DEPRECATION_CHECK_::/, deprecatedCheckBlock);
|
||||
|
||||
const replacedSource: string = j
|
||||
.withParser('flow')(replacedTemplate)
|
||||
const replacedSourceRoot = j.withParser('flow')(replacedTemplate);
|
||||
|
||||
replacedSourceRoot
|
||||
.find(j.Identifier, {
|
||||
name: 'VIEW_CONFIG',
|
||||
})
|
||||
@@ -294,8 +362,24 @@ module.exports = {
|
||||
component,
|
||||
imports,
|
||||
),
|
||||
)
|
||||
.toSource({quote: 'single', trailingComma: true});
|
||||
);
|
||||
|
||||
const commands = buildCommands(
|
||||
schema,
|
||||
paperComponentName,
|
||||
component,
|
||||
imports,
|
||||
);
|
||||
if (commands) {
|
||||
replacedSourceRoot
|
||||
.find(j.ExportDefaultDeclaration)
|
||||
.insertAfter(j(commands).toSource());
|
||||
}
|
||||
|
||||
const replacedSource: string = replacedSourceRoot.toSource({
|
||||
quote: 'single',
|
||||
trailingComma: true,
|
||||
});
|
||||
|
||||
return replacedSource;
|
||||
})
|
||||
|
||||
@@ -846,6 +846,69 @@ const TWO_COMPONENTS_DIFFERENT_FILES: SchemaType = {
|
||||
},
|
||||
};
|
||||
|
||||
const COMMANDS: SchemaType = {
|
||||
modules: {
|
||||
Switch: {
|
||||
components: {
|
||||
CommandNativeComponent: {
|
||||
extendsProps: [
|
||||
{
|
||||
type: 'ReactNativeBuiltInType',
|
||||
knownTypeName: 'ReactNativeCoreViewProps',
|
||||
},
|
||||
],
|
||||
events: [],
|
||||
props: [],
|
||||
commands: [
|
||||
{
|
||||
name: 'hotspotUpdate',
|
||||
optional: false,
|
||||
typeAnnotation: {
|
||||
type: 'FunctionTypeAnnotation',
|
||||
params: [
|
||||
{
|
||||
name: 'x',
|
||||
typeAnnotation: {
|
||||
type: 'Int32TypeAnnotation',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'y',
|
||||
typeAnnotation: {
|
||||
type: 'Int32TypeAnnotation',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'scrollTo',
|
||||
optional: false,
|
||||
typeAnnotation: {
|
||||
type: 'FunctionTypeAnnotation',
|
||||
params: [
|
||||
{
|
||||
name: 'y',
|
||||
typeAnnotation: {
|
||||
type: 'Int32TypeAnnotation',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'animated',
|
||||
typeAnnotation: {
|
||||
type: 'BooleanTypeAnnotation',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
NO_PROPS_NO_EVENTS,
|
||||
INTERFACE_ONLY,
|
||||
@@ -863,4 +926,5 @@ module.exports = {
|
||||
EVENT_NESTED_OBJECT_PROPS,
|
||||
TWO_COMPONENTS_SAME_FILE,
|
||||
TWO_COMPONENTS_DIFFERENT_FILES,
|
||||
COMMANDS,
|
||||
};
|
||||
|
||||
+23
@@ -63,6 +63,29 @@ namespace react {
|
||||
|
||||
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GenerateEventEmitterCpp can generate fixture COMMANDS 1`] = `
|
||||
Map {
|
||||
"EventEmitters.cpp" => "
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <react/components/COMMANDS/EventEmitters.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
",
|
||||
|
||||
+24
@@ -66,6 +66,30 @@ namespace react {
|
||||
|
||||
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GenerateEventEmitterH can generate fixture COMMANDS 1`] = `
|
||||
Map {
|
||||
"EventEmitters.h" => "
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <react/components/view/ViewEventEmitter.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
",
|
||||
|
||||
+29
@@ -95,6 +95,35 @@ ColorPropNativeComponentProps::ColorPropNativeComponentProps(
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GeneratePropsCpp can generate fixture COMMANDS 1`] = `
|
||||
Map {
|
||||
"Props.cpp" => "
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <react/components/COMMANDS/Props.h>
|
||||
#include <react/core/propsConversions.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
CommandNativeComponentProps::CommandNativeComponentProps(
|
||||
const CommandNativeComponentProps &sourceProps,
|
||||
const RawProps &rawProps): ViewProps(sourceProps, rawProps)
|
||||
|
||||
|
||||
{}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GeneratePropsCpp can generate fixture ENUM_PROP 1`] = `
|
||||
Map {
|
||||
"Props.cpp" => "
|
||||
|
||||
+32
@@ -157,6 +157,38 @@ class ColorPropNativeComponentProps final : public ViewProps {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GeneratePropsH can generate fixture COMMANDS 1`] = `
|
||||
Map {
|
||||
"Props.h" => "
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <react/components/view/ViewProps.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class CommandNativeComponentProps final : public ViewProps {
|
||||
public:
|
||||
CommandNativeComponentProps() = default;
|
||||
CommandNativeComponentProps(const CommandNativeComponentProps &sourceProps, const RawProps &rawProps);
|
||||
|
||||
#pragma mark - Props
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GeneratePropsH can generate fixture ENUM_PROP 1`] = `
|
||||
Map {
|
||||
"Props.h" => "
|
||||
|
||||
+23
@@ -69,6 +69,29 @@ extern const char ColorPropNativeComponentComponentName[] = \\"ColorPropNativeCo
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GenerateShadowNodeCpp can generate fixture COMMANDS 1`] = `
|
||||
Map {
|
||||
"ShadowNodes.cpp" => "
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <react/components/COMMANDS/ShadowNodes.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
extern const char CommandNativeComponentComponentName[] = \\"CommandNativeComponent\\";
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GenerateShadowNodeCpp can generate fixture ENUM_PROP 1`] = `
|
||||
Map {
|
||||
"ShadowNodes.cpp" => "
|
||||
|
||||
+33
@@ -99,6 +99,39 @@ using ColorPropNativeComponentShadowNode = ConcreteViewShadowNode<
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GenerateShadowNodeH can generate fixture COMMANDS 1`] = `
|
||||
Map {
|
||||
"ShadowNodes.h" => "
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <react/components/COMMANDS/Props.h>
|
||||
#include <react/components/view/ConcreteViewShadowNode.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
extern const char CommandNativeComponentComponentName[];
|
||||
|
||||
/*
|
||||
* \`ShadowNode\` for <CommandNativeComponent> component.
|
||||
*/
|
||||
using CommandNativeComponentShadowNode = ConcreteViewShadowNode<
|
||||
CommandNativeComponentComponentName,
|
||||
CommandNativeComponentProps>;
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GenerateShadowNodeH can generate fixture ENUM_PROP 1`] = `
|
||||
Map {
|
||||
"ShadowNodes.h" => "
|
||||
|
||||
+28
@@ -103,6 +103,34 @@ TEST(ColorPropNativeComponentProps_tintColor, etc) {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GenerateTests can generate fixture COMMANDS 1`] = `
|
||||
Map {
|
||||
"Tests.cpp" => "/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <react/components/COMMANDS/Props.h>
|
||||
#include <react/core/RawProps.h>
|
||||
#include <react/core/RawPropsParser.h>
|
||||
#include <react/core/propsConversions.h>
|
||||
|
||||
using namespace facebook::react;
|
||||
|
||||
TEST(CommandNativeComponentProps_DoesNotDie, etc) {
|
||||
auto propParser = RawPropsParser();
|
||||
propParser.prepare<CommandNativeComponentProps>();
|
||||
auto const &sourceProps = CommandNativeComponentProps();
|
||||
auto const &rawProps = RawProps(folly::dynamic::object(\\"xx_invalid_xx\\", \\"xx_invalid_xx\\"));
|
||||
rawProps.parse(propParser);
|
||||
CommandNativeComponentProps(sourceProps, rawProps);
|
||||
}",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GenerateTests can generate fixture ENUM_PROP 1`] = `
|
||||
Map {
|
||||
"Tests.cpp" => "/**
|
||||
|
||||
+52
@@ -112,6 +112,58 @@ export default nativeComponentName;
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GenerateViewConfigJs can generate fixture COMMANDS 1`] = `
|
||||
Map {
|
||||
"COMMANDSNativeViewConfig.js" => "
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const registerGeneratedViewConfig = require('registerGeneratedViewConfig');
|
||||
const {UIManager} = require(\\"react-native\\")
|
||||
const {findNodeHandle} = require(\\"react-native\\")
|
||||
|
||||
const CommandNativeComponentViewConfig = {
|
||||
uiViewClassName: 'CommandNativeComponent',
|
||||
validAttributes: {},
|
||||
};
|
||||
|
||||
let nativeComponentName = 'CommandNativeComponent';
|
||||
|
||||
registerGeneratedViewConfig(nativeComponentName, CommandNativeComponentViewConfig);
|
||||
|
||||
export const __INTERNAL_VIEW_CONFIG = CommandNativeComponentViewConfig;
|
||||
|
||||
export default nativeComponentName;
|
||||
|
||||
export const Commands = {
|
||||
hotspotUpdate(ref, x, y) {
|
||||
UIManager.dispatchViewCommand(
|
||||
findNodeHandle(ref),
|
||||
UIManager.getViewManagerConfig(\\"CommandNativeComponent\\").Commands.hotspotUpdate,
|
||||
[x, y]
|
||||
);
|
||||
},
|
||||
|
||||
scrollTo(ref, y, animated) {
|
||||
UIManager.dispatchViewCommand(
|
||||
findNodeHandle(ref),
|
||||
UIManager.getViewManagerConfig(\\"CommandNativeComponent\\").Commands.scrollTo,
|
||||
[y, animated]
|
||||
);
|
||||
}
|
||||
};
|
||||
",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GenerateViewConfigJs can generate fixture ENUM_PROP 1`] = `
|
||||
Map {
|
||||
"ENUM_PROPNativeViewConfig.js" => "
|
||||
|
||||
Reference in New Issue
Block a user