Back out "Back out "[RNCodegen] codegenNativeCommands takes list of supported commands""

Summary:
Original commit changeset: 34a8f8395ca7

The problem with the original commit was the usage of optional chaining. This diff removes the usage of optional chaining with good old fashioned null checks.

Reviewed By: rickhanlonii

Differential Revision: D16593623

fbshipit-source-id: d24cc40c85de9a2e712e5de19e9deb196003ccf2
This commit is contained in:
Eli White 2019-08-01 15:01:57 -07:00 коммит произвёл Facebook Github Bot
Родитель a84a62834c
Коммит b526f66c19
8 изменённых файлов: 209 добавлений и 19 удалений

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

@ -10,7 +10,11 @@
'use strict';
function codegenNativeCommands<T>(): T {
type Options<T = string> = $ReadOnly<{|
supportedCommands: $ReadOnlyArray<T>,
|}>;
function codegenNativeCommands<T>(options: Options<$Keys<T>>): T {
return (({}: any): T);
}

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

@ -46,7 +46,9 @@ type ModuleProps = $ReadOnly<{|
onBubblingEventDefinedInlineNull: BubblingEventHandler<null>,
|}>;
export const Commands = codegenNativeCommands<NativeCommands>();
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate', 'scrollTo'],
});
export default codegenNativeComponent<ModuleProps>('Module', {
interfaceOnly: true,

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

@ -1,5 +1,5 @@
{
"version": "0.0.3",
"version": "0.0.4",
"name": "babel-plugin-inline-view-configs",
"description": "Babel plugin to inline view configs for React Native",
"repository": {

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

@ -41,7 +41,9 @@ export type ModuleProps = $ReadOnly<{|
export const Commands = codegenNativeCommands<{
+hotspotUpdate: (ref: React.Ref<'RCTView'>, x: Int32, y: Int32) => void;
}>();
}>({
supportedCommands: ['hotspotUpdate']
});
export default codegenNativeComponent<ModuleProps>('Module');
`;
@ -79,8 +81,12 @@ export type ModuleProps = $ReadOnly<{|
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>();
export const Commands2 = codegenNativeCommands<NativeCommands>();
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate']
});
export const Commands2 = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate']
});
export default codegenNativeComponent<ModuleProps>('Module');
`;
@ -118,10 +124,93 @@ export type ModuleProps = $ReadOnly<{|
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate']
});
export default codegenNativeComponent<ModuleProps>('Module');
`;
const COMMANDS_DEFINED_WITH_MISMATCHED_METHOD_NAMES = `
/**
* 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';
const codegenNativeComponent = require('codegenNativeComponent');
const codegenNativeCommands = require('codegenNativeCommands');
import type {
Int32,
BubblingEventHandler,
DirectEventHandler,
} from 'CodegenTypes';
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;
}
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['scrollTo']
});
export default codegenNativeComponent<ModuleProps>('Module');
`;
const COMMANDS_DEFINED_WITHOUT_METHOD_NAMES = `
/**
* 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';
const codegenNativeComponent = require('codegenNativeComponent');
const codegenNativeCommands = require('codegenNativeCommands');
import type {
Int32,
BubblingEventHandler,
DirectEventHandler,
} from 'CodegenTypes';
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;
}
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>();
export default codegenNativeComponent<ModuleProps>('Module');
`;
const NULLABLE_WITH_DEFAULT = `
/**
* Copyright (c) Facebook, Inc. and its affiliates.
@ -186,6 +275,8 @@ export default codegenNativeComponent<ModuleProps>('Module');
module.exports = {
COMMANDS_DEFINED_INLINE,
COMMANDS_DEFINED_MULTIPLE_TIMES,
COMMANDS_DEFINED_WITH_MISMATCHED_METHOD_NAMES,
COMMANDS_DEFINED_WITHOUT_METHOD_NAMES,
COMMANDS_DEFINED_WITHOUT_REF,
NULLABLE_WITH_DEFAULT,
NON_OPTIONAL_KEY_WITH_DEFAULT_VALUE,

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

@ -559,7 +559,9 @@ export type ModuleProps = $ReadOnly<{|
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>();
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate', 'scrollTo']
});
export default codegenNativeComponent<ModuleProps>('Module');
`;
@ -603,7 +605,9 @@ export type ModuleProps = $ReadOnly<{|
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>();
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['scrollTo']
});
export default codegenNativeComponent<ModuleProps>('Module');
`;
@ -656,7 +660,9 @@ export type ModuleProps = $ReadOnly<{|
onDirectEventDefinedInlineWithPaperName: DirectEventHandler<EventInFile, 'paperDirectEventDefinedInlineWithPaperName'>,
|}>;
export const Commands = codegenNativeCommands<NativeCommands>();
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['scrollTo']
});
export default codegenNativeComponent<ModuleProps>('Module');
`;

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

@ -4,6 +4,10 @@ exports[`RN Codegen Flow Parser Fails with error message COMMANDS_DEFINED_INLINE
exports[`RN Codegen Flow Parser Fails with error message COMMANDS_DEFINED_MULTIPLE_TIMES 1`] = `"codegenNativeCommands may only be called once in a file"`;
exports[`RN Codegen Flow Parser Fails with error message COMMANDS_DEFINED_WITH_MISMATCHED_METHOD_NAMES 1`] = `"codegenNativeCommands expected the same supportedCommands specified in the NativeCommands interface: hotspotUpdate, scrollTo"`;
exports[`RN Codegen Flow Parser Fails with error message COMMANDS_DEFINED_WITHOUT_METHOD_NAMES 1`] = `"codegenNativeCommands must be passed options including the supported commands"`;
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 NON_OPTIONAL_KEY_WITH_DEFAULT_VALUE 1`] = `"key required_key_with_default must be optional if used with WithDefault<> annotation"`;

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

@ -14,7 +14,7 @@ import type {ComponentSchemaBuilderConfig} from './schema.js';
const {getCommands} = require('./commands');
const {getEvents} = require('./events');
const {getProps} = require('./props');
const {getOptions} = require('./options');
const {getCommandOptions, getOptions} = require('./options');
const {getExtendsProps} = require('./extends');
function findComponentConfig(ast) {
@ -58,11 +58,12 @@ function findComponentConfig(ast) {
const commandsTypeNames = namedExports
.map(statement => {
let callExpression;
let calleeName;
try {
calleeName = statement.declaration.declarations[0].init.callee.name;
callExpression = statement.declaration.declarations[0].init;
calleeName = callExpression.callee.name;
} catch (e) {
// Not a function call
return;
}
@ -70,8 +71,14 @@ function findComponentConfig(ast) {
return;
}
const typeArgumentParam =
statement.declaration.declarations[0].init.typeArguments.params[0];
// const statement.declaration.declarations[0].init
if (callExpression.arguments.length !== 1) {
throw new Error(
'codegenNativeCommands must be passed options including the supported commands',
);
}
const typeArgumentParam = callExpression.typeArguments.params[0];
if (typeArgumentParam.type !== 'GenericTypeAnnotation') {
throw new Error(
@ -79,7 +86,10 @@ function findComponentConfig(ast) {
);
}
return typeArgumentParam.id.name;
return {
commandTypeName: typeArgumentParam.id.name,
commandOptionsExpression: callExpression.arguments[0],
};
})
.filter(Boolean);
@ -89,7 +99,14 @@ function findComponentConfig(ast) {
return {
...foundConfig,
commandTypeName: commandsTypeNames[0],
commandTypeName:
commandsTypeNames[0] == null
? null
: commandsTypeNames[0].commandTypeName,
commandOptionsExpression:
commandsTypeNames[0] == null
? null
: commandsTypeNames[0].commandOptionsExpression,
};
}
@ -104,7 +121,7 @@ function getPropProperties(propsTypeName, types) {
}
}
function getCommandProperties(commandTypeName, types) {
function getCommandProperties(commandTypeName, types, commandOptions) {
if (commandTypeName == null) {
return [];
}
@ -119,13 +136,39 @@ function getCommandProperties(commandTypeName, types) {
);
}
let properties;
try {
return typeAlias.body.properties;
properties = typeAlias.body.properties;
} catch (e) {
throw new Error(
`Failed to find type definition for "${commandTypeName}", please check that you have a valid codegen flow file`,
);
}
const flowPropertyNames = properties
.map(property => property && property.key && property.key.name)
.filter(Boolean);
if (commandOptions == null || commandOptions.supportedCommands == null) {
throw new Error(
'codegenNativeCommands must be given an options object with supportedCommands array',
);
}
if (
commandOptions.supportedCommands.length !== flowPropertyNames.length ||
!commandOptions.supportedCommands.every(supportedCommand =>
flowPropertyNames.includes(supportedCommand),
)
) {
throw new Error(
`codegenNativeCommands expected the same supportedCommands specified in the ${commandTypeName} interface: ${flowPropertyNames.join(
', ',
)}`,
);
}
return properties;
}
// $FlowFixMe there's no flowtype for AST
@ -134,11 +177,18 @@ function processComponent(ast, types): ComponentSchemaBuilderConfig {
componentName,
propsTypeName,
commandTypeName,
commandOptionsExpression,
optionsExpression,
} = findComponentConfig(ast);
const propProperties = getPropProperties(propsTypeName, types);
const commandProperties = getCommandProperties(commandTypeName, types);
const commandOptions = getCommandOptions(commandOptionsExpression);
const commandProperties = getCommandProperties(
commandTypeName,
types,
commandOptions,
);
const extendsProps = getExtendsProps(propProperties);
const options = getOptions(optionsExpression);

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

@ -15,6 +15,38 @@ import type {OptionsShape} from '../../../CodegenSchema.js';
// $FlowFixMe there's no flowtype for ASTs
type OptionsAST = Object;
export type CommandOptions = $ReadOnly<{|
supportedCommands: $ReadOnlyArray<string>,
|}>;
function getCommandOptions(
commandOptionsExpression: OptionsAST,
): ?CommandOptions {
if (commandOptionsExpression == null) {
return null;
}
let foundOptions;
try {
foundOptions = commandOptionsExpression.properties.reduce(
(options, prop) => {
options[prop.key.name] = (
(prop && prop.value && prop.value.elements) ||
[]
).map(element => element && element.value);
return options;
},
{},
);
} catch (e) {
throw new Error(
'Failed to parse command options, please check that they are defined correctly',
);
}
return foundOptions;
}
function getOptions(optionsExpression: OptionsAST): ?OptionsShape {
if (!optionsExpression) {
return null;
@ -44,5 +76,6 @@ function getOptions(optionsExpression: OptionsAST): ?OptionsShape {
}
module.exports = {
getCommandOptions,
getOptions,
};