mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Summary: For now, separate the definition of `modules` generator per platform to avoid file output collision. Additionally: * For Android, produce files under java/ (plus nested subdirs based on packageName) and jni/ (for C++ files) - JavaPoet version already does it * Allow configuring packageName for Android - JavaPoet version has this * Avoid tmp directory dance in the CLI script, given the proper modules separation Changelog: [Internal] Reviewed By: hramos Differential Revision: D24410864 fbshipit-source-id: 9bd6bc1d65bec037bfca32ec478f3af50d72e927
234 lines
6.3 KiB
JavaScript
234 lines
6.3 KiB
JavaScript
/**
|
|
* 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 {
|
|
SchemaType,
|
|
NativeModulePropertySchema,
|
|
NativeModuleMethodParamSchema,
|
|
NativeModuleFunctionTypeAnnotation,
|
|
NativeModuleParamTypeAnnotation,
|
|
} from '../../CodegenSchema';
|
|
|
|
import type {AliasResolver} from './Utils';
|
|
const {createAliasResolver, getModules} = require('./Utils');
|
|
const {unwrapNullable} = require('../../parsers/flow/modules/utils');
|
|
|
|
type FilesOutput = Map<string, string>;
|
|
|
|
const HostFunctionTemplate = ({
|
|
hasteModuleName,
|
|
methodName,
|
|
isVoid,
|
|
args,
|
|
}: $ReadOnly<{
|
|
hasteModuleName: string,
|
|
methodName: string,
|
|
isVoid: boolean,
|
|
args: Array<string>,
|
|
}>) => {
|
|
const methodCallArgs = ['rt', ...args].join(', ');
|
|
const methodCall = `static_cast<${hasteModuleName}CxxSpecJSI *>(&turboModule)->${methodName}(${methodCallArgs});`;
|
|
|
|
return `static jsi::Value __hostFunction_${hasteModuleName}CxxSpecJSI_${methodName}(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {${
|
|
isVoid ? `\n ${methodCall}` : ''
|
|
}
|
|
return ${isVoid ? 'jsi::Value::undefined();' : methodCall}
|
|
}`;
|
|
};
|
|
|
|
const ModuleTemplate = ({
|
|
hasteModuleName,
|
|
hostFunctions,
|
|
moduleName,
|
|
methods,
|
|
}: $ReadOnly<{|
|
|
hasteModuleName: string,
|
|
hostFunctions: $ReadOnlyArray<string>,
|
|
moduleName: string,
|
|
methods: $ReadOnlyArray<
|
|
$ReadOnly<{|methodName: string, paramCount: number|}>,
|
|
>,
|
|
|}>) => {
|
|
return `${hostFunctions.join('\n')}
|
|
|
|
${hasteModuleName}CxxSpecJSI::${hasteModuleName}CxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker)
|
|
: TurboModule("${moduleName}", jsInvoker) {
|
|
${methods
|
|
.map(({methodName, paramCount}) => {
|
|
return ` methodMap_["${methodName}"] = MethodMetadata {${paramCount}, __hostFunction_${hasteModuleName}CxxSpecJSI_${methodName}};`;
|
|
})
|
|
.join('\n')}
|
|
}`;
|
|
};
|
|
|
|
const FileTemplate = ({
|
|
libraryName,
|
|
modules,
|
|
}: $ReadOnly<{
|
|
libraryName: string,
|
|
modules: string,
|
|
}>) => {
|
|
return `/**
|
|
* ${'C'}opyright (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.
|
|
*
|
|
* ${'@'}generated by codegen project: GenerateModuleH.js
|
|
*/
|
|
|
|
#include <react/modules/${libraryName}/NativeModules.h>
|
|
|
|
namespace facebook {
|
|
namespace react {
|
|
|
|
${modules}
|
|
|
|
|
|
} // namespace react
|
|
} // namespace facebook
|
|
`;
|
|
};
|
|
|
|
function serializeArg(
|
|
arg: NativeModuleMethodParamSchema,
|
|
index: number,
|
|
resolveAlias: AliasResolver,
|
|
): string {
|
|
function wrap(suffix) {
|
|
return `args[${index}]${suffix}`;
|
|
}
|
|
const {typeAnnotation: nullableTypeAnnotation} = arg;
|
|
const [typeAnnotation] = unwrapNullable<NativeModuleParamTypeAnnotation>(
|
|
nullableTypeAnnotation,
|
|
);
|
|
|
|
let realTypeAnnotation = typeAnnotation;
|
|
if (realTypeAnnotation.type === 'TypeAliasTypeAnnotation') {
|
|
realTypeAnnotation = resolveAlias(realTypeAnnotation.name);
|
|
}
|
|
|
|
switch (realTypeAnnotation.type) {
|
|
case 'ReservedFunctionValueTypeAnnotation':
|
|
switch (realTypeAnnotation.name) {
|
|
case 'RootTag':
|
|
return wrap('.getNumber()');
|
|
default:
|
|
(realTypeAnnotation.name: empty);
|
|
throw new Error(
|
|
`Unknown prop type for "${arg.name}, found: ${realTypeAnnotation.name}"`,
|
|
);
|
|
}
|
|
case 'StringTypeAnnotation':
|
|
return wrap('.getString(rt)');
|
|
case 'BooleanTypeAnnotation':
|
|
return wrap('.getBool()');
|
|
case 'NumberTypeAnnotation':
|
|
return wrap('.getNumber()');
|
|
case 'FloatTypeAnnotation':
|
|
return wrap('.getNumber()');
|
|
case 'DoubleTypeAnnotation':
|
|
return wrap('.getNumber()');
|
|
case 'Int32TypeAnnotation':
|
|
return wrap('.getNumber()');
|
|
case 'ArrayTypeAnnotation':
|
|
return wrap('.getObject(rt).getArray(rt)');
|
|
case 'FunctionTypeAnnotation':
|
|
return `std::move(${wrap('.getObject(rt).getFunction(rt)')})`;
|
|
case 'GenericObjectTypeAnnotation':
|
|
return wrap('.getObject(rt)');
|
|
case 'ObjectTypeAnnotation':
|
|
return wrap('.getObject(rt)');
|
|
default:
|
|
(realTypeAnnotation.type: empty);
|
|
throw new Error(
|
|
`Unknown prop type for "${arg.name}, found: ${realTypeAnnotation.type}"`,
|
|
);
|
|
}
|
|
}
|
|
|
|
function serializePropertyIntoHostFunction(
|
|
hasteModuleName: string,
|
|
property: NativeModulePropertySchema,
|
|
resolveAlias: AliasResolver,
|
|
): string {
|
|
const [
|
|
propertyTypeAnnotation,
|
|
] = unwrapNullable<NativeModuleFunctionTypeAnnotation>(
|
|
property.typeAnnotation,
|
|
);
|
|
const isVoid =
|
|
propertyTypeAnnotation.returnTypeAnnotation.type === 'VoidTypeAnnotation';
|
|
|
|
return HostFunctionTemplate({
|
|
hasteModuleName,
|
|
methodName: property.name,
|
|
isVoid,
|
|
args: propertyTypeAnnotation.params.map((p, i) =>
|
|
serializeArg(p, i, resolveAlias),
|
|
),
|
|
});
|
|
}
|
|
|
|
module.exports = {
|
|
generate(
|
|
libraryName: string,
|
|
schema: SchemaType,
|
|
moduleSpecName: string,
|
|
packageName?: string,
|
|
): FilesOutput {
|
|
const nativeModules = getModules(schema);
|
|
|
|
const modules = Object.keys(nativeModules)
|
|
.map((hasteModuleName: string) => {
|
|
const nativeModule = nativeModules[hasteModuleName];
|
|
const {
|
|
aliases,
|
|
spec: {properties},
|
|
moduleNames,
|
|
} = nativeModule;
|
|
const resolveAlias = createAliasResolver(aliases);
|
|
const hostFunctions = properties.map(property =>
|
|
serializePropertyIntoHostFunction(
|
|
hasteModuleName,
|
|
property,
|
|
resolveAlias,
|
|
),
|
|
);
|
|
|
|
return ModuleTemplate({
|
|
hasteModuleName,
|
|
hostFunctions,
|
|
// TODO: What happens when there are more than one NativeModule requires?
|
|
moduleName: moduleNames[0],
|
|
methods: properties.map(
|
|
({name: propertyName, typeAnnotation: nullableTypeAnnotation}) => {
|
|
const [{params}] = unwrapNullable(nullableTypeAnnotation);
|
|
return {
|
|
methodName: propertyName,
|
|
paramCount: params.length,
|
|
};
|
|
},
|
|
),
|
|
});
|
|
})
|
|
.join('\n');
|
|
|
|
const fileName = 'NativeModules.cpp';
|
|
const replacedTemplate = FileTemplate({
|
|
modules,
|
|
libraryName,
|
|
});
|
|
return new Map([[fileName, replacedTemplate]]);
|
|
},
|
|
};
|