Add GeneratePropsJavaInterface

Summary: Adds GeneratePropsJavaInterface to the codegen to init open sourcing the Java codegen

Reviewed By: mdvacca, makovkastar

Differential Revision: D16280966

fbshipit-source-id: e3428285562329a22c1710cc7347c31f7c01d9c0
This commit is contained in:
Rick Hanlon
2019-07-19 15:04:05 -07:00
committed by Facebook Github Bot
parent b1bec856c3
commit 9830d0d7b7
4 changed files with 540 additions and 0 deletions
@@ -0,0 +1,143 @@
/**
* 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
* @format
*/
'use strict';
import type {
ComponentShape,
PropTypeShape,
SchemaType,
} from '../../CodegenSchema';
const {getImports, toSafeJavaString} = require('./JavaHelpers');
// File path -> contents
type FilesOutput = Map<string, string>;
const template = `
package com.facebook.react.viewmanagers;
::_IMPORTS_::
public interface ::_CLASSNAME_::<T extends ::_EXTEND_CLASSES_::> {
::_PROP_SETTERS_::
}
`;
function getJavaValueForProp(
prop: PropTypeShape,
componentName: string,
): string {
const typeAnnotation = prop.typeAnnotation;
switch (typeAnnotation.type) {
case 'BooleanTypeAnnotation':
return 'boolean value';
case 'StringTypeAnnotation':
return 'String value';
case 'Int32TypeAnnotation':
return 'int value';
case 'FloatTypeAnnotation':
return 'Float value';
case 'NativePrimitiveTypeAnnotation':
switch (typeAnnotation.name) {
case 'ColorPrimitive':
return 'Integer value';
case 'ImageSourcePrimitive':
return 'ReadableMap value';
case 'PointPrimitive':
return 'ReadableMap value';
default:
(typeAnnotation.name: empty);
throw new Error('Receieved unknown NativePrimitiveTypeAnnotation');
}
case 'ArrayTypeAnnotation': {
return 'ReadableArray value';
}
case 'StringEnumTypeAnnotation':
return 'String value';
default:
(typeAnnotation: empty);
throw new Error('Receieved invalid typeAnnotation');
}
}
function generatePropsString(component: ComponentShape, componentName: string) {
if (component.props.length === 0) {
return ' // No props';
}
return component.props
.map(prop => {
return `void set${toSafeJavaString(
prop.name,
)}(T view, ${getJavaValueForProp(prop, componentName)});`;
})
.join(',\n' + ' ');
}
function getClassExtendString(component): string {
const extendString = component.extendsProps
.map(extendProps => {
switch (extendProps.type) {
case 'ReactNativeBuiltInType':
switch (extendProps.knownTypeName) {
case 'ReactNativeCoreViewProps':
return 'View';
default:
(extendProps.knownTypeName: empty);
throw new Error('Invalid knownTypeName');
}
default:
(extendProps.type: empty);
throw new Error('Invalid extended type');
}
})
.join('');
return extendString;
}
module.exports = {
generate(libraryName: string, schema: SchemaType): FilesOutput {
const files = new Map();
Object.keys(schema.modules).forEach(moduleName => {
const components = schema.modules[moduleName].components;
// No components in this module
if (components == null) {
return;
}
return Object.keys(components).forEach(componentName => {
const component = components[componentName];
const className = `${componentName}Interface`;
const fileName = `${className}.java`;
const imports = getImports(component);
const propsString = generatePropsString(component, componentName);
const extendString = getClassExtendString(component);
const replacedTemplate = template
.replace(
/::_IMPORTS_::/g,
Array.from(imports)
.sort()
.join('\n'),
)
.replace(/::_CLASSNAME_::/g, className)
.replace('::_EXTEND_CLASSES_::', extendString)
.replace('::_PROP_SETTERS_::', propsString);
files.set(fileName, replacedTemplate);
});
});
return files;
},
};
@@ -0,0 +1,77 @@
/**
* 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
* @format
*/
'use strict';
import type {ComponentShape} from '../../CodegenSchema';
function upperCaseFirst(inString: string): string {
return inString[0].toUpperCase() + inString.slice(1);
}
export function toSafeJavaString(input: string): string {
return input
.split('-')
.map(upperCaseFirst)
.join('');
}
export function getImports(component: ComponentShape): Set<string> {
const imports: Set<string> = new Set();
component.extendsProps.forEach(extendProps => {
switch (extendProps.type) {
case 'ReactNativeBuiltInType':
switch (extendProps.knownTypeName) {
case 'ReactNativeCoreViewProps':
imports.add('import android.view.View;');
return;
default:
(extendProps.knownTypeName: empty);
throw new Error('Invalid knownTypeName');
}
default:
(extendProps.type: empty);
throw new Error('Invalid extended type');
}
});
function addImportsForNativeName(name) {
switch (name) {
case 'ColorPrimitive':
return;
case 'ImageSourcePrimitive':
imports.add('import com.facebook.react.bridge.ReadableMap;');
return;
case 'PointPrimitive':
imports.add('import com.facebook.react.bridge.ReadableMap;');
return;
default:
(name: empty);
throw new Error(
`Invalid NativePrimitiveTypeAnnotation name, got ${name}`,
);
}
}
component.props.forEach(prop => {
const typeAnnotation = prop.typeAnnotation;
if (typeAnnotation.type === 'NativePrimitiveTypeAnnotation') {
addImportsForNativeName(typeAnnotation.name);
}
if (typeAnnotation.type === 'ArrayTypeAnnotation') {
imports.add('import com.facebook.react.bridge.ReadableArray;');
}
});
return imports;
}
@@ -0,0 +1,27 @@
/**
* 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.
*
* @emails oncall+react_native
* @flow strict-local
* @format
*/
'use strict';
const fixtures = require('../__test_fixtures__/fixtures.js');
const generator = require('../GeneratePropsJavaInterface.js');
describe('GeneratePropsJavaInterface', () => {
Object.keys(fixtures)
.sort()
.forEach(fixtureName => {
const fixture = fixtures[fixtureName];
it(`can generate fixture ${fixtureName}`, () => {
expect(generator.generate(fixtureName, fixture)).toMatchSnapshot();
});
});
});
@@ -0,0 +1,293 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`GeneratePropsJavaInterface can generate fixture ARRAY_PROPS 1`] = `
Map {
"ArrayPropsNativeComponentInterface.java" => "
package com.facebook.react.viewmanagers;
import android.view.View;
import com.facebook.react.bridge.ReadableArray;
public interface ArrayPropsNativeComponentInterface<T extends View> {
void setNames(T view, ReadableArray value);,
void setDisableds(T view, ReadableArray value);,
void setProgress(T view, ReadableArray value);,
void setRadii(T view, ReadableArray value);,
void setColors(T view, ReadableArray value);,
void setSrcs(T view, ReadableArray value);,
void setPoints(T view, ReadableArray value);,
void setSizes(T view, ReadableArray value);
}
",
}
`;
exports[`GeneratePropsJavaInterface can generate fixture BOOLEAN_PROP 1`] = `
Map {
"BooleanPropNativeComponentInterface.java" => "
package com.facebook.react.viewmanagers;
import android.view.View;
public interface BooleanPropNativeComponentInterface<T extends View> {
void setDisabled(T view, boolean value);
}
",
}
`;
exports[`GeneratePropsJavaInterface can generate fixture COLOR_PROP 1`] = `
Map {
"ColorPropNativeComponentInterface.java" => "
package com.facebook.react.viewmanagers;
import android.view.View;
public interface ColorPropNativeComponentInterface<T extends View> {
void setTintColor(T view, Integer value);
}
",
}
`;
exports[`GeneratePropsJavaInterface can generate fixture COMMANDS 1`] = `
Map {
"CommandNativeComponentInterface.java" => "
package com.facebook.react.viewmanagers;
import android.view.View;
public interface CommandNativeComponentInterface<T extends View> {
// No props
}
",
}
`;
exports[`GeneratePropsJavaInterface can generate fixture ENUM_PROP 1`] = `
Map {
"EnumPropsNativeComponentInterface.java" => "
package com.facebook.react.viewmanagers;
import android.view.View;
public interface EnumPropsNativeComponentInterface<T extends View> {
void setAlignment(T view, String value);
}
",
}
`;
exports[`GeneratePropsJavaInterface can generate fixture EVENT_NESTED_OBJECT_PROPS 1`] = `
Map {
"EventsNestedObjectNativeComponentInterface.java" => "
package com.facebook.react.viewmanagers;
import android.view.View;
public interface EventsNestedObjectNativeComponentInterface<T extends View> {
void setDisabled(T view, boolean value);
}
",
}
`;
exports[`GeneratePropsJavaInterface can generate fixture EVENT_PROPS 1`] = `
Map {
"EventsNativeComponentInterface.java" => "
package com.facebook.react.viewmanagers;
import android.view.View;
public interface EventsNativeComponentInterface<T extends View> {
void setDisabled(T view, boolean value);
}
",
}
`;
exports[`GeneratePropsJavaInterface can generate fixture EVENTS_WITH_PAPER_NAME 1`] = `
Map {
"InterfaceOnlyComponentInterface.java" => "
package com.facebook.react.viewmanagers;
import android.view.View;
public interface InterfaceOnlyComponentInterface<T extends View> {
// No props
}
",
}
`;
exports[`GeneratePropsJavaInterface can generate fixture FLOAT_PROPS 1`] = `
Map {
"FloatPropNativeComponentInterface.java" => "
package com.facebook.react.viewmanagers;
import android.view.View;
public interface FloatPropNativeComponentInterface<T extends View> {
void setBlurRadius(T view, Float value);,
void setBlurRadius2(T view, Float value);,
void setBlurRadius3(T view, Float value);,
void setBlurRadius4(T view, Float value);,
void setBlurRadius5(T view, Float value);,
void setBlurRadius6(T view, Float value);
}
",
}
`;
exports[`GeneratePropsJavaInterface can generate fixture IMAGE_PROP 1`] = `
Map {
"ImagePropNativeComponentInterface.java" => "
package com.facebook.react.viewmanagers;
import android.view.View;
import com.facebook.react.bridge.ReadableMap;
public interface ImagePropNativeComponentInterface<T extends View> {
void setThumbImage(T view, ReadableMap value);
}
",
}
`;
exports[`GeneratePropsJavaInterface can generate fixture INTEGER_PROPS 1`] = `
Map {
"IntegerPropNativeComponentInterface.java" => "
package com.facebook.react.viewmanagers;
import android.view.View;
public interface IntegerPropNativeComponentInterface<T extends View> {
void setProgress1(T view, int value);,
void setProgress2(T view, int value);,
void setProgress3(T view, int value);
}
",
}
`;
exports[`GeneratePropsJavaInterface can generate fixture INTERFACE_ONLY 1`] = `
Map {
"InterfaceOnlyComponentInterface.java" => "
package com.facebook.react.viewmanagers;
import android.view.View;
public interface InterfaceOnlyComponentInterface<T extends View> {
void setAccessibilityHint(T view, String value);
}
",
}
`;
exports[`GeneratePropsJavaInterface can generate fixture MULTI_NATIVE_PROP 1`] = `
Map {
"ImageColorPropNativeComponentInterface.java" => "
package com.facebook.react.viewmanagers;
import android.view.View;
import com.facebook.react.bridge.ReadableMap;
public interface ImageColorPropNativeComponentInterface<T extends View> {
void setThumbImage(T view, ReadableMap value);,
void setColor(T view, Integer value);,
void setThumbTintColor(T view, Integer value);,
void setPoint(T view, ReadableMap value);
}
",
}
`;
exports[`GeneratePropsJavaInterface can generate fixture NO_PROPS_NO_EVENTS 1`] = `
Map {
"NoPropsNoEventsComponentInterface.java" => "
package com.facebook.react.viewmanagers;
import android.view.View;
public interface NoPropsNoEventsComponentInterface<T extends View> {
// No props
}
",
}
`;
exports[`GeneratePropsJavaInterface can generate fixture POINT_PROP 1`] = `
Map {
"PointPropNativeComponentInterface.java" => "
package com.facebook.react.viewmanagers;
import android.view.View;
import com.facebook.react.bridge.ReadableMap;
public interface PointPropNativeComponentInterface<T extends View> {
void setStartPoint(T view, ReadableMap value);
}
",
}
`;
exports[`GeneratePropsJavaInterface can generate fixture STRING_PROP 1`] = `
Map {
"StringPropComponentInterface.java" => "
package com.facebook.react.viewmanagers;
import android.view.View;
public interface StringPropComponentInterface<T extends View> {
void setAccessibilityHint(T view, String value);,
void setAccessibilityRole(T view, String value);
}
",
}
`;
exports[`GeneratePropsJavaInterface can generate fixture TWO_COMPONENTS_DIFFERENT_FILES 1`] = `
Map {
"MultiFile1NativeComponentInterface.java" => "
package com.facebook.react.viewmanagers;
import android.view.View;
public interface MultiFile1NativeComponentInterface<T extends View> {
void setDisabled(T view, boolean value);
}
",
"MultiFile2NativeComponentInterface.java" => "
package com.facebook.react.viewmanagers;
import android.view.View;
public interface MultiFile2NativeComponentInterface<T extends View> {
void setDisabled(T view, boolean value);
}
",
}
`;
exports[`GeneratePropsJavaInterface can generate fixture TWO_COMPONENTS_SAME_FILE 1`] = `
Map {
"MultiComponent1NativeComponentInterface.java" => "
package com.facebook.react.viewmanagers;
import android.view.View;
public interface MultiComponent1NativeComponentInterface<T extends View> {
void setDisabled(T view, boolean value);
}
",
"MultiComponent2NativeComponentInterface.java" => "
package com.facebook.react.viewmanagers;
import android.view.View;
public interface MultiComponent2NativeComponentInterface<T extends View> {
void setDisabled(T view, boolean value);
}
",
}
`;