Skip to content

Commit

Permalink
Use mitt as the event emitter
Browse files Browse the repository at this point in the history
This replaces the outdated rollup polyfill[1]
by a 200 byte event emitter called mitt[2] that comes with
a wildcard event matching.

[1]: ionic-team/rollup-plugin-node-polyfills#8
[2]: https://github.com/developit/mitt
  • Loading branch information
OrKoN committed Nov 24, 2022
1 parent bebb0a8 commit d245c17
Show file tree
Hide file tree
Showing 10 changed files with 223 additions and 67 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"argparse": "^2.0.1",
"chai-exclude": "^2.1.0",
"debug": "^4.3.4",
"mitt": "3.0.0",
"puppeteer": "14.1.2",
"ts-node": "^10.8.0",
"tslib": "^2.4.0",
Expand Down Expand Up @@ -59,7 +60,6 @@
"prettier": "2.6.2",
"rimraf": "^3.0.2",
"rollup": "^2.74.1",
"rollup-plugin-node-polyfills": "^0.2.1",
"rollup-plugin-string": "^3.0.0",
"rollup-plugin-terser": "^7.0.2",
"sinon": "^14.0.0",
Expand Down
9 changes: 3 additions & 6 deletions src/bidiMapper/domains/context/browsingContextProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,13 @@ export class BrowsingContextProcessor {

this.#setTargetEventListeners(sessionCdpClient);

sessionCdpClient.on('event', async (method, params) => {
sessionCdpClient.on('*', async (method, params) => {
await this.#eventManager.registerEvent(
{
method: 'cdp.eventReceived',
params: {
cdpMethod: method,
cdpParams: params,
cdpParams: params || {},
cdpSession: sessionId,
},
},
Expand Down Expand Up @@ -299,10 +299,7 @@ export class BrowsingContextProcessor {
eventParams: Protocol.Target.DetachedFromTargetEvent
) => {
if (eventParams.targetId === commandParams.context) {
browserCdpClient.removeListener(
'Target.detachedFromTarget',
onContextDestroyed
);
browserCdpClient.off('Target.detachedFromTarget', onContextDestroyed);
resolve();
}
};
Expand Down
2 changes: 0 additions & 2 deletions src/bidiMapper/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
*/

import typescript from '@rollup/plugin-typescript';
import nodePolyfills from 'rollup-plugin-node-polyfills';
import json from '@rollup/plugin-json';
import {string} from 'rollup-plugin-string';
import {terser} from 'rollup-plugin-terser';
Expand All @@ -32,7 +31,6 @@ export default {
},
plugins: [
json(),
nodePolyfills(),
string({
include: 'src/bidiMapper/scripts/*.es',
}),
Expand Down
23 changes: 7 additions & 16 deletions src/bidiMapper/utils/bidiServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/

import {log, LogType} from '../../utils/log';
import {EventEmitter} from 'events';
import {EventEmitter} from '../../utils/EventEmitter';

import {ITransport} from '../../utils/transport';
import {Message} from '../domains/protocol/bidiProtocolTypes';
Expand All @@ -36,9 +36,9 @@ export interface IBidiServer {
close(): void;
}

interface BidiServerEvents {
type BidiServerEvents = {
message: Message.RawCommandRequest;
}
};

export class BiDiMessageEntry {
readonly #message: Message.OutgoingMessage;
Expand Down Expand Up @@ -74,19 +74,10 @@ export class BiDiMessageEntry {
}
}

export declare interface BidiServer {
on<U extends keyof BidiServerEvents>(
event: U,
listener: (params: BidiServerEvents[U]) => void
): this;

emit<U extends keyof BidiServerEvents>(
event: U,
params: BidiServerEvents[U]
): boolean;
}

export class BidiServer extends EventEmitter implements IBidiServer {
export class BidiServer
extends EventEmitter<BidiServerEvents>
implements IBidiServer
{
#messageQueue: ProcessingQueue<BiDiMessageEntry>;

constructor(private _transport: ITransport) {
Expand Down
6 changes: 3 additions & 3 deletions src/cdp/cdpClient.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ describe('CdpClient tests.', function () {

// Register event callbacks.
const genericCallback = sinon.fake();
cdpClient.on('event', genericCallback);
cdpClient.on('*', genericCallback);

const typedCallback = sinon.fake();
cdpClient.on('Target.attachedToTarget', typedCallback);
Expand All @@ -146,8 +146,8 @@ describe('CdpClient tests.', function () {
typedCallback.resetHistory();

// Unregister callbacks.
cdpClient.removeListener('event', genericCallback);
cdpClient.removeListener('Target.attachedToTarget', typedCallback);
cdpClient.off('*', genericCallback);
cdpClient.off('Target.attachedToTarget', typedCallback);

// Send another CDP event.
await mockCdpServer.emulateIncomingMessage({
Expand Down
40 changes: 6 additions & 34 deletions src/cdp/cdpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,44 +15,16 @@
* limitations under the License.
*/

import {EventEmitter} from 'events';
import {EventEmitter} from '../utils/EventEmitter';
import {CdpConnection} from './cdpConnection';

import type {ProtocolMapping} from 'devtools-protocol/types/protocol-mapping.js';

export interface CdpClient {
on<K extends keyof ProtocolMapping.Events>(
eventName: K,
handler: (...params: ProtocolMapping.Events[K]) => void
): EventEmitter;
on(
eventName: 'event',
handler: (method: keyof ProtocolMapping.Events, ...params: any) => void
): EventEmitter;
removeListener<K extends keyof ProtocolMapping.Events>(
eventName: K,
handler: (...params: ProtocolMapping.Events[K]) => void
): EventEmitter;
removeListener(
eventName: 'event',
handler: (method: keyof ProtocolMapping.Events, ...params: any) => void
): EventEmitter;
emit<K extends keyof ProtocolMapping.Events>(
eventName: K,
...args: ProtocolMapping.Events[K]
): void;
emit<K extends keyof ProtocolMapping.Events>(
eventName: 'event',
methodName: K,
...args: ProtocolMapping.Events[K]
): void;
sendCommand<T extends keyof ProtocolMapping.Commands>(
method: T,
...params: ProtocolMapping.Commands[T]['paramsType']
): Promise<ProtocolMapping.Commands[T]['returnType']>;
}
type Mapping = {
[Property in keyof ProtocolMapping.Events]: ProtocolMapping.Events[Property][0];
};

class CdpClientImpl extends EventEmitter implements CdpClient {
export class CdpClient extends EventEmitter<Mapping> {
constructor(
private _cdpConnection: CdpConnection,
private _sessionId: string | null
Expand Down Expand Up @@ -84,5 +56,5 @@ export function createClient(
cdpConnection: CdpConnection,
sessionId: string | null
): CdpClient {
return new CdpClientImpl(cdpConnection, sessionId);
return new CdpClient(cdpConnection, sessionId);
}
1 change: 0 additions & 1 deletion src/cdp/cdpConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ export class CdpConnection {
: this.#browserCdpClient;
if (client) {
client.emit(parsed.method, parsed.params || {});
client.emit('event', parsed.method, parsed.params || {});
}
}
};
Expand Down
5 changes: 1 addition & 4 deletions src/mapperServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,7 @@ export class MapperServer {
try {
const parsed = JSON.parse(payload);
if (parsed.launched) {
mapperCdpClient.removeListener(
'Runtime.bindingCalled',
onBindingCalled
);
mapperCdpClient.off('Runtime.bindingCalled', onBindingCalled);
resolve();
}
} catch (e) {
Expand Down
122 changes: 122 additions & 0 deletions src/utils/EventEmitter.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/**
* Copyright 2020 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {EventEmitter} from './EventEmitter';
import sinon from 'sinon';
import {expect} from 'chai';

describe('EventEmitter', () => {
type Events = {
foo: undefined;
bar: undefined;
};
let emitter: EventEmitter<Events>;

beforeEach(() => {
emitter = new EventEmitter();
});

describe('on', () => {
const onTests = (methodName: 'on'): void => {
it(`${methodName}: adds an event listener that is fired when the event is emitted`, () => {
const listener = sinon.spy();
emitter[methodName]('foo', listener);
emitter.emit('foo', undefined);
expect(listener.callCount).to.equal(1);
});

it(`${methodName} sends the event data to the handler`, () => {
const listener = sinon.spy();
const data = {};
emitter[methodName]('foo', listener);
emitter.emit('foo', data);
expect(listener.callCount).to.equal(1);
expect(listener.firstCall.args[0]!).to.equal(data);
});

it(`${methodName}: supports chaining`, () => {
const listener = sinon.spy();
const returnValue = emitter[methodName]('foo', listener);
expect(returnValue).to.equal(emitter);
});
};
onTests('on');
});

describe('off', () => {
const offTests = (methodName: 'off'): void => {
it(`${methodName}: removes the listener so it is no longer called`, () => {
const listener = sinon.spy();
emitter.on('foo', listener);
emitter.emit('foo', undefined);
expect(listener.callCount).to.equal(1);
emitter.off('foo', listener);
emitter.emit('foo', undefined);
expect(listener.callCount).to.equal(1);
});

it(`${methodName}: supports chaining`, () => {
const listener = sinon.spy();
emitter.on('foo', listener);
const returnValue = emitter.off('foo', listener);
expect(returnValue).to.equal(emitter);
});
};
offTests('off');
});

describe('once', () => {
it('only calls the listener once and then removes it', () => {
const listener = sinon.spy();
emitter.once('foo', listener);
emitter.emit('foo', undefined);
expect(listener.callCount).to.equal(1);
emitter.emit('foo', undefined);
expect(listener.callCount).to.equal(1);
});

it('supports chaining', () => {
const listener = sinon.spy();
const returnValue = emitter.once('foo', listener);
expect(returnValue).to.equal(emitter);
});
});

describe('emit', () => {
it('calls all the listeners for an event', () => {
const listener1 = sinon.spy();
const listener2 = sinon.spy();
const listener3 = sinon.spy();
emitter.on('foo', listener1).on('foo', listener2).on('bar', listener3);

emitter.emit('foo', undefined);

expect(listener1.callCount).to.equal(1);
expect(listener2.callCount).to.equal(1);
expect(listener3.callCount).to.equal(0);
});

it('passes data through to the listener', () => {
const listener = sinon.spy();
emitter.on('foo', listener);
const data = {};

emitter.emit('foo', data);
expect(listener.callCount).to.equal(1);
expect(listener.firstCall.args[0]!).to.equal(data);
});
});
});
Loading

0 comments on commit d245c17

Please sign in to comment.