Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 8 additions & 136 deletions Libraries/vendor/emitter/EventEmitter.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,139 +8,11 @@
* @format
*/

export interface EventSubscription {
remove(): void;
}

export interface IEventEmitter<TEventToArgsMap: {...}> {
addListener<TEvent: $Keys<TEventToArgsMap>>(
eventType: TEvent,
listener: (...args: $ElementType<TEventToArgsMap, TEvent>) => mixed,
context?: mixed,
): EventSubscription;

emit<TEvent: $Keys<TEventToArgsMap>>(
eventType: TEvent,
...args: $ElementType<TEventToArgsMap, TEvent>
): void;

removeAllListeners<TEvent: $Keys<TEventToArgsMap>>(eventType?: ?TEvent): void;

listenerCount<TEvent: $Keys<TEventToArgsMap>>(eventType: TEvent): number;
}

interface Registration<TArgs> {
+context: mixed;
+listener: (...args: TArgs) => mixed;
+remove: () => void;
}

type Registry<TEventToArgsMap: {...}> = $ObjMap<
TEventToArgsMap,
<TArgs>(TArgs) => Set<Registration<TArgs>>,
>;

/**
* EventEmitter manages listeners and publishes events to them.
*
* EventEmitter accepts a single type parameter that defines the valid events
* and associated listener argument(s).
*
* @example
*
* const emitter = new EventEmitter<{
* success: [number, string],
* error: [Error],
* }>();
*
* emitter.on('success', (statusCode, responseText) => {...});
* emitter.emit('success', 200, '...');
*
* emitter.on('error', error => {...});
* emitter.emit('error', new Error('Resource not found'));
*
*/
export default class EventEmitter<TEventToArgsMap: {...}>
implements IEventEmitter<TEventToArgsMap>
{
_registry: Registry<TEventToArgsMap> = {};

/**
* Registers a listener that is called when the supplied event is emitted.
* Returns a subscription that has a `remove` method to undo registration.
*/
addListener<TEvent: $Keys<TEventToArgsMap>>(
eventType: TEvent,
listener: (...args: $ElementType<TEventToArgsMap, TEvent>) => mixed,
context: mixed,
): EventSubscription {
const registrations = allocate(this._registry, eventType);
const registration: Registration<$ElementType<TEventToArgsMap, TEvent>> = {
context,
listener,
remove(): void {
registrations.delete(registration);
},
};
registrations.add(registration);
return registration;
}

/**
* Emits the supplied event. Additional arguments supplied to `emit` will be
* passed through to each of the registered listeners.
*
* If a listener modifies the listeners registered for the same event, those
* changes will not be reflected in the current invocation of `emit`.
*/
emit<TEvent: $Keys<TEventToArgsMap>>(
eventType: TEvent,
...args: $ElementType<TEventToArgsMap, TEvent>
): void {
const registrations: ?Set<
Registration<$ElementType<TEventToArgsMap, TEvent>>,
> = this._registry[eventType];
if (registrations != null) {
for (const registration of [...registrations]) {
registration.listener.apply(registration.context, args);
}
}
}

/**
* Removes all registered listeners.
*/
removeAllListeners<TEvent: $Keys<TEventToArgsMap>>(
eventType?: ?TEvent,
): void {
if (eventType == null) {
this._registry = {};
} else {
delete this._registry[eventType];
}
}

/**
* Returns the number of registered listeners for the supplied event.
*/
listenerCount<TEvent: $Keys<TEventToArgsMap>>(eventType: TEvent): number {
const registrations: ?Set<Registration<mixed>> = this._registry[eventType];
return registrations == null ? 0 : registrations.size;
}
}

function allocate<
TEventToArgsMap: {...},
TEvent: $Keys<TEventToArgsMap>,
TEventArgs: $ElementType<TEventToArgsMap, TEvent>,
>(
registry: Registry<TEventToArgsMap>,
eventType: TEvent,
): Set<Registration<TEventArgs>> {
let registrations: ?Set<Registration<TEventArgs>> = registry[eventType];
if (registrations == null) {
registrations = new Set();
registry[eventType] = registrations;
}
return registrations;
}
import type {
EventSubscription,
IEventEmitter,
} from '@react-native/event-emitter';
import EventEmitter from '@react-native/event-emitter';

export type {EventSubscription, IEventEmitter};
export default EventEmitter;
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@
"@react-native-community/cli-platform-android": "^9.0.0-alpha.10",
"@react-native-community/cli-platform-ios": "^9.0.0-alpha.10",
"@react-native/assets": "1.0.0",
"@react-native/event-emitter": "^0.71.0",
"@react-native/normalize-color": "2.0.0",
"@react-native/polyfills": "2.0.0",
"abort-controller": "^3.0.0",
Expand Down Expand Up @@ -139,7 +140,8 @@
"flow-bin": "^0.184.0",
"hermes-eslint": "0.8.0",
"react": "18.2.0",
"react-test-renderer": "^18.2.0"
"react-test-renderer": "^18.2.0",
"typescript": "^4.7.4"
},
"codegenConfig": {
"libraries": [
Expand Down
3 changes: 3 additions & 0 deletions packages/event-emitter/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-flow"]
}
10 changes: 10 additions & 0 deletions packages/event-emitter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# @react-native/event-emitter

`@react-native/event-emitter` is an event library used by React Native that is
published as an independent package so it can also be used by applications.

Consult the included type definitions for usage instructions.

## License

`idx` is [MIT licensed](./LICENSE).
83 changes: 83 additions & 0 deletions packages/event-emitter/lib/EventEmitter.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/**
* 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
*/

export interface EventSubscription {
remove(): void;
}

export interface IEventEmitter<TEventToArgsMap extends {[eventType: string]: unknown[]}> {
addListener<TEvent extends keyof TEventToArgsMap>(
eventType: TEvent,
listener: (...args: TEventToArgsMap[TEvent]) => void,
context?: unknown,
): EventSubscription;

emit<TEvent extends keyof TEventToArgsMap>(
eventType: TEvent,
...args: TEventToArgsMap[TEvent]
): void;

removeAllListeners<TEvent extends keyof TEventToArgsMap>(eventType?: TEvent | null): void;

listenerCount<TEvent extends keyof TEventToArgsMap>(eventType: TEvent): number;
}

/**
* EventEmitter manages listeners and publishes events to them.
*
* EventEmitter accepts a single type parameter that defines the valid events
* and associated listener argument(s).
*
* @example
*
* const emitter = new EventEmitter<{
* success: [number, string],
* error: [Error],
* }>();
*
* emitter.on('success', (statusCode, responseText) => {...});
* emitter.emit('success', 200, '...');
*
* emitter.on('error', error => {...});
* emitter.emit('error', new Error('Resource not found'));
*
*/
export default class EventEmitter<TEventToArgsMap extends {[eventType: string]: unknown[]}> implements IEventEmitter<TEventToArgsMap> {
/**
* Registers a listener that is called when the supplied event is emitted.
* Returns a subscription that has a `remove` method to undo registration.
*/
addListener<TEvent extends keyof TEventToArgsMap>(
eventType: TEvent,
listener: (...args: TEventToArgsMap[TEvent]) => void,
context?: unknown,
): EventSubscription;

/**
* Emits the supplied event. Additional arguments supplied to `emit` will be
* passed through to each of the registered listeners.
*
* If a listener modifies the listeners registered for the same event, those
* changes will not be reflected in the current invocation of `emit`.
*/
emit<TEvent extends keyof TEventToArgsMap>(
eventType: TEvent,
...args: TEventToArgsMap[TEvent]
): void;

/**
* Removes all registered listeners.
*/
removeAllListeners<TEvent extends keyof TEventToArgsMap>(eventType?: TEvent | null): void;

/**
* Returns the number of registered listeners for the supplied event.
*/
listenerCount<TEvent extends keyof TEventToArgsMap>(eventType: TEvent): number;
}
98 changes: 98 additions & 0 deletions packages/event-emitter/lib/EventEmitter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/**
* 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
*/

/**
* EventEmitter manages listeners and publishes events to them.
*
* EventEmitter accepts a single type parameter that defines the valid events
* and associated listener argument(s).
*
* @example
*
* const emitter = new EventEmitter<{
* success: [number, string],
* error: [Error],
* }>();
*
* emitter.on('success', (statusCode, responseText) => {...});
* emitter.emit('success', 200, '...');
*
* emitter.on('error', error => {...});
* emitter.emit('error', new Error('Resource not found'));
*
*/
export default class EventEmitter {
_registry = {};
/**
* Registers a listener that is called when the supplied event is emitted.
* Returns a subscription that has a `remove` method to undo registration.
*/

addListener(eventType, listener, context) {
const registrations = allocate(this._registry, eventType);
const registration = {
context,
listener,

remove() {
registrations.delete(registration);
},
};
registrations.add(registration);
return registration;
}
/**
* Emits the supplied event. Additional arguments supplied to `emit` will be
* passed through to each of the registered listeners.
*
* If a listener modifies the listeners registered for the same event, those
* changes will not be reflected in the current invocation of `emit`.
*/

emit(eventType, ...args) {
const registrations = this._registry[eventType];

if (registrations != null) {
for (const registration of [...registrations]) {
registration.listener.apply(registration.context, args);
}
}
}
/**
* Removes all registered listeners.
*/

removeAllListeners(eventType) {
if (eventType == null) {
this._registry = {};
} else {
delete this._registry[eventType];
}
}
/**
* Returns the number of registered listeners for the supplied event.
*/

listenerCount(eventType) {
const registrations = this._registry[eventType];
return registrations == null ? 0 : registrations.size;
}
}

function allocate(registry, eventType) {
let registrations = registry[eventType];

if (registrations == null) {
registrations = new Set();
registry[eventType] = registrations;
}

return registrations;
}
Loading