mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Summary:
This diff allows generating native events without any arguments with an event like:
```
{
name: 'onEnd',
optional: true,
bubblingType: 'bubble',
typeAnnotation: {
type: 'EventTypeAnnotation',
// note: no argument key
},
},
```
See the snapshot updates in the diff for the native code that will be generated �
Reviewed By: shergin
Differential Revision: D15403791
fbshipit-source-id: 925a49bb477eebb234e181df681f0d6b1d4e8cf1
229 lines
5.6 KiB
JavaScript
229 lines
5.6 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-local
|
|
* @format
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
const nullthrows = require('nullthrows');
|
|
|
|
const {getCppTypeForAnnotation} = require('./CppHelpers.js');
|
|
const {generateStructName} = require('./EventEmitterHelpers.js');
|
|
|
|
import type {
|
|
ComponentShape,
|
|
EventTypeShape,
|
|
ObjectPropertyType,
|
|
SchemaType,
|
|
} from '../CodegenSchema';
|
|
|
|
// File path -> contents
|
|
type FilesOutput = Map<string, string>;
|
|
type StructsMap = Map<string, string>;
|
|
|
|
type ComponentCollection = $ReadOnly<{
|
|
[component: string]: ComponentShape,
|
|
}>;
|
|
|
|
const template = `
|
|
/**
|
|
* 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.
|
|
*/
|
|
#pragma once
|
|
|
|
#include <react/components/view/ViewEventEmitter.h>
|
|
|
|
namespace facebook {
|
|
namespace react {
|
|
|
|
::_COMPONENT_EMITTERS_::
|
|
|
|
} // namespace react
|
|
} // namespace facebook
|
|
`;
|
|
|
|
const componentTemplate = `
|
|
::_STRUCTS_::
|
|
|
|
class ::_CLASSNAME_::EventEmitter : public ViewEventEmitter {
|
|
public:
|
|
using ViewEventEmitter::ViewEventEmitter;
|
|
|
|
::_EVENTS_::
|
|
};
|
|
`.trim();
|
|
|
|
const structTemplate = `
|
|
struct ::_STRUCT_NAME_:: {
|
|
::_FIELDS_::
|
|
};
|
|
`.trim();
|
|
|
|
function getNativeTypeFromAnnotation(
|
|
componentName: string,
|
|
eventProperty: ObjectPropertyType,
|
|
nameParts: $ReadOnlyArray<string>,
|
|
): string {
|
|
const type = eventProperty.type;
|
|
|
|
switch (type) {
|
|
case 'BooleanTypeAnnotation':
|
|
case 'StringTypeAnnotation':
|
|
case 'Int32TypeAnnotation':
|
|
case 'FloatTypeAnnotation':
|
|
return getCppTypeForAnnotation(type);
|
|
case 'ObjectTypeAnnotation':
|
|
return generateStructName(
|
|
componentName,
|
|
nameParts.concat([eventProperty.name]),
|
|
);
|
|
default:
|
|
(type: empty);
|
|
throw new Error(`Receieved invalid event property type ${type}`);
|
|
}
|
|
}
|
|
|
|
function generateStruct(
|
|
structs: StructsMap,
|
|
componentName: string,
|
|
nameParts: $ReadOnlyArray<string>,
|
|
properties: $ReadOnlyArray<ObjectPropertyType>,
|
|
): void {
|
|
const structNameParts = nameParts;
|
|
const structName = generateStructName(componentName, structNameParts);
|
|
|
|
const fields = properties
|
|
.map(property => {
|
|
return `${getNativeTypeFromAnnotation(
|
|
componentName,
|
|
property,
|
|
structNameParts,
|
|
)} ${property.name};`;
|
|
})
|
|
.join('\n' + ' ');
|
|
|
|
properties.forEach((property: ObjectPropertyType) => {
|
|
const name = property.name;
|
|
switch (property.type) {
|
|
case 'BooleanTypeAnnotation':
|
|
case 'StringTypeAnnotation':
|
|
case 'Int32TypeAnnotation':
|
|
case 'FloatTypeAnnotation':
|
|
return;
|
|
case 'ObjectTypeAnnotation':
|
|
generateStruct(
|
|
structs,
|
|
componentName,
|
|
nameParts.concat([name]),
|
|
nullthrows(property.properties),
|
|
);
|
|
return;
|
|
default:
|
|
(property: empty);
|
|
throw new Error(
|
|
`Receieved invalid event property type ${property.type}`,
|
|
);
|
|
}
|
|
});
|
|
|
|
structs.set(
|
|
structName,
|
|
structTemplate
|
|
.replace('::_STRUCT_NAME_::', structName)
|
|
.replace('::_FIELDS_::', fields),
|
|
);
|
|
}
|
|
|
|
function generateStructs(componentName: string, component): string {
|
|
const structs: StructsMap = new Map();
|
|
|
|
component.events.forEach(event => {
|
|
if (event.typeAnnotation.argument) {
|
|
generateStruct(
|
|
structs,
|
|
componentName,
|
|
[event.name],
|
|
event.typeAnnotation.argument.properties,
|
|
);
|
|
}
|
|
});
|
|
|
|
return Array.from(structs.values()).join('\n\n');
|
|
}
|
|
|
|
function generateEvent(componentName: string, event: EventTypeShape): string {
|
|
if (event.typeAnnotation.argument) {
|
|
const structName = generateStructName(componentName, [event.name]);
|
|
|
|
return `void ${event.name}(${structName} value) const;`;
|
|
}
|
|
|
|
return `void ${event.name}() const;`;
|
|
}
|
|
function generateEvents(componentName: string, component): string {
|
|
return component.events
|
|
.map(event => generateEvent(componentName, event))
|
|
.join('\n\n' + ' ');
|
|
}
|
|
|
|
module.exports = {
|
|
generate(libraryName: string, schema: SchemaType): FilesOutput {
|
|
const moduleComponents: ComponentCollection = Object.keys(schema.modules)
|
|
.map(moduleName => {
|
|
const components = schema.modules[moduleName].components;
|
|
// No components in this module
|
|
if (components == null) {
|
|
return null;
|
|
}
|
|
|
|
return components;
|
|
})
|
|
.filter(Boolean)
|
|
.reduce((acc, components) => Object.assign(acc, components), {});
|
|
|
|
const moduleComponentsWithEvents = Object.keys(moduleComponents).filter(
|
|
componentName => moduleComponents[componentName].events.length > 0,
|
|
);
|
|
|
|
const fileName = 'EventEmitters.h';
|
|
|
|
const componentEmitters =
|
|
moduleComponentsWithEvents.length > 0
|
|
? Object.keys(moduleComponents)
|
|
.map(componentName => {
|
|
const component = moduleComponents[componentName];
|
|
|
|
const replacedTemplate = componentTemplate
|
|
.replace(/::_CLASSNAME_::/g, componentName)
|
|
.replace(
|
|
'::_STRUCTS_::',
|
|
generateStructs(componentName, component),
|
|
)
|
|
.replace(
|
|
'::_EVENTS_::',
|
|
generateEvents(componentName, component),
|
|
)
|
|
.trim();
|
|
|
|
return replacedTemplate;
|
|
})
|
|
.join('\n')
|
|
: '';
|
|
|
|
const replacedTemplate = template.replace(
|
|
/::_COMPONENT_EMITTERS_::/g,
|
|
componentEmitters,
|
|
);
|
|
|
|
return new Map([[fileName, replacedTemplate]]);
|
|
},
|
|
};
|