Files
react-native/packages/react-native-codegen/src/generators/components/GeneratePropsJavaInterface.js
T
Nicola Corti 450967938a Do not include Facebook license on users codegen'd code (#32840)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/32840

Closes #31516
I've cherry-picked the original PR that had merge conficts + updated all
the headers as the one for the TurboModule generators were not handled.

Original Commit Message from acoates

The codegen generates a Facebook copyright notice at the top of the generated files.

While this might make sense on the core files, this codegen will be run on external components too.
The notice also refers to a LICENSE file in the root of this project, which might not be there if this is run on another project.
I did a quick look at some of the codegen that we ship within windows dev tools, and it looks like we normally just have comments
saying the file was codegen'd and so the file shouldn't be manually edited.
Open to suggestions on what the comment header should say.

Changelog:
[General] [Changed] - Do not include Facebook license on users codegen'd code

Reviewed By: ShikaSD

Differential Revision: D33455176

fbshipit-source-id: b247e72efb242e79d99b388c80e4126633e5234d
2022-01-19 08:07:35 -08:00

285 lines
7.4 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and 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 {
NamedShape,
CommandTypeAnnotation,
ComponentShape,
PropTypeAnnotation,
SchemaType,
} from '../../CodegenSchema';
const {
getImports,
toSafeJavaString,
getInterfaceJavaClassName,
} = require('./JavaHelpers');
// File path -> contents
type FilesOutput = Map<string, string>;
const FileTemplate = ({
packageName,
imports,
className,
extendClasses,
methods,
}: {
packageName: string,
imports: string,
className: string,
extendClasses: string,
methods: string,
}) => `/**
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
*
* Do not edit this file as changes may cause incorrect behavior and will be lost
* once the code is regenerated.
*
* ${'@'}generated by codegen project: GeneratePropsJavaInterface.js
*/
package ${packageName};
${imports}
public interface ${className}<T extends ${extendClasses}> {
${methods}
}
`;
function addNullable(imports) {
imports.add('import androidx.annotation.Nullable;');
}
function getJavaValueForProp(
prop: NamedShape<PropTypeAnnotation>,
imports,
): string {
const typeAnnotation = prop.typeAnnotation;
switch (typeAnnotation.type) {
case 'BooleanTypeAnnotation':
if (typeAnnotation.default === null) {
addNullable(imports);
return '@Nullable Boolean value';
} else {
return 'boolean value';
}
case 'StringTypeAnnotation':
addNullable(imports);
return '@Nullable String value';
case 'Int32TypeAnnotation':
return 'int value';
case 'DoubleTypeAnnotation':
return 'double value';
case 'FloatTypeAnnotation':
if (typeAnnotation.default === null) {
addNullable(imports);
return '@Nullable Float value';
} else {
return 'float value';
}
case 'ReservedPropTypeAnnotation':
switch (typeAnnotation.name) {
case 'ColorPrimitive':
addNullable(imports);
return '@Nullable Integer value';
case 'ImageSourcePrimitive':
addNullable(imports);
return '@Nullable ReadableMap value';
case 'PointPrimitive':
addNullable(imports);
return '@Nullable ReadableMap value';
case 'EdgeInsetsPrimitive':
addNullable(imports);
return '@Nullable ReadableMap value';
default:
(typeAnnotation.name: empty);
throw new Error('Received unknown ReservedPropTypeAnnotation');
}
case 'ArrayTypeAnnotation': {
addNullable(imports);
return '@Nullable ReadableArray value';
}
case 'ObjectTypeAnnotation': {
addNullable(imports);
return '@Nullable ReadableMap value';
}
case 'StringEnumTypeAnnotation':
addNullable(imports);
return '@Nullable String value';
case 'Int32EnumTypeAnnotation':
addNullable(imports);
return '@Nullable Integer value';
default:
(typeAnnotation: empty);
throw new Error('Received invalid typeAnnotation');
}
}
function generatePropsString(component: ComponentShape, imports) {
if (component.props.length === 0) {
return '// No props';
}
return component.props
.map(prop => {
return `void set${toSafeJavaString(
prop.name,
)}(T view, ${getJavaValueForProp(prop, imports)});`;
})
.join('\n' + ' ');
}
function getCommandArgJavaType(param) {
const {typeAnnotation} = param;
switch (typeAnnotation.type) {
case 'ReservedTypeAnnotation':
switch (typeAnnotation.name) {
case 'RootTag':
return 'double';
default:
(typeAnnotation.name: empty);
throw new Error(`Receieved invalid type: ${typeAnnotation.name}`);
}
case 'BooleanTypeAnnotation':
return 'boolean';
case 'DoubleTypeAnnotation':
return 'double';
case 'FloatTypeAnnotation':
return 'float';
case 'Int32TypeAnnotation':
return 'int';
case 'StringTypeAnnotation':
return 'String';
default:
(typeAnnotation.type: empty);
throw new Error('Receieved invalid typeAnnotation');
}
}
function getCommandArguments(
command: NamedShape<CommandTypeAnnotation>,
componentName: string,
): string {
return [
'T view',
...command.typeAnnotation.params.map(param => {
const commandArgJavaType = getCommandArgJavaType(param);
return `${commandArgJavaType} ${param.name}`;
}),
].join(', ');
}
function generateCommandsString(
component: ComponentShape,
componentName: string,
) {
return component.commands
.map(command => {
const safeJavaName = toSafeJavaString(command.name, false);
return `void ${safeJavaName}(${getCommandArguments(
command,
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,
packageName?: string,
assumeNonnull: boolean = false,
): FilesOutput {
// TODO: This doesn't support custom package name yet.
const normalizedPackageName = 'com.facebook.react.viewmanagers';
const outputDir = `java/${normalizedPackageName.replace(/\./g, '/')}`;
const files = new Map();
Object.keys(schema.modules).forEach(moduleName => {
const module = schema.modules[moduleName];
if (module.type !== 'Component') {
return;
}
const {components} = module;
// No components in this module
if (components == null) {
return;
}
return Object.keys(components)
.filter(componentName => {
const component = components[componentName];
return !(
component.excludedPlatforms &&
component.excludedPlatforms.includes('android')
);
})
.forEach(componentName => {
const component = components[componentName];
const className = getInterfaceJavaClassName(componentName);
const imports = getImports(component, 'interface');
const propsString = generatePropsString(component, imports);
const commandsString = generateCommandsString(
component,
componentName,
);
const extendString = getClassExtendString(component);
const replacedTemplate = FileTemplate({
imports: Array.from(imports).sort().join('\n'),
packageName: normalizedPackageName,
className,
extendClasses: extendString,
methods: [propsString, commandsString]
.join('\n' + ' ')
.trimRight(),
});
files.set(`${outputDir}/${className}.java`, replacedTemplate);
});
});
return files;
},
};