├── .editorconfig
├── .eslintrc
├── .github
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ ├── compressed-size.yml
│ └── main.yml
├── .gitignore
├── LICENSE
├── README.md
├── package.json
├── src
└── index.ts
├── test
├── index_test.ts
└── test-types-compilation.ts
└── tsconfig.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = tab
5 | end_of_line = lf
6 | charset = utf-8
7 | trim_trailing_whitespace = true
8 | insert_final_newline = true
9 |
10 | [{package.json,.*rc,*.yml}]
11 | indent_style = space
12 | indent_size = 2
13 | insert_final_newline = false
14 |
15 | [*.md]
16 | trim_trailing_whitespace = false
17 | indent_style = space
18 | indent_size = 2
19 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "ignorePatterns": [
3 | "node_modules",
4 | "dist",
5 | "index.d.ts"
6 | ],
7 | "extends": [
8 | "plugin:@typescript-eslint/eslint-recommended",
9 | "plugin:@typescript-eslint/recommended",
10 | "developit"
11 | ],
12 | "parser": "@typescript-eslint/parser",
13 | "parserOptions": {
14 | "sourceType": "module"
15 | },
16 | "env": {
17 | "browser": true,
18 | "mocha": true,
19 | "jest": false,
20 | "es6": true
21 | },
22 | "globals": {
23 | "expect": true
24 | },
25 | "rules": {
26 | "semi": [
27 | 2,
28 | "always"
29 | ],
30 | "brace-style": [
31 | 2,
32 | "1tbs"
33 | ],
34 | "quotes": [
35 | 2,
36 | "single"
37 | ],
38 | "lines-around-comment": [
39 | 2,
40 | {
41 | "allowBlockStart": true,
42 | "allowObjectStart": true
43 | }
44 | ],
45 | "jest/valid-expect": 0,
46 | "@typescript-eslint/no-explicit-any": 0,
47 | "@typescript-eslint/explicit-function-return-type": 0,
48 | "@typescript-eslint/explicit-module-boundary-types": 0,
49 | "@typescript-eslint/no-empty-function": 0,
50 | "@typescript-eslint/no-non-null-assertion": 0
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | #### What is the purpose of this pull request? (put an "X" next to an item)
6 |
7 | - [ ] Documentation update
8 | - [ ] Bug fix
9 | - [ ] Feature
10 | - [ ] Code style update (formatting)
11 | - [ ] Refactoring (no functional changes)
12 | - [ ] CI related changes
13 | - [ ] Other, please explain:
14 |
15 | #### What changes did you make? (Give an overview)
16 |
17 | #### Is there anything you'd like reviewers to focus on?
18 |
19 | #### Does this PR introduce a breaking change? (What changes might other developers need to make in their application due to this PR?)
--------------------------------------------------------------------------------
/.github/workflows/compressed-size.yml:
--------------------------------------------------------------------------------
1 | name: Compressed Size
2 |
3 | on: [pull_request]
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v2
10 | - uses: preactjs/compressed-size-action@v2
11 | with:
12 | pattern: "./dist/*.{js,mjs,cjs}"
13 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - "**"
7 | push:
8 | branches:
9 | - main
10 |
11 | jobs:
12 | build:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v2
16 | - uses: actions/setup-node@v2
17 | with:
18 | node-version: 14
19 | - name: npm install, build, and test
20 | run: |
21 | npm install
22 | npm run build --if-present
23 | npm test
24 | env:
25 | CI: true
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /dist
2 | /test-reports
3 | /node_modules
4 | /npm-debug.log
5 | /index.d.ts
6 | package-lock.json
7 | .DS_Store
8 | .idea
9 | .vscode
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Jason Miller
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | # Mitt
10 |
11 | > Tiny 200b functional event emitter / pubsub.
12 |
13 | - **Microscopic:** weighs less than 200 bytes gzipped
14 | - **Useful:** a wildcard `"*"` event type listens to all events
15 | - **Familiar:** same names & ideas as [Node's EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
16 | - **Functional:** methods don't rely on `this`
17 | - **Great Name:** somehow [mitt](https://npm.im/mitt) wasn't taken
18 |
19 | Mitt was made for the browser, but works in any JavaScript runtime. It has no dependencies and supports IE9+.
20 |
21 | ## Table of Contents
22 |
23 | - [Install](#install)
24 | - [Usage](#usage)
25 | - [Examples & Demos](#examples--demos)
26 | - [API](#api)
27 | - [Contribute](#contribute)
28 | - [License](#license)
29 |
30 | ## Install
31 |
32 | This project uses [node](http://nodejs.org) and [npm](https://npmjs.com). Go check them out if you don't have them locally installed.
33 |
34 | ```sh
35 | $ npm install --save mitt
36 | ```
37 |
38 | Then with a module bundler like [rollup](http://rollupjs.org/) or [webpack](https://webpack.js.org/), use as you would anything else:
39 |
40 | ```javascript
41 | // using ES6 modules
42 | import mitt from 'mitt'
43 |
44 | // using CommonJS modules
45 | var mitt = require('mitt')
46 | ```
47 |
48 | The [UMD](https://github.com/umdjs/umd) build is also available on [unpkg](https://unpkg.com):
49 |
50 | ```html
51 |
52 | ```
53 |
54 | You can find the library on `window.mitt`.
55 |
56 | ## Usage
57 |
58 | ```js
59 | import mitt from 'mitt'
60 |
61 | const emitter = mitt()
62 |
63 | // listen to an event
64 | emitter.on('foo', e => console.log('foo', e) )
65 |
66 | // listen to all events
67 | emitter.on('*', (type, e) => console.log(type, e) )
68 |
69 | // fire an event
70 | emitter.emit('foo', { a: 'b' })
71 |
72 | // clearing all events
73 | emitter.all.clear()
74 |
75 | // working with handler references:
76 | function onFoo() {}
77 | emitter.on('foo', onFoo) // listen
78 | emitter.off('foo', onFoo) // unlisten
79 | ```
80 |
81 | ### Typescript
82 |
83 | Set `"strict": true` in your tsconfig.json to get improved type inference for `mitt` instance methods.
84 |
85 | ```ts
86 | import mitt from 'mitt';
87 |
88 | type Events = {
89 | foo: string;
90 | bar?: number;
91 | };
92 |
93 | const emitter = mitt(); // inferred as Emitter
94 |
95 | emitter.on('foo', (e) => {}); // 'e' has inferred type 'string'
96 |
97 | emitter.emit('foo', 42); // Error: Argument of type 'number' is not assignable to parameter of type 'string'. (2345)
98 | ```
99 |
100 | Alternatively, you can use the provided `Emitter` type:
101 |
102 | ```ts
103 | import mitt, { Emitter } from 'mitt';
104 |
105 | type Events = {
106 | foo: string;
107 | bar?: number;
108 | };
109 |
110 | const emitter: Emitter = mitt();
111 | ```
112 |
113 | ## Examples & Demos
114 |
115 |
116 | Preact + Mitt Codepen Demo
117 |
118 |
119 |
120 |
121 | * * *
122 |
123 | ## API
124 |
125 |
126 |
127 | #### Table of Contents
128 |
129 | - [mitt](#mitt)
130 | - [all](#all)
131 | - [on](#on)
132 | - [Parameters](#parameters)
133 | - [off](#off)
134 | - [Parameters](#parameters-1)
135 | - [emit](#emit)
136 | - [Parameters](#parameters-2)
137 |
138 | ### mitt
139 |
140 | Mitt: Tiny (~200b) functional event emitter / pubsub.
141 |
142 | Returns **Mitt**
143 |
144 | ### all
145 |
146 | A Map of event names to registered handler functions.
147 |
148 | ### on
149 |
150 | Register an event handler for the given type.
151 |
152 | #### Parameters
153 |
154 | - `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
155 | - `handler` **[Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)** Function to call in response to given event
156 |
157 | ### off
158 |
159 | Remove an event handler for the given type.
160 | If `handler` is omitted, all handlers of the given type are removed.
161 |
162 | #### Parameters
163 |
164 | - `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 `'*'`
165 | - `handler` **[Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)?** Handler function to remove
166 |
167 | ### emit
168 |
169 | Invoke all handlers for the given type.
170 | If present, `'*'` handlers are invoked after type-matched handlers.
171 |
172 | Note: Manually firing '\*' handlers is not supported.
173 |
174 | #### Parameters
175 |
176 | - `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
177 | - `evt` **Any?** Any value (object is recommended and powerful), passed to each handler
178 |
179 | ## Contribute
180 |
181 | First off, thanks for taking the time to contribute!
182 | Now, take a moment to be sure your contributions make sense to everyone else.
183 |
184 | ### Reporting Issues
185 |
186 | Found a problem? Want a new feature? First of all see if your issue or idea has [already been reported](../../issues).
187 | If don't, just open a [new clear and descriptive issue](../../issues/new).
188 |
189 | ### Submitting pull requests
190 |
191 | Pull requests are the greatest contributions, so be sure they are focused in scope, and do avoid unrelated commits.
192 |
193 | - Fork it!
194 | - Clone your fork: `git clone https://github.com//mitt`
195 | - Navigate to the newly cloned directory: `cd mitt`
196 | - Create a new branch for the new feature: `git checkout -b my-new-feature`
197 | - Install the tools necessary for development: `npm install`
198 | - Make your changes.
199 | - Commit your changes: `git commit -am 'Add some feature'`
200 | - Push to the branch: `git push origin my-new-feature`
201 | - Submit a pull request with full remarks documenting your changes.
202 |
203 | ## License
204 |
205 | [MIT License](https://opensource.org/licenses/MIT) © [Jason Miller](https://jasonformat.com/)
206 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mitt",
3 | "version": "3.0.1",
4 | "description": "Tiny 200b functional Event Emitter / pubsub.",
5 | "module": "dist/mitt.mjs",
6 | "main": "dist/mitt.js",
7 | "jsnext:main": "dist/mitt.mjs",
8 | "umd:main": "dist/mitt.umd.js",
9 | "source": "src/index.ts",
10 | "typings": "index.d.ts",
11 | "exports": {
12 | "types": "./index.d.ts",
13 | "module": "./dist/mitt.mjs",
14 | "import": "./dist/mitt.mjs",
15 | "require": "./dist/mitt.js",
16 | "default": "./dist/mitt.mjs"
17 | },
18 | "scripts": {
19 | "test": "npm-run-all --silent typecheck lint mocha test-types",
20 | "mocha": "mocha test",
21 | "test-types": "tsc test/test-types-compilation.ts --noEmit --strict",
22 | "lint": "eslint src test --ext ts --ext js",
23 | "typecheck": "tsc --noEmit",
24 | "bundle": "microbundle -f es,cjs,umd",
25 | "build": "npm-run-all --silent clean -p bundle -s docs",
26 | "clean": "rimraf dist",
27 | "docs": "documentation readme src/index.ts --section API -q --parse-extension ts",
28 | "release": "npm run -s build -s && npm t && git commit -am $npm_package_version && git tag $npm_package_version && git push && git push --tags && npm publish"
29 | },
30 | "repository": "developit/mitt",
31 | "keywords": [
32 | "events",
33 | "eventemitter",
34 | "emitter",
35 | "pubsub"
36 | ],
37 | "homepage": "https://github.com/developit/mitt",
38 | "authors": [
39 | "Jason Miller "
40 | ],
41 | "license": "MIT",
42 | "files": [
43 | "dist",
44 | "index.d.ts"
45 | ],
46 | "mocha": {
47 | "extension": [
48 | "ts"
49 | ],
50 | "require": [
51 | "ts-node/register",
52 | "esm"
53 | ],
54 | "spec": [
55 | "test/*_test.ts"
56 | ]
57 | },
58 | "prettier": {
59 | "singleQuote": true,
60 | "trailingComma": "none"
61 | },
62 | "devDependencies": {
63 | "@types/chai": "^4.2.11",
64 | "@types/mocha": "^7.0.2",
65 | "@types/sinon": "^9.0.4",
66 | "@types/sinon-chai": "^3.2.4",
67 | "@typescript-eslint/eslint-plugin": "^5.61.0",
68 | "@typescript-eslint/parser": "^5.61.0",
69 | "chai": "^4.2.0",
70 | "documentation": "^14.0.2",
71 | "eslint": "^7.32.0",
72 | "eslint-config-developit": "^1.2.0",
73 | "eslint-plugin-compat": "^4.1.4",
74 | "esm": "^3.2.25",
75 | "microbundle": "^0.12.3",
76 | "mocha": "^8.0.1",
77 | "npm-run-all": "^4.1.5",
78 | "prettier": "^2.8.8",
79 | "rimraf": "^3.0.2",
80 | "sinon": "^9.0.2",
81 | "sinon-chai": "^3.5.0",
82 | "ts-node": "^10.9.1",
83 | "typescript": "^4.9.5"
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export type EventType = string | symbol;
2 |
3 | // An event handler can take an optional event argument
4 | // and should not return a value
5 | export type Handler = (event: T) => void;
6 | export type WildcardHandler> = (
7 | type: keyof T,
8 | event: T[keyof T]
9 | ) => void;
10 |
11 | // An array of all currently registered event handlers for a type
12 | export type EventHandlerList = Array>;
13 | export type WildCardEventHandlerList> = Array<
14 | WildcardHandler
15 | >;
16 |
17 | // A map of event types and their corresponding event handlers.
18 | export type EventHandlerMap> = Map<
19 | keyof Events | '*',
20 | EventHandlerList | WildCardEventHandlerList
21 | >;
22 |
23 | export interface Emitter> {
24 | all: EventHandlerMap;
25 |
26 | on(type: Key, handler: Handler): void;
27 | on(type: '*', handler: WildcardHandler): void;
28 |
29 | off(
30 | type: Key,
31 | handler?: Handler
32 | ): void;
33 | off(type: '*', handler: WildcardHandler): void;
34 |
35 | emit(type: Key, event: Events[Key]): void;
36 | emit(
37 | type: undefined extends Events[Key] ? Key : never
38 | ): void;
39 | }
40 |
41 | /**
42 | * Mitt: Tiny (~200b) functional event emitter / pubsub.
43 | * @name mitt
44 | * @returns {Mitt}
45 | */
46 | export default function mitt>(
47 | all?: EventHandlerMap
48 | ): Emitter {
49 | type GenericEventHandler =
50 | | Handler
51 | | WildcardHandler;
52 | all = all || new Map();
53 |
54 | return {
55 | /**
56 | * A Map of event names to registered handler functions.
57 | */
58 | all,
59 |
60 | /**
61 | * Register an event handler for the given type.
62 | * @param {string|symbol} type Type of event to listen for, or `'*'` for all events
63 | * @param {Function} handler Function to call in response to given event
64 | * @memberOf mitt
65 | */
66 | on(type: Key, handler: GenericEventHandler) {
67 | const handlers: Array | undefined = all!.get(type);
68 | if (handlers) {
69 | handlers.push(handler);
70 | } else {
71 | all!.set(type, [handler] as EventHandlerList);
72 | }
73 | },
74 |
75 | /**
76 | * Remove an event handler for the given type.
77 | * If `handler` is omitted, all handlers of the given type are removed.
78 | * @param {string|symbol} type Type of event to unregister `handler` from (`'*'` to remove a wildcard handler)
79 | * @param {Function} [handler] Handler function to remove
80 | * @memberOf mitt
81 | */
82 | off(type: Key, handler?: GenericEventHandler) {
83 | const handlers: Array | undefined = all!.get(type);
84 | if (handlers) {
85 | if (handler) {
86 | handlers.splice(handlers.indexOf(handler) >>> 0, 1);
87 | } else {
88 | all!.set(type, []);
89 | }
90 | }
91 | },
92 |
93 | /**
94 | * Invoke all handlers for the given type.
95 | * If present, `'*'` handlers are invoked after type-matched handlers.
96 | *
97 | * Note: Manually firing '*' handlers is not supported.
98 | *
99 | * @param {string|symbol} type The event type to invoke
100 | * @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler
101 | * @memberOf mitt
102 | */
103 | emit(type: Key, evt?: Events[Key]) {
104 | let handlers = all!.get(type);
105 | if (handlers) {
106 | (handlers as EventHandlerList)
107 | .slice()
108 | .map((handler) => {
109 | handler(evt!);
110 | });
111 | }
112 |
113 | handlers = all!.get('*');
114 | if (handlers) {
115 | (handlers as WildCardEventHandlerList)
116 | .slice()
117 | .map((handler) => {
118 | handler(type, evt!);
119 | });
120 | }
121 | }
122 | };
123 | }
124 |
--------------------------------------------------------------------------------
/test/index_test.ts:
--------------------------------------------------------------------------------
1 | import mitt, { Emitter, EventHandlerMap } from '..';
2 | import chai, { expect } from 'chai';
3 | import { spy } from 'sinon';
4 | import sinonChai from 'sinon-chai';
5 | chai.use(sinonChai);
6 |
7 | describe('mitt', () => {
8 | it('should default export be a function', () => {
9 | expect(mitt).to.be.a('function');
10 | });
11 |
12 | it('should accept an optional event handler map', () => {
13 | expect(() => mitt(new Map())).not.to.throw;
14 | const map = new Map();
15 | const a = spy();
16 | const b = spy();
17 | map.set('foo', [a, b]);
18 | const events = mitt<{ foo: undefined }>(map);
19 | events.emit('foo');
20 | expect(a).to.have.been.calledOnce;
21 | expect(b).to.have.been.calledOnce;
22 | });
23 | });
24 |
25 | describe('mitt#', () => {
26 | const eventType = Symbol('eventType');
27 | type Events = {
28 | foo: unknown;
29 | constructor: unknown;
30 | FOO: unknown;
31 | bar: unknown;
32 | Bar: unknown;
33 | 'baz:bat!': unknown;
34 | 'baz:baT!': unknown;
35 | Foo: unknown;
36 | [eventType]: unknown;
37 | };
38 | let events: EventHandlerMap, inst: Emitter;
39 |
40 | beforeEach(() => {
41 | events = new Map();
42 | inst = mitt(events);
43 | });
44 |
45 | describe('properties', () => {
46 | it('should expose the event handler map', () => {
47 | expect(inst).to.have.property('all').that.is.a('map');
48 | });
49 | });
50 |
51 | describe('on()', () => {
52 | it('should be a function', () => {
53 | expect(inst).to.have.property('on').that.is.a('function');
54 | });
55 |
56 | it('should register handler for new type', () => {
57 | const foo = () => {};
58 | inst.on('foo', foo);
59 |
60 | expect(events.get('foo')).to.deep.equal([foo]);
61 | });
62 |
63 | it('should register handlers for any type strings', () => {
64 | const foo = () => {};
65 | inst.on('constructor', foo);
66 |
67 | expect(events.get('constructor')).to.deep.equal([foo]);
68 | });
69 |
70 | it('should append handler for existing type', () => {
71 | const foo = () => {};
72 | const bar = () => {};
73 | inst.on('foo', foo);
74 | inst.on('foo', bar);
75 |
76 | expect(events.get('foo')).to.deep.equal([foo, bar]);
77 | });
78 |
79 | it('should NOT normalize case', () => {
80 | const foo = () => {};
81 | inst.on('FOO', foo);
82 | inst.on('Bar', foo);
83 | inst.on('baz:baT!', foo);
84 |
85 | expect(events.get('FOO')).to.deep.equal([foo]);
86 | expect(events.has('foo')).to.equal(false);
87 | expect(events.get('Bar')).to.deep.equal([foo]);
88 | expect(events.has('bar')).to.equal(false);
89 | expect(events.get('baz:baT!')).to.deep.equal([foo]);
90 | });
91 |
92 | it('can take symbols for event types', () => {
93 | const foo = () => {};
94 | inst.on(eventType, foo);
95 | expect(events.get(eventType)).to.deep.equal([foo]);
96 | });
97 |
98 | // Adding the same listener multiple times should register it multiple times.
99 | // See https://nodejs.org/api/events.html#events_emitter_on_eventname_listener
100 | it('should add duplicate listeners', () => {
101 | const foo = () => {};
102 | inst.on('foo', foo);
103 | inst.on('foo', foo);
104 | expect(events.get('foo')).to.deep.equal([foo, foo]);
105 | });
106 | });
107 |
108 | describe('off()', () => {
109 | it('should be a function', () => {
110 | expect(inst).to.have.property('off').that.is.a('function');
111 | });
112 |
113 | it('should remove handler for type', () => {
114 | const foo = () => {};
115 | inst.on('foo', foo);
116 | inst.off('foo', foo);
117 |
118 | expect(events.get('foo')).to.be.empty;
119 | });
120 |
121 | it('should NOT normalize case', () => {
122 | const foo = () => {};
123 | inst.on('FOO', foo);
124 | inst.on('Bar', foo);
125 | inst.on('baz:bat!', foo);
126 |
127 | inst.off('FOO', foo);
128 | inst.off('Bar', foo);
129 | inst.off('baz:baT!', foo);
130 |
131 | expect(events.get('FOO')).to.be.empty;
132 | expect(events.has('foo')).to.equal(false);
133 | expect(events.get('Bar')).to.be.empty;
134 | expect(events.has('bar')).to.equal(false);
135 | expect(events.get('baz:bat!')).to.have.lengthOf(1);
136 | });
137 |
138 | it('should remove only the first matching listener', () => {
139 | const foo = () => {};
140 | inst.on('foo', foo);
141 | inst.on('foo', foo);
142 | inst.off('foo', foo);
143 | expect(events.get('foo')).to.deep.equal([foo]);
144 | inst.off('foo', foo);
145 | expect(events.get('foo')).to.deep.equal([]);
146 | });
147 |
148 | it('off("type") should remove all handlers of the given type', () => {
149 | inst.on('foo', () => {});
150 | inst.on('foo', () => {});
151 | inst.on('bar', () => {});
152 | inst.off('foo');
153 | expect(events.get('foo')).to.deep.equal([]);
154 | expect(events.get('bar')).to.have.length(1);
155 | inst.off('bar');
156 | expect(events.get('bar')).to.deep.equal([]);
157 | });
158 | });
159 |
160 | describe('emit()', () => {
161 | it('should be a function', () => {
162 | expect(inst).to.have.property('emit').that.is.a('function');
163 | });
164 |
165 | it('should invoke handler for type', () => {
166 | const event = { a: 'b' };
167 |
168 | inst.on('foo', (one, two?: unknown) => {
169 | expect(one).to.deep.equal(event);
170 | expect(two).to.be.an('undefined');
171 | });
172 |
173 | inst.emit('foo', event);
174 | });
175 |
176 | it('should NOT ignore case', () => {
177 | const onFoo = spy(),
178 | onFOO = spy();
179 | events.set('Foo', [onFoo]);
180 | events.set('FOO', [onFOO]);
181 |
182 | inst.emit('Foo', 'Foo arg');
183 | inst.emit('FOO', 'FOO arg');
184 |
185 | expect(onFoo).to.have.been.calledOnce.and.calledWith('Foo arg');
186 | expect(onFOO).to.have.been.calledOnce.and.calledWith('FOO arg');
187 | });
188 |
189 | it('should invoke * handlers', () => {
190 | const star = spy(),
191 | ea = { a: 'a' },
192 | eb = { b: 'b' };
193 |
194 | events.set('*', [star]);
195 |
196 | inst.emit('foo', ea);
197 | expect(star).to.have.been.calledOnce.and.calledWith('foo', ea);
198 | star.resetHistory();
199 |
200 | inst.emit('bar', eb);
201 | expect(star).to.have.been.calledOnce.and.calledWith('bar', eb);
202 | });
203 | });
204 | });
205 |
--------------------------------------------------------------------------------
/test/test-types-compilation.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/ban-ts-comment, @typescript-eslint/no-unused-vars */
2 |
3 | import mitt from '..';
4 |
5 | interface SomeEventData {
6 | name: string;
7 | }
8 |
9 | const emitter = mitt<{
10 | foo: string;
11 | someEvent: SomeEventData;
12 | bar?: number;
13 | }>();
14 |
15 | const barHandler = (x?: number) => {};
16 | const fooHandler = (x: string) => {};
17 | const wildcardHandler = (
18 | _type: 'foo' | 'bar' | 'someEvent',
19 | _event: string | SomeEventData | number | undefined
20 | ) => {};
21 |
22 | /*
23 | * Check that 'on' args are inferred correctly
24 | */
25 | {
26 | // @ts-expect-error
27 | emitter.on('foo', barHandler);
28 | emitter.on('foo', fooHandler);
29 |
30 | emitter.on('bar', barHandler);
31 | // @ts-expect-error
32 | emitter.on('bar', fooHandler);
33 |
34 | emitter.on('*', wildcardHandler);
35 | // fooHandler is ok, because ('foo' | 'bar' | 'someEvent') extends string
36 | emitter.on('*', fooHandler);
37 | // @ts-expect-error
38 | emitter.on('*', barHandler);
39 | }
40 |
41 | /*
42 | * Check that 'off' args are inferred correctly
43 | */
44 | {
45 | // @ts-expect-error
46 | emitter.off('foo', barHandler);
47 | emitter.off('foo', fooHandler);
48 |
49 | emitter.off('bar', barHandler);
50 | // @ts-expect-error
51 | emitter.off('bar', fooHandler);
52 |
53 | emitter.off('*', wildcardHandler);
54 | // fooHandler is ok, because ('foo' | 'bar' | 'someEvent') extends string
55 | emitter.off('*', fooHandler);
56 | // @ts-expect-error
57 | emitter.off('*', barHandler);
58 | }
59 |
60 | /*
61 | * Check that 'emit' args are inferred correctly
62 | */
63 | {
64 | // @ts-expect-error
65 | emitter.emit('someEvent', 'NOT VALID');
66 | emitter.emit('someEvent', { name: 'jack' });
67 |
68 | // @ts-expect-error
69 | emitter.emit('foo');
70 | // @ts-expect-error
71 | emitter.emit('foo', 1);
72 | emitter.emit('foo', 'string');
73 |
74 | emitter.emit('bar');
75 | emitter.emit('bar', 1);
76 | // @ts-expect-error
77 | emitter.emit('bar', 'string');
78 | }
79 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "strict": true,
5 | "noEmit": true,
6 | "declaration": true,
7 | "moduleResolution": "node",
8 | "skipLibCheck": true,
9 | "esModuleInterop": true,
10 | },
11 | "include": [
12 | "src/*.ts",
13 | "test/*.ts"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------