├── .eslintrc.json
├── .gitignore
├── .npmignore
├── .travis.yml
├── LICENSE
├── README.md
├── package.json
├── src
├── tests.ts
└── weak-emitter.ts
└── tsconfig.json
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es6": true,
5 | "node": true
6 | },
7 | "extends": [
8 | "standard"
9 | ],
10 | "globals": {
11 | "Atomics": "readonly",
12 | "SharedArrayBuffer": "readonly"
13 | },
14 | "parser": "@typescript-eslint/parser",
15 | "parserOptions": {
16 | "ecmaVersion": 2018,
17 | "sourceType": "module"
18 | },
19 | "plugins": [
20 | "@typescript-eslint"
21 | ],
22 | "rules": {
23 | }
24 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | npm-debug.log
4 | .lvimrc
5 | .tern-port
6 | lib
7 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | npm-debug.log
4 | .lvimrc
5 | .tern-port
6 | src
7 | .eslintrc
8 | .travis.yml
9 | tsconfig.json
10 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "12.14.0"
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020 Jacobo Tabernero - github.com/jacoborus
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | 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, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | weak-emitter
2 | ============
3 |
4 | Contexts based event emitter on ES6 WeakMaps
5 |
6 | [](https://travis-ci.org/jacoborus/weak-emitter) [](https://www.npmjs.com/package/weak-emitter)
7 |
8 |
9 | ```js
10 | const context = {}
11 | const eventKey = 'propName'
12 | emitter.on(context, eventKey, (...args) => console.log(args))
13 | emitter.emit(context, eventKey, 'first', 2, true)
14 | // ['first', 2, true]
15 | ```
16 |
17 |
18 | ## Install
19 |
20 | ```sh
21 | npm i weak-emitter --save
22 | # or: yarn add weak-emitter
23 | ```
24 |
25 | ## Emitter API
26 |
27 | - [on](#emitter-on-api)
28 | - [emit](#emitter-emit-api)
29 | - [off](#emitter-off-api)
30 |
31 | **Argument types:**
32 |
33 | - `context: object`
34 | - `eventKey: string | object`
35 | - `handler: (...args: []) => void`
36 |
37 |
38 | ### on(context, eventKey, handler)
39 |
40 | Create new event with `eventKey` in `context` that will trigger the `handler`.
41 | Every handler will be added once, despite the number of times it was added to the event. Handlers are invoked in the order they were added.
42 |
43 | Returns an event controller that has three methods:
44 |
45 | - emit(...args): trigger handler
46 | - off(): remove event
47 | - tranfer(destination): moves the event to `destination` context
48 |
49 | ```js
50 | const context = {}
51 | const eventKey = 'propName'
52 | const evController = emitter.on(context, eventKey, (...args) => doSomething(args))
53 |
54 | const ctx2 = {}
55 | evController.transfer(ctx2)
56 | evController.off()
57 |
58 | ```
59 |
60 |
61 |
62 | ### emit(context, eventKey[, ...args])
63 |
64 | Invoke all handlers in `context` tagged with `key`, passing the rest of the arguments
65 |
66 | ```js
67 | const context = {}
68 | const eventKey = 'propName'
69 | emitter.on(context, eventKey, (...args) => console.log(args))
70 | emitter.emit(context, eventKey, 'first', 2, true)
71 | // ['first', 2, true]
72 | ```
73 |
74 |
75 |
76 | ### off(context, key, handler)
77 |
78 | Removes `handler` from the event tagged with `key` in `context`
79 |
80 | ```js
81 | emitter.off(context, eventKey, handler)
82 | ```
83 |
84 |
85 |
86 | ## Testing
87 |
88 | ```sh
89 | npm test
90 | ```
91 |
92 |
93 |
94 | ---
95 |
96 | © 2020 [Jacobo Tabernero](http://jacoborus.codes) - Released under [MIT License](https://raw.github.com/jacoborus/weak-emitter/master/LICENSE)
97 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "weak-emitter",
3 | "version": "5.2.1",
4 | "description": "Contexts based event emitter on ES6 WeakMaps",
5 | "main": "lib/weak-emitter.js",
6 | "types": "lib/weak-emitter.d.ts",
7 | "scripts": {
8 | "linter": "eslint 'src/*.ts'",
9 | "test": "tape -r ts-node/register src/tests.ts",
10 | "prepublish": "rm -rf lib && tsc",
11 | "build": "rm -rf lib && tsc"
12 | },
13 | "repository": {
14 | "type": "git",
15 | "url": "git+ssh://git@github.com/jacoborus/weak-emitter.git"
16 | },
17 | "keywords": [
18 | "event emitter",
19 | "map",
20 | "ES6"
21 | ],
22 | "author": "Jacobo Tabernero",
23 | "license": "MIT",
24 | "bugs": {
25 | "url": "https://github.com/jacoborus/weak-emitter/issues"
26 | },
27 | "homepage": "https://github.com/jacoborus/weak-emitter",
28 | "devDependencies": {
29 | "@types/tape": "4.2.34",
30 | "@typescript-eslint/eslint-plugin": "2.20.0",
31 | "@typescript-eslint/parser": "2.20.0",
32 | "eslint": "6.8.0",
33 | "eslint-config-standard": "14.1.0",
34 | "eslint-plugin-import": "2.20.1",
35 | "eslint-plugin-node": "11.0.0",
36 | "eslint-plugin-promise": "4.2.1",
37 | "eslint-plugin-standard": "4.0.1",
38 | "tape": "4.13.0",
39 | "ts-node": "8.6.2",
40 | "typescript": "3.7.5"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/tests.ts:
--------------------------------------------------------------------------------
1 | import test from 'tape'
2 | import { weakEmitter } from './weak-emitter'
3 |
4 | test('on and emit', t => {
5 | const emitter = weakEmitter()
6 | const obj = {}
7 | let control = 0
8 | const fn = () => ++control
9 | emitter.on(obj, obj, fn)
10 | emitter.emit(obj, obj)
11 | t.is(control, 1, 'trigger')
12 | emitter.emit(obj, obj)
13 | t.is(control, 2, 'trigger')
14 | emitter.on(obj, obj, fn)
15 | emitter.emit(obj, obj)
16 | t.is(control, 3, 'trigger')
17 | t.end()
18 | })
19 |
20 | test('off action', t => {
21 | const emitter = weakEmitter()
22 | const obj = {}
23 | const control = {
24 | a: 0,
25 | b: 0
26 | }
27 | const fn = () => ++control.b
28 | emitter.on(obj, obj, () => ++control.a)
29 | emitter.on(obj, obj, fn)
30 | emitter.emit(obj, obj)
31 | t.is(control.a, 1, 'control a')
32 | t.is(control.b, 1, 'control b')
33 | emitter.off(obj, obj, fn)
34 | emitter.emit(obj, obj)
35 | t.is(control.a, 2, 'control a')
36 | t.is(control.b, 1, 'control b')
37 | t.end()
38 | })
39 |
40 | test('eventController#emit', t => {
41 | const ee = weakEmitter()
42 | const obj = {}
43 | t.plan(1)
44 | const event = ee.on(obj, 'a', (...change) => {
45 | t.same(change, [1, 2, 3, 4])
46 | })
47 | event.emit(1, 2, 3, 4)
48 | t.end()
49 | })
50 |
51 | test('eventController#off', t => {
52 | const ee = weakEmitter()
53 | const obj = {}
54 | const event = ee.on(obj, 'a', () => { t.fail() })
55 | event.off()
56 | ee.emit(obj, 'a')
57 | t.pass()
58 | t.end()
59 | })
60 |
61 | test('eventController#transfer', t => {
62 | t.plan(1)
63 | const ee = weakEmitter()
64 | const pass = () => t.pass()
65 | const fail = () => t.fail()
66 | const control = {
67 | action: pass
68 | }
69 | const obj = {}
70 | const dest = {}
71 | const event = ee.on(obj, 'a', () => { control.action() })
72 |
73 | event.transfer(dest)
74 | ee.emit(dest, 'a')
75 |
76 | control.action = fail
77 | ee.emit(obj, 'a')
78 |
79 | event.off()
80 | ee.emit(dest, 'a')
81 |
82 | t.end()
83 | })
84 |
85 | test('emit actions in order', t => {
86 | const emitter = weakEmitter()
87 | const obj = {}
88 | let control = ''
89 | emitter.on(obj, obj, () => { control = control + 'a' })
90 | emitter.emit(obj, obj)
91 | t.is(control, 'a', 'trigger')
92 | emitter.emit(obj, obj)
93 | t.is(control, 'aa', 'trigger')
94 |
95 | emitter.on(obj, obj, () => { control = control + 'b' })
96 | emitter.emit(obj, obj)
97 | t.is(control, 'aaab', 'unsubscribe')
98 |
99 | emitter.on(obj, obj, () => { control = control + 'c' })
100 | emitter.emit(obj, obj)
101 | t.is(control, 'aaababc', 'unsubscribe')
102 |
103 | t.end()
104 | })
105 |
106 | test('emit with arguments', t => {
107 | const emitter = weakEmitter()
108 | const obj = {}
109 | const control = {
110 | a: 0
111 | }
112 | emitter.on(obj, obj, a => { control.a = a })
113 |
114 | emitter.emit(obj, obj, 1)
115 | t.is(control.a, 1)
116 |
117 | t.end()
118 | })
119 |
120 | test('remove listener in a event with muliple listeners', t => {
121 | const emitter = weakEmitter()
122 | const out: number[] = []
123 | const tt = {}
124 | const f1 = () => out.push(1)
125 | const f3 = () => out.push(3)
126 | const f2 = () => {
127 | out.push(2)
128 | emitter.off(tt, tt, f3)
129 | }
130 | emitter.on(tt, tt, f1)
131 | emitter.on(tt, tt, f2)
132 | emitter.on(tt, tt, f3)
133 | emitter.emit(tt, tt)
134 | t.is(out[0], 1)
135 | t.is(out[1], 2)
136 | t.notOk(out[2])
137 | emitter.emit(tt, tt)
138 | t.is(out[2], 1)
139 | t.is(out[3], 2)
140 | t.end()
141 | })
142 |
--------------------------------------------------------------------------------
/src/weak-emitter.ts:
--------------------------------------------------------------------------------
1 | type EventHandler = (...args: any[]) => void
2 | type EventKey = PropertyKey | object
3 | type EventStack = Set
4 | type EventContext = Map
5 | type Contexts = WeakMap