/** * 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('./CppHelpers.js'), generateEventStructName = _require.generateEventStructName; const FileTemplate = ({events, libraryName}) => ` /** * 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: GenerateEventEmitterCpp.js */ #include namespace facebook { namespace react { ${events} } // namespace react } // namespace facebook `; const ComponentTemplate = ({ className, eventName, structName, dispatchEventName, implementation, }) => { const capture = implementation.includes('event') ? 'event=std::move(event)' : ''; return ` void ${className}EventEmitter::${eventName}(${structName} event) const { dispatchEvent("${dispatchEventName}", [${capture}](jsi::Runtime &runtime) { ${implementation} }); } `.trim(); }; const BasicComponentTemplate = ({className, eventName, dispatchEventName}) => ` void ${className}EventEmitter::${eventName}() const { dispatchEvent("${dispatchEventName}"); } `.trim(); function generateSetter(variableName, propertyName, propertyParts) { const trailingPeriod = propertyParts.length === 0 ? '' : '.'; const eventChain = `event.${propertyParts.join( '.', )}${trailingPeriod}${propertyName});`; return `${variableName}.setProperty(runtime, "${propertyName}", ${eventChain}`; } function generateEnumSetter(variableName, propertyName, propertyParts) { const trailingPeriod = propertyParts.length === 0 ? '' : '.'; const eventChain = `event.${propertyParts.join( '.', )}${trailingPeriod}${propertyName})`; return `${variableName}.setProperty(runtime, "${propertyName}", toString(${eventChain});`; } function generateSetters(parentPropertyName, properties, propertyParts) { const propSetters = properties .map(eventProperty => { const typeAnnotation = eventProperty.typeAnnotation; switch (typeAnnotation.type) { case 'BooleanTypeAnnotation': return generateSetter( parentPropertyName, eventProperty.name, propertyParts, ); case 'StringTypeAnnotation': return generateSetter( parentPropertyName, eventProperty.name, propertyParts, ); case 'Int32TypeAnnotation': return generateSetter( parentPropertyName, eventProperty.name, propertyParts, ); case 'DoubleTypeAnnotation': return generateSetter( parentPropertyName, eventProperty.name, propertyParts, ); case 'FloatTypeAnnotation': return generateSetter( parentPropertyName, eventProperty.name, propertyParts, ); case 'StringEnumTypeAnnotation': return generateEnumSetter( parentPropertyName, eventProperty.name, propertyParts, ); case 'ObjectTypeAnnotation': const propertyName = eventProperty.name; return ` { auto ${propertyName} = jsi::Object(runtime); ${generateSetters( propertyName, typeAnnotation.properties, propertyParts.concat([propertyName]), )} ${parentPropertyName}.setProperty(runtime, "${propertyName}", ${propertyName}); } `.trim(); default: typeAnnotation.type; throw new Error('Received invalid event property type'); } }) .join('\n'); return propSetters; } function generateEvent(componentName, event) { // This is a gross hack necessary because native code is sending // events named things like topChange to JS which is then converted back to // call the onChange prop. We should be consistent throughout the system. // In order to migrate to this new system we have to support the current // naming scheme. We should delete this once we are able to control this name // throughout the system. const dispatchEventName = `${event.name[2].toLowerCase()}${event.name.slice( 3, )}`; if (event.typeAnnotation.argument) { const implementation = ` auto payload = jsi::Object(runtime); ${generateSetters('payload', event.typeAnnotation.argument.properties, [])} return payload; `.trim(); if (!event.name.startsWith('on')) { throw new Error('Expected the event name to start with `on`'); } return ComponentTemplate({ className: componentName, eventName: event.name, dispatchEventName, structName: generateEventStructName([event.name]), implementation, }); } return BasicComponentTemplate({ className: componentName, eventName: event.name, dispatchEventName, }); } module.exports = { generate(libraryName, schema, packageName, assumeNonnull = false) { const moduleComponents = 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 components; }) .filter(Boolean) .reduce((acc, components) => Object.assign(acc, components), {}); const fileName = 'EventEmitters.cpp'; const componentEmitters = Object.keys(moduleComponents) .map(componentName => { const component = moduleComponents[componentName]; return component.events .map(event => { return generateEvent(componentName, event); }) .join('\n'); }) .join('\n'); const replacedTemplate = FileTemplate({ libraryName, events: componentEmitters, }); return new Map([[fileName, replacedTemplate]]); }, };