From be5237f301b1059ffff9e1e795fd66594644892a Mon Sep 17 00:00:00 2001 From: Jack Franklin Date: Tue, 26 May 2020 22:45:53 +0100 Subject: Migrate to TypeScript and use Map (#99) Migrate to TypeScript & Microbundle, move to Map for event handler storage --- README.md | 34 ++++++++++++------------- mitt.d.ts | 53 --------------------------------------- package.json | 60 +++++++++++++++++++-------------------------- rollup.config.js | 20 --------------- src/index.js | 63 ----------------------------------------------- src/index.ts | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ test/index.js | 63 ++++++++++++++++++++++++++--------------------- typings.json | 5 ---- 8 files changed, 152 insertions(+), 221 deletions(-) delete mode 100644 mitt.d.ts delete mode 100644 rollup.config.js delete mode 100644 src/index.js create mode 100644 src/index.ts delete mode 100644 typings.json diff --git a/README.md b/README.md index 37a135d..b48fa97 100644 --- a/README.md +++ b/README.md @@ -95,13 +95,19 @@ const emitter: mitt.Emitter = mitt(); -### mitt +#### Table of Contents -Mitt: Tiny (~200b) functional event emitter / pubsub. +- [mitt](#mitt) +- [on](#on) + - [Parameters](#parameters) +- [off](#off) + - [Parameters](#parameters-1) +- [emit](#emit) + - [Parameters](#parameters-2) -**Parameters** +### mitt -- `all` **EventHandlerMap** +Mitt: Tiny (~200b) functional event emitter / pubsub. Returns **Mitt** @@ -109,18 +115,18 @@ Returns **Mitt** Register an event handler for the given type. -**Parameters** +#### Parameters -- `type` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** Type of event to listen for, or `"*"` for all events +- `type` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [symbol](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Symbol))** Type of event to listen for, or `"*"` for all events - `handler` **[Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)** Function to call in response to given event ### off Remove an event handler for the given type. -**Parameters** +#### Parameters -- `type` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** Type of event to unregister `handler` from, or `"*"` +- `type` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [symbol](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Symbol))** Type of event to unregister `handler` from, or `"*"` - `handler` **[Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)** Handler function to remove ### emit @@ -128,11 +134,11 @@ Remove an event handler for the given type. Invoke all handlers for the given type. If present, `"*"` handlers are invoked after type-matched handlers. -_Note: Manually firing "*" handlers is not supported._ +Note: Manually firing "\*" handlers is not supported. -**Parameters** +#### Parameters -- `type` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** The event type to invoke +- `type` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [symbol](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Symbol))** The event type to invoke - `evt` **Any?** Any value (object is recommended and powerful), passed to each handler ## Contribute @@ -140,12 +146,6 @@ _Note: Manually firing "*" handlers is not supported._ First off, thanks for taking the time to contribute! Now, take a moment to be sure your contributions make sense to everyone else. -Development Start: - -This project is typed with Flow Type annotations. To ensure you have the proper typings for this project run - -`flow-typed install` - ### Reporting Issues Found a problem? Want a new feature? First of all see if your issue or idea has [already been reported](../../issues). diff --git a/mitt.d.ts b/mitt.d.ts deleted file mode 100644 index 6cb62d8..0000000 --- a/mitt.d.ts +++ /dev/null @@ -1,53 +0,0 @@ -declare var mitt: mitt.MittStatic; - -declare module "mitt" { - export = mitt; -} - -declare namespace mitt { - type Handler = (event?: any) => void; - type WildcardHandler = (type: string, event?: any) => void; - - interface MittStatic { - (all?: {[key: string]: Array}): Emitter; - } - - interface Emitter { - /** - * Register an event handler for the given type. - * - * @param {string} type Type of event to listen for, or `"*"` for all events. - * @param {Handler} handler Function to call in response to the given event. - * - * @memberOf Mitt - */ - on(type: string, handler: Handler): void; - on(type: "*", handler: WildcardHandler): void; - - /** - * Function to call in response to the given event - * - * @param {string} type Type of event to unregister `handler` from, or `"*"` - * @param {Handler} handler Handler function to remove. - * - * @memberOf Mitt - */ - off(type: string, handler: Handler): void; - off(type: "*", handler: WildcardHandler): void; - - /** - * Invoke all handlers for the given type. - * If present, `"*"` handlers are invoked prior to type-matched handlers. - * - * @param {string} type The event type to invoke - * @param {any} [event] An event object, passed to each handler - * - * @memberOf Mitt - */ - emit(type: string, event?: any): void; - /** - * Note: Manually firing "*" events is unsupported. - */ - emit(type: "*", event?: any): void; - } -} diff --git a/package.json b/package.json index 6275b3a..ac14731 100644 --- a/package.json +++ b/package.json @@ -3,20 +3,20 @@ "version": "1.2.0", "description": "Tiny 200b functional Event Emitter / pubsub.", "jsnext:main": "dist/mitt.es.js", + "source": "src/index.ts", "module": "dist/mitt.es.js", "main": "dist/mitt.js", "umd:main": "dist/mitt.umd.js", + "typings": "dist/index.d.ts", "scripts": { "bump": "standard-version", - "testonly": "mocha --require esm --require flow-remove-types/register test/**/*.js", - "lint": "eslint src test", - "test": "flow && npm run lint && npm run testonly", - "build": "npm-run-all --silent clean -p rollup -p minify:* -s docs size", + "testonly": "mocha --require esm --require ts-node/register test/**/*.js", + "lint": "eslint src test --ext ts --ext js", + "test": "tsc src/index.ts --noEmit && npm run lint && npm run testonly", + "bundle": "microbundle", + "build": "npm-run-all --silent clean -p bundle -s docs size", "clean": "rimraf dist", - "rollup": "rollup -c", - "minify:cjs": "uglifyjs $npm_package_main -cm toplevel -o $npm_package_main -p relative --in-source-map ${npm_package_main}.map --source-map ${npm_package_main}.map", - "minify:umd": "uglifyjs $npm_package_umd_main -cm -o $npm_package_umd_main -p relative --in-source-map ${npm_package_umd_main}.map --source-map ${npm_package_umd_main}.map", - "docs": "documentation readme src/index.js --section API -q", + "docs": "documentation readme src/index.ts --section API -q --parse-extension ts", "size": "echo \"Gzipped Size: $(strip-json-comments --no-whitespace $npm_package_main | gzip-size | pretty-bytes)\"", "release": "npm run build -s && npm run bump && git push --follow-tags origin master && npm publish" }, @@ -33,21 +33,15 @@ "license": "MIT", "files": [ "src", - "dist", - "mitt.d.ts" + "dist" ], - "babel": { - "presets": [ - "es2015", - "stage-0" - ], - "plugins": [ - "transform-flow-strip-types" - ] - }, "eslintConfig": { - "extends": "eslint:recommended", - "parser": "babel-eslint", + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" + ], + "parser": "@typescript-eslint/parser", "parserOptions": { "sourceType": "module" }, @@ -63,34 +57,30 @@ "semi": [ 2, "always" - ] + ], + "@typescript-eslint/no-explicit-any": 0, + "@typescript-eslint/explicit-function-return-type": 0, + "@typescript-eslint/no-empty-function": 0 } }, - "typings": "./mitt.d.ts", "devDependencies": { - "babel-core": "^6.9.1", - "babel-eslint": "^10.0.3", - "babel-plugin-transform-flow-strip-types": "^6.21.0", - "babel-preset-es2015": "^6.9.0", - "babel-preset-stage-0": "^6.5.0", + "@typescript-eslint/eslint-plugin": "^2.34.0", + "@typescript-eslint/parser": "^2.34.0", "chai": "^3.5.0", - "documentation": "^4.0.0-beta4", + "documentation": "^13.0.0", "eslint": "^6.5.1", "esm": "^3.2.25", - "flow-bin": "^0.38.0", - "flow-remove-types": "^1.2.0", "gzip-size-cli": "^1.0.0", + "microbundle": "^0.12.0", "mocha": "^3.2.0", "npm-run-all": "^2.1.1", "pretty-bytes-cli": "^2.0.0", "rimraf": "^2.5.2", - "rollup": "^0.41.4", - "rollup-plugin-buble": "^0.15.0", - "rollup-plugin-flow": "^1.1.1", "sinon": "^1.17.4", "sinon-chai": "^2.8.0", "standard-version": "^4.0.0", "strip-json-comments-cli": "^1.0.1", - "uglify-js": "^2.6.2" + "ts-node": "^8.10.1", + "typescript": "^3.9.3" } } diff --git a/rollup.config.js b/rollup.config.js deleted file mode 100644 index 9eba049..0000000 --- a/rollup.config.js +++ /dev/null @@ -1,20 +0,0 @@ -import buble from 'rollup-plugin-buble'; -import flow from 'rollup-plugin-flow'; -import fs from 'fs'; - -const pkg = JSON.parse(fs.readFileSync('./package.json')); - -export default { - entry: 'src/index.js', - useStrict: false, - sourceMap: true, - plugins: [ - flow(), - buble() - ], - targets: [ - { dest: pkg.main, format: 'cjs' }, - { dest: pkg.module, format: 'es' }, - { dest: pkg['umd:main'], format: 'umd', moduleName: pkg.name } - ] -}; diff --git a/src/index.js b/src/index.js deleted file mode 100644 index d9b36f8..0000000 --- a/src/index.js +++ /dev/null @@ -1,63 +0,0 @@ -// @flow -// An event handler can take an optional event argument -// and should not return a value -type EventHandler = (event?: any) => void; -type WildCardEventHandler = (type: string, event?: any) => void - -// An array of all currently registered event handlers for a type -type EventHandlerList = Array; -type WildCardEventHandlerList = Array; -// A map of event types and their corresponding event handlers. -type EventHandlerMap = { - '*'?: WildCardEventHandlerList, - [type: string]: EventHandlerList, -}; - -/** Mitt: Tiny (~200b) functional event emitter / pubsub. - * @name mitt - * @returns {Mitt} - */ -export default function mitt(all: EventHandlerMap) { - all = all || Object.create(null); - - return { - /** - * Register an event handler for the given type. - * - * @param {String} type Type of event to listen for, or `"*"` for all events - * @param {Function} handler Function to call in response to given event - * @memberOf mitt - */ - on(type: string, handler: EventHandler) { - (all[type] || (all[type] = [])).push(handler); - }, - - /** - * Remove an event handler for the given type. - * - * @param {String} type Type of event to unregister `handler` from, or `"*"` - * @param {Function} handler Handler function to remove - * @memberOf mitt - */ - off(type: string, handler: EventHandler) { - if (all[type]) { - all[type].splice(all[type].indexOf(handler) >>> 0, 1); - } - }, - - /** - * Invoke all handlers for the given type. - * If present, `"*"` handlers are invoked after type-matched handlers. - * - * Note: Manually firing "*" handlers is not supported. - * - * @param {String} type The event type to invoke - * @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler - * @memberOf mitt - */ - emit(type: string, evt: any) { - (all[type] || []).slice().map((handler) => { handler(evt); }); - (all['*'] || []).slice().map((handler) => { handler(type, evt); }); - } - }; -} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..f0a2ebb --- /dev/null +++ b/src/index.ts @@ -0,0 +1,75 @@ +type EventType = string | symbol; + +// An event handler can take an optional event argument +// and should not return a value +type Handler = (event?: any) => void; +type WildcardHandler= (type: EventType, event?: any) => void + +// An array of all currently registered event handlers for a type +type EventHandlerList = Array; +type WildCardEventHandlerList = Array; + +// A map of event types and their corresponding event handlers. +type EventHandlerMap = Map; + +export interface Emitter { + on(type: EventType, handler: Handler): void; + on(type: "*", handler: WildcardHandler): void; + + off(type: EventType, handler: Handler): void; + off(type: "*", handler: WildcardHandler): void; + + emit(type: EventType, event?: T): void; + emit(type: "*", event?: any): void; +} + +/** Mitt: Tiny (~200b) functional event emitter / pubsub. + * @name mitt + * @returns {Mitt} + */ +export default function mitt(all: EventHandlerMap): Emitter { + all = all || new Map(); + + return { + /** + * Register an event handler for the given type. + * + * @param {string|symbol} type Type of event to listen for, or `"*"` for all events + * @param {Function} handler Function to call in response to given event + * @memberOf mitt + */ + on(type: EventType, handler: Handler) { + const handlers = (all.get(type) || []); + handlers.push(handler); + all.set(type, handlers); + }, + + /** + * Remove an event handler for the given type. + * + * @param {string|symbol} type Type of event to unregister `handler` from, or `"*"` + * @param {Function} handler Handler function to remove + * @memberOf mitt + */ + off(type: EventType, handler: Handler) { + if (all.has(type)) { + all.get(type).splice(all.get(type).indexOf(handler) >>> 0, 1); + } + }, + + /** + * Invoke all handlers for the given type. + * If present, `"*"` handlers are invoked after type-matched handlers. + * + * Note: Manually firing "*" handlers is not supported. + * + * @param {string|symbol} type The event type to invoke + * @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler + * @memberOf mitt + */ + emit(type: EventType, evt: any) { + ((all.get(type) || []) as EventHandlerList).slice().map((handler) => { handler(evt); }); + ((all.get('*') || []) as WildCardEventHandlerList).slice().map((handler) => { handler(type, evt); }); + } + }; +} diff --git a/test/index.js b/test/index.js index 3e0ef78..554f1f1 100644 --- a/test/index.js +++ b/test/index.js @@ -12,7 +12,7 @@ describe('mitt#', () => { let events, inst; beforeEach( () => { - events = Object.create(null); + events = new Map(); inst = mitt(events); }); @@ -24,39 +24,46 @@ describe('mitt#', () => { }); it('should register handler for new type', () => { - let foo = () => {}; + const foo = () => {}; inst.on('foo', foo); - expect(events).to.have.property('foo').that.deep.equals([foo]); + expect(events.get('foo')).to.deep.equal([foo]); }); it('should register handlers for any type strings', () => { - let foo = () => {}; + const foo = () => {}; inst.on('constructor', foo); - expect(events).to.have.property('constructor').that.deep.equals([foo]); + expect(events.get('constructor')).to.deep.equal([foo]); }); it('should append handler for existing type', () => { - let foo = () => {}; - let bar = () => {}; + const foo = () => {}; + const bar = () => {}; inst.on('foo', foo); inst.on('foo', bar); - expect(events).to.have.property('foo').that.deep.equals([foo, bar]); + expect(events.get('foo')).to.deep.equal([foo, bar]); }); it('should NOT normalize case', () => { - let foo = () => {}; + const foo = () => {}; inst.on('FOO', foo); inst.on('Bar', foo); inst.on('baz:baT!', foo); - expect(events).to.have.property('FOO').that.deep.equals([foo]); - expect(events).to.not.have.property('foo'); - expect(events).to.have.property('Bar').that.deep.equals([foo]); - expect(events).to.not.have.property('bar'); - expect(events).to.have.property('baz:baT!').that.deep.equals([foo]); + expect(events.get('FOO')).to.deep.equal([foo]); + expect(events.has('foo')).to.equal(false); + expect(events.get('Bar')).to.deep.equal([foo]); + expect(events.has('bar')).to.equal(false); + expect(events.get('baz:baT!')).to.deep.equal([foo]); + }); + + it('can take symbols for event types', () => { + const foo = () => {}; + const eventType = Symbol('eventType'); + inst.on(eventType, foo); + expect(events.get(eventType)).to.deep.equal([foo]); }); }); @@ -68,15 +75,15 @@ describe('mitt#', () => { }); it('should remove handler for type', () => { - let foo = () => {}; + const foo = () => {}; inst.on('foo', foo); inst.off('foo', foo); - expect(events).to.have.property('foo').that.is.empty; + expect(events.get('foo')).to.be.empty; }); it('should NOT normalize case', () => { - let foo = () => {}; + const foo = () => {}; inst.on('FOO', foo); inst.on('Bar', foo); inst.on('baz:bat!', foo); @@ -85,11 +92,11 @@ describe('mitt#', () => { inst.off('Bar', foo); inst.off('baz:baT!', foo); - expect(events).to.have.property('FOO').that.is.empty; - expect(events).to.not.have.property('foo'); - expect(events).to.have.property('Bar').that.is.empty; - expect(events).to.not.have.property('bar'); - expect(events).to.have.property('baz:bat!').with.length(1); + expect(events.get('FOO')).to.be.empty; + expect(events.has('foo')).to.equal(false); + expect(events.get('Bar')).to.be.empty; + expect(events.has('bar')).to.equal(false); + expect(events.get('baz:bat!')).to.have.lengthOf(1); }); }); @@ -101,7 +108,7 @@ describe('mitt#', () => { }); it('should invoke handler for type', () => { - let event = { a: 'b' }; + const event = { a: 'b' }; inst.on('foo', (one, two) => { expect(one).to.deep.equal(event); @@ -112,10 +119,10 @@ describe('mitt#', () => { }); it('should NOT ignore case', () => { - let onFoo = spy(), + const onFoo = spy(), onFOO = spy(); - events.Foo = [onFoo]; - events.FOO = [onFOO]; + events.set('Foo', [onFoo]); + events.set('FOO', [onFOO]); inst.emit('Foo', 'Foo arg'); inst.emit('FOO', 'FOO arg'); @@ -125,11 +132,11 @@ describe('mitt#', () => { }); it('should invoke * handlers', () => { - let star = spy(), + const star = spy(), ea = { a: 'a' }, eb = { b: 'b' }; - events['*'] = [star]; + events.set('*', [star]); inst.emit('foo', ea); expect(star).to.have.been.calledOnce.and.calledWith('foo', ea); diff --git a/typings.json b/typings.json deleted file mode 100644 index ad8ab5f..0000000 --- a/typings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "mitt", - "main": "mitt.d.ts", - "version": false -} \ No newline at end of file -- cgit v1.2.3