328 lines
9.1 KiB
JavaScript
328 lines
9.1 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.
|
|
*
|
|
*
|
|
* @format
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
function getOrdinalNumber(num) {
|
|
switch (num) {
|
|
case 1:
|
|
return '1st';
|
|
case 2:
|
|
return '2nd';
|
|
case 3:
|
|
return '3rd';
|
|
}
|
|
if (num <= 20) {
|
|
return `${num}th`;
|
|
}
|
|
return 'unknown';
|
|
}
|
|
const ProtocolTemplate = ({componentName, methods}) =>
|
|
`
|
|
@protocol RCT${componentName}ViewProtocol <NSObject>
|
|
${methods}
|
|
@end
|
|
`.trim();
|
|
const CommandHandlerIfCaseConvertArgTemplate = ({
|
|
componentName,
|
|
expectedKind,
|
|
argNumber,
|
|
argNumberString,
|
|
expectedKindString,
|
|
argConversion,
|
|
}) =>
|
|
`
|
|
NSObject *arg${argNumber} = args[${argNumber}];
|
|
#if RCT_DEBUG
|
|
if (!RCTValidateTypeOfViewCommandArgument(arg${argNumber}, ${expectedKind}, @"${expectedKindString}", @"${componentName}", commandName, @"${argNumberString}")) {
|
|
return;
|
|
}
|
|
#endif
|
|
${argConversion}
|
|
`.trim();
|
|
const CommandHandlerIfCaseTemplate = ({
|
|
componentName,
|
|
commandName,
|
|
numArgs,
|
|
convertArgs,
|
|
commandCall,
|
|
}) =>
|
|
`
|
|
if ([commandName isEqualToString:@"${commandName}"]) {
|
|
#if RCT_DEBUG
|
|
if ([args count] != ${numArgs}) {
|
|
RCTLogError(@"%@ command %@ received %d arguments, expected %d.", @"${componentName}", commandName, (int)[args count], ${numArgs});
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
${convertArgs}
|
|
|
|
${commandCall}
|
|
return;
|
|
}
|
|
`.trim();
|
|
const CommandHandlerTemplate = ({componentName, ifCases}) =>
|
|
`
|
|
RCT_EXTERN inline void RCT${componentName}HandleCommand(
|
|
id<RCT${componentName}ViewProtocol> componentView,
|
|
NSString const *commandName,
|
|
NSArray const *args)
|
|
{
|
|
${ifCases}
|
|
|
|
#if RCT_DEBUG
|
|
RCTLogError(@"%@ received command %@, which is not a supported command.", @"${componentName}", commandName);
|
|
#endif
|
|
}
|
|
`.trim();
|
|
const FileTemplate = ({componentContent}) =>
|
|
`
|
|
/**
|
|
* 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: GenerateComponentHObjCpp.js
|
|
*/
|
|
|
|
#import <Foundation/Foundation.h>
|
|
#import <React/RCTDefines.h>
|
|
#import <React/RCTLog.h>
|
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
${componentContent}
|
|
|
|
NS_ASSUME_NONNULL_END
|
|
`.trim();
|
|
function getObjCParamType(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 'BOOL';
|
|
case 'DoubleTypeAnnotation':
|
|
return 'double';
|
|
case 'FloatTypeAnnotation':
|
|
return 'float';
|
|
case 'Int32TypeAnnotation':
|
|
return 'NSInteger';
|
|
case 'StringTypeAnnotation':
|
|
return 'NSString *';
|
|
default:
|
|
typeAnnotation.type;
|
|
throw new Error('Received invalid param type annotation');
|
|
}
|
|
}
|
|
function getObjCExpectedKindParamType(param) {
|
|
const typeAnnotation = param.typeAnnotation;
|
|
switch (typeAnnotation.type) {
|
|
case 'ReservedTypeAnnotation':
|
|
switch (typeAnnotation.name) {
|
|
case 'RootTag':
|
|
return '[NSNumber class]';
|
|
default:
|
|
typeAnnotation.name;
|
|
throw new Error(`Receieved invalid type: ${typeAnnotation.name}`);
|
|
}
|
|
case 'BooleanTypeAnnotation':
|
|
return '[NSNumber class]';
|
|
case 'DoubleTypeAnnotation':
|
|
return '[NSNumber class]';
|
|
case 'FloatTypeAnnotation':
|
|
return '[NSNumber class]';
|
|
case 'Int32TypeAnnotation':
|
|
return '[NSNumber class]';
|
|
case 'StringTypeAnnotation':
|
|
return '[NSString class]';
|
|
default:
|
|
typeAnnotation.type;
|
|
throw new Error('Received invalid param type annotation');
|
|
}
|
|
}
|
|
function getReadableExpectedKindParamType(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 'number';
|
|
case 'StringTypeAnnotation':
|
|
return 'string';
|
|
default:
|
|
typeAnnotation.type;
|
|
throw new Error('Received invalid param type annotation');
|
|
}
|
|
}
|
|
function getObjCRightHandAssignmentParamType(param, index) {
|
|
const typeAnnotation = param.typeAnnotation;
|
|
switch (typeAnnotation.type) {
|
|
case 'ReservedTypeAnnotation':
|
|
switch (typeAnnotation.name) {
|
|
case 'RootTag':
|
|
return `[(NSNumber *)arg${index} doubleValue]`;
|
|
default:
|
|
typeAnnotation.name;
|
|
throw new Error(`Receieved invalid type: ${typeAnnotation.name}`);
|
|
}
|
|
case 'BooleanTypeAnnotation':
|
|
return `[(NSNumber *)arg${index} boolValue]`;
|
|
case 'DoubleTypeAnnotation':
|
|
return `[(NSNumber *)arg${index} doubleValue]`;
|
|
case 'FloatTypeAnnotation':
|
|
return `[(NSNumber *)arg${index} floatValue]`;
|
|
case 'Int32TypeAnnotation':
|
|
return `[(NSNumber *)arg${index} intValue]`;
|
|
case 'StringTypeAnnotation':
|
|
return `(NSString *)arg${index}`;
|
|
default:
|
|
typeAnnotation.type;
|
|
throw new Error('Received invalid param type annotation');
|
|
}
|
|
}
|
|
function generateProtocol(component, componentName) {
|
|
const methods = component.commands
|
|
.map(command => {
|
|
const params = command.typeAnnotation.params;
|
|
const paramString =
|
|
params.length === 0
|
|
? ''
|
|
: params
|
|
.map((param, index) => {
|
|
const objCType = getObjCParamType(param);
|
|
return `${index === 0 ? '' : param.name}:(${objCType})${
|
|
param.name
|
|
}`;
|
|
})
|
|
.join(' ');
|
|
return `- (void)${command.name}${paramString};`;
|
|
})
|
|
.join('\n')
|
|
.trim();
|
|
return ProtocolTemplate({
|
|
componentName,
|
|
methods,
|
|
});
|
|
}
|
|
function generateConvertAndValidateParam(param, index, componentName) {
|
|
const leftSideType = getObjCParamType(param);
|
|
const expectedKind = getObjCExpectedKindParamType(param);
|
|
const expectedKindString = getReadableExpectedKindParamType(param);
|
|
const argConversion = `${leftSideType} ${
|
|
param.name
|
|
} = ${getObjCRightHandAssignmentParamType(param, index)};`;
|
|
return CommandHandlerIfCaseConvertArgTemplate({
|
|
componentName,
|
|
argConversion,
|
|
argNumber: index,
|
|
argNumberString: getOrdinalNumber(index + 1),
|
|
expectedKind,
|
|
expectedKindString,
|
|
});
|
|
}
|
|
function generateCommandIfCase(command, componentName) {
|
|
const params = command.typeAnnotation.params;
|
|
const convertArgs = params
|
|
.map((param, index) =>
|
|
generateConvertAndValidateParam(param, index, componentName),
|
|
)
|
|
.join('\n\n')
|
|
.trim();
|
|
const commandCallArgs =
|
|
params.length === 0
|
|
? ''
|
|
: params
|
|
.map((param, index) => {
|
|
return `${index === 0 ? '' : param.name}:${param.name}`;
|
|
})
|
|
.join(' ');
|
|
const commandCall = `[componentView ${command.name}${commandCallArgs}];`;
|
|
return CommandHandlerIfCaseTemplate({
|
|
componentName,
|
|
commandName: command.name,
|
|
numArgs: params.length,
|
|
convertArgs,
|
|
commandCall,
|
|
});
|
|
}
|
|
function generateCommandHandler(component, componentName) {
|
|
if (component.commands.length === 0) {
|
|
return null;
|
|
}
|
|
const ifCases = component.commands
|
|
.map(command => generateCommandIfCase(command, componentName))
|
|
.join('\n\n');
|
|
return CommandHandlerTemplate({
|
|
componentName,
|
|
ifCases,
|
|
});
|
|
}
|
|
module.exports = {
|
|
generate(libraryName, schema, packageName, assumeNonnull = false) {
|
|
const fileName = 'RCTComponentViewHelpers.h';
|
|
const componentContent = Object.keys(schema.modules)
|
|
.map(moduleName => {
|
|
const module = schema.modules[moduleName];
|
|
if (module.type !== 'Component') {
|
|
return;
|
|
}
|
|
const components = module.components;
|
|
// No components in this module
|
|
if (components == null) {
|
|
return null;
|
|
}
|
|
return Object.keys(components)
|
|
.filter(componentName => {
|
|
const component = components[componentName];
|
|
return !(
|
|
component.excludedPlatforms &&
|
|
component.excludedPlatforms.includes('iOS')
|
|
);
|
|
})
|
|
.map(componentName => {
|
|
return [
|
|
generateProtocol(components[componentName], componentName),
|
|
generateCommandHandler(components[componentName], componentName),
|
|
]
|
|
.join('\n\n')
|
|
.trim();
|
|
})
|
|
.join('\n\n');
|
|
})
|
|
.filter(Boolean)
|
|
.join('\n\n');
|
|
const replacedTemplate = FileTemplate({
|
|
componentContent,
|
|
});
|
|
return new Map([[fileName, replacedTemplate]]);
|
|
},
|
|
};
|