/** * 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. * * * @format */ 'use strict'; const _require = require('./JavaHelpers'), getImports = _require.getImports, toSafeJavaString = _require.toSafeJavaString, getInterfaceJavaClassName = _require.getInterfaceJavaClassName; // File path -> contents const FileTemplate = ({ packageName, imports, className, extendClasses, methods, }) => `/** * 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} { ${methods} } `; function addNullable(imports) { imports.add('import androidx.annotation.Nullable;'); } function getJavaValueForProp(prop, imports) { 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 'ImageRequestPrimitive': addNullable(imports); return '@Nullable ReadableMap value'; case 'PointPrimitive': addNullable(imports); return '@Nullable ReadableMap value'; case 'EdgeInsetsPrimitive': addNullable(imports); return '@Nullable ReadableMap value'; case 'DimensionPrimitive': addNullable(imports); return '@Nullable YogaValue value'; default: typeAnnotation.name; 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'; case 'MixedTypeAnnotation': return 'Dynamic value'; default: typeAnnotation; throw new Error('Received invalid typeAnnotation'); } } function generatePropsString(component, 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.typeAnnotation; switch (typeAnnotation.type) { case 'ReservedTypeAnnotation': switch (typeAnnotation.name) { case 'RootTag': return 'double'; default: typeAnnotation.name; 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; throw new Error('Receieved invalid typeAnnotation'); } } function getCommandArguments(command, componentName) { return [ 'T view', ...command.typeAnnotation.params.map(param => { const commandArgJavaType = getCommandArgJavaType(param); return `${commandArgJavaType} ${param.name}`; }), ].join(', '); } function generateCommandsString(component, componentName) { return component.commands .map(command => { const safeJavaName = toSafeJavaString(command.name, false); return `void ${safeJavaName}(${getCommandArguments( command, componentName, )});`; }) .join('\n' + ' '); } function getClassExtendString(component) { const extendString = component.extendsProps .map(extendProps => { switch (extendProps.type) { case 'ReactNativeBuiltInType': switch (extendProps.knownTypeName) { case 'ReactNativeCoreViewProps': return 'View'; default: extendProps.knownTypeName; throw new Error('Invalid knownTypeName'); } default: extendProps.type; throw new Error('Invalid extended type'); } }) .join(''); return extendString; } module.exports = { generate(libraryName, schema, packageName, assumeNonnull = false) { // 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.components; // 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; }, };