ts-typed-events
Version:
Zero dependency strongly typed event emitters for TypeScript
213 lines (212 loc) • 8.1 kB
JavaScript
;
/**
* This is a very simple strongly typed event emitter class, see README.md
* for more details.
*/
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
exports.__esModule = true;
exports.createEventEmitter = exports.Event = exports.createEmitter = exports.SealedEvent = exports.BaseEvent = void 0;
/**
* A ts-typed-events event to register callbacks on to be invoked when they are
* emitted.
*/
var BaseEvent = /** @class */ (function () {
/**
* Marked as protected to discourage creation outside of
* createEventEmitter().
*/
function BaseEvent() {
/** All the current listeners for this event. */
this.listeners = [];
// pass
}
/**
* Attaches a callback to trigger on all emits for this event.
*
* @param callback - The callback to invoke on all emits.
*/
BaseEvent.prototype.on = function (callback) {
this.listeners.push({
once: false,
callback: callback
});
};
/**
* Attaches a callback to trigger on only the first emit for this event.
*
* This version either takes a callback or returns a promise.
*
* @param callback - Optional callback, if specified invokes the callback
* only once when the event is triggered, then removes it.
* Otherwise returns a promise that resolves with the value the next time
* this event is triggered.
* @returns Nothing if a callback is passed, otherwise a Promise that
* should resolve once this Event emits.
*/
BaseEvent.prototype.once = function (callback) {
var _this = this;
if (!callback) {
// then they want us to return the promise
var promise = new Promise(function (resolve) {
// this will invoke the version that has a callback,
// so resolve can be used as the callback
_this.once(resolve);
});
// attach the promise we just made to the listener (it was pushed
// on the end via this.once() above)
this.listeners[this.listeners.length - 1].promise = promise;
return promise;
}
// else we were sent a normal callback, so attach it
this.listeners.push({
once: true,
callback: callback
});
};
/**
* Removes a callback from this event (regardless of once vs on).
*
* Returns true if a callback was removed, false otherwise.
*
* @param listener - The callback to remove.
* @returns True if a callback was removed, false otherwise.
*/
BaseEvent.prototype.off = function (listener) {
var originalLength = this.listeners.length;
// remove all listeners that have the same callback as this one
this.listeners = this.listeners.filter(function (l) {
return listener !== l.callback && (!l.promise || listener !== l.promise);
});
return this.listeners.length !== originalLength;
};
/**
* Removes ALL callbacks from this event, regardless of once vs on.
*
* Returns the number of listeners removed.
*
* @returns The number of listeners removed.
*/
BaseEvent.prototype.offAll = function () {
var originalLength = this.listeners.length;
this.listeners.length = 0; // empty our listener array
return originalLength;
};
return BaseEvent;
}());
exports.BaseEvent = BaseEvent;
/**
* A ts-typed-events event to register callbacks on to be invoked when they are
* emitted.
*
* This Event class signifies the event is "sealed" from the outside and cannot
* be emitted through itself. The emitter function is separated.
*/
var SealedEvent = /** @class */ (function (_super) {
__extends(SealedEvent, _super);
function SealedEvent() {
return _super !== null && _super.apply(this, arguments) || this;
}
return SealedEvent;
}(BaseEvent));
exports.SealedEvent = SealedEvent;
/** @internal */
function createEmitterWithBaseEvent(event) {
// Hack-y, we are reaching into to grab the listeners
// realistically, this would be a friend style function
var publicListenersEvent = event;
/**
* The emitter function for the event.
*
* @param emitting - Whatever is being emitted.
* @returns True if any listeners were emitted to, false otherwise.
*/
function emit(emitting) {
var listeners = publicListenersEvent.listeners;
var hadListeners = listeners.length > 0;
for (var _i = 0, listeners_1 = listeners; _i < listeners_1.length; _i++) {
var listener = listeners_1[_i];
listener.callback(emitting);
}
// remove all listeners that only wanted to listen once
publicListenersEvent.listeners = listeners.filter(function (_a) {
var once = _a.once;
return !once;
});
return hadListeners;
}
// Hack: Need to find a better way to convince TS this function will have
// the correct keys on these circular functions.
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access */
var func = emit;
func.event = event;
func.emit = emit;
var emitter = func;
/* eslint-enable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access */
return emitter;
}
/**
* Creates and returns an emitter for a SealedEvent, with the event keyed off
* the emitter via `.event`.
*
* @returns An emitter function that will emit to the `.event` SealedEvent.
*/
function createEmitter() {
// This is a hack-y way to create a new class instance that doesn't want you
// to.
var EventClass = SealedEvent;
return createEmitterWithBaseEvent(new EventClass());
}
exports.createEmitter = createEmitter;
/**
* A specialized Event that holds a reference to its own emit function.
* This allows any code with access to the Event to also trigger emits.
*/
var Event = /** @class */ (function (_super) {
__extends(Event, _super);
/**
* Creates a new Event, with its emit accessible as a member function.
*/
function Event() {
var _this = _super.call(this) || this;
/**
* Emits a value to all the listeners, triggering their callbacks.
* Returns true if the event had listeners emitted to,
* false otherwise.
* Because this exists on the event, any code with access to this event
* can trigger the callback for all listeners.
*
* @param emitting - If the Event has a type, this is the data of that type
* to emit to all listeners. If no type (undefined) this argument should
* be omitted.
* @returns True if the event had listeners emitted to, false otherwise.
*/
_this.emit = createEmitterWithBaseEvent(_this);
return _this;
}
return Event;
}(BaseEvent));
exports.Event = Event;
/**
* Creates and returns an emitter for an Event, with the event keyed off
* the emitter via `.event`.
* **Note**: The `event` here is will have a member function `.emit` that emits
* to the same function as the emitter returned here.
*
* @returns An emitter function that will emit to the `.event` Event.
*/
function createEventEmitter() {
return createEmitterWithBaseEvent(new Event());
}
exports.createEventEmitter = createEventEmitter;