├── .gitignore ├── .prettierrc ├── README.md ├── package-lock.json ├── package.json ├── src └── tiny-signal.js └── test └── tiny-signal.test.js /.gitignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /node_modules 3 | /npm-debug.log 4 | .DS_Store 5 | .idea 6 | .vscode -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "bracketSpacing": true, 4 | "trailingComma": "es5", 5 | "printWidth": 80, 6 | "tabWidth": 2, 7 | "useTabs": false, 8 | "semi": false 9 | } 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tiny-signal 2 | 3 | tiny-signal is a minimal implementation of the observer design pattern in only 228 bytes gzipped. 4 | 5 | ## Installation 6 | 7 | ``` 8 | npm i tiny-signal 9 | ``` 10 | 11 | ## Usage 12 | 13 | ```js 14 | import signal from 'tiny-signal' 15 | 16 | const instance = signal() 17 | 18 | const foo = data => console.log(`foo! ${data}`) 19 | const bar = data => console.log(`bar! ${data}`) 20 | const baz = data => console.log(`baz! ${data}`) 21 | 22 | instance.add(foo) 23 | instance.add(bar) 24 | instance.add(baz) 25 | 26 | instance.dispatch('hello world!') 27 | // foo! hello world! 28 | // bar! hello world! 29 | // baz! hello world! 30 | 31 | instance.remove(bar) 32 | instance.dispatch('howdy!') 33 | // foo! howdy! 34 | // baz! howdy! 35 | 36 | instance.destroy() // removes all listeners 37 | ``` 38 | 39 | ## License 40 | 41 | [MIT License](https://opensource.org/licenses/MIT) © [Mike Wagz](https://wagz.io) 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tiny-signal", 3 | "version": "1.0.0", 4 | "description": "Minimal 228 byte implementation of the observer design pattern.", 5 | "main": "dist/tiny-signal.js", 6 | "umd:main": "dist/tiny-signal.umd.js", 7 | "module": "dist/tiny-signal.mjs", 8 | "unpkg": "dist/tiny-signal.umd.js", 9 | "source": "src/tiny-signal.js", 10 | "scripts": { 11 | "build": "microbundle build", 12 | "watch": "microbundle watch", 13 | "test": "jest" 14 | }, 15 | "repository": "mikehwagz/tiny-signal", 16 | "keywords": [ 17 | "signal", 18 | "signals", 19 | "event", 20 | "events", 21 | "observer", 22 | "emitter" 23 | ], 24 | "homepage": "https://github.com/mikehwagz/tiny-signal", 25 | "author": "Mike Wagz (https://selfaware.studio)", 26 | "license": "MIT", 27 | "devDependencies": { 28 | "jest": "^23.6.0", 29 | "microbundle": "^0.8.3" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/tiny-signal.js: -------------------------------------------------------------------------------- 1 | export default function() { 2 | let fns = new Set() 3 | 4 | return { 5 | add(fn) { 6 | fns.add(fn) 7 | }, 8 | 9 | remove(fn) { 10 | fns.delete(fn) 11 | }, 12 | 13 | dispatch(data) { 14 | fns.forEach(fn => { 15 | fn(data) 16 | }) 17 | }, 18 | 19 | destroy() { 20 | fns.clear() 21 | }, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/tiny-signal.test.js: -------------------------------------------------------------------------------- 1 | const signal = require('../dist/tiny-signal') 2 | 3 | describe('tiny-signal:', () => { 4 | it('should be a function', () => { 5 | expect(typeof signal).toBe('function') 6 | }) 7 | 8 | let instance = signal() 9 | 10 | it('should return an object', () => { 11 | expect(typeof instance).toBe('object') 12 | }) 13 | 14 | let foo = jest.fn().mockName('foo') 15 | let bar = jest.fn().mockName('bar') 16 | let baz = jest.fn().mockName('baz') 17 | 18 | describe('add()', () => { 19 | it('should be a function', () => { 20 | expect(typeof instance.add).toBe('function') 21 | }) 22 | 23 | it('should add a listener', () => { 24 | instance.add(foo) 25 | instance.dispatch() 26 | expect(foo).toHaveBeenCalledTimes(1) 27 | }) 28 | 29 | it('should not add duplicate listener', () => { 30 | instance.add(foo) 31 | instance.dispatch() 32 | expect(foo).toHaveBeenCalledTimes(2) 33 | }) 34 | }) 35 | 36 | describe('remove()', () => { 37 | it('should be a function', () => { 38 | expect(typeof instance.remove).toBe('function') 39 | }) 40 | 41 | it('should remove a listener', () => { 42 | instance.remove(foo) 43 | instance.dispatch() 44 | expect(foo).toHaveBeenCalledTimes(2) 45 | }) 46 | }) 47 | 48 | describe('dispatch()', () => { 49 | it('should be a function', () => { 50 | expect(typeof instance.dispatch).toBe('function') 51 | }) 52 | 53 | it('should invoke added listener with provided arguments', () => { 54 | let data = { a: 'b' } 55 | instance.add(bar) 56 | instance.dispatch(data) 57 | expect(bar).toHaveBeenLastCalledWith(data) 58 | }) 59 | }) 60 | 61 | describe('destroy()', () => { 62 | it('should be a function', () => { 63 | expect(typeof instance.destroy).toBe('function') 64 | }) 65 | 66 | it('should remove all listeners', () => { 67 | expect(foo).toHaveBeenCalledTimes(2) 68 | expect(bar).toHaveBeenCalledTimes(1) 69 | expect(baz).toHaveBeenCalledTimes(0) 70 | 71 | instance.add(foo) 72 | instance.add(baz) 73 | instance.dispatch() 74 | 75 | expect(foo).toHaveBeenCalledTimes(3) 76 | expect(bar).toHaveBeenCalledTimes(2) 77 | expect(baz).toHaveBeenCalledTimes(1) 78 | 79 | instance.destroy() 80 | instance.dispatch() 81 | 82 | expect(foo).toHaveBeenCalledTimes(3) 83 | expect(bar).toHaveBeenCalledTimes(2) 84 | expect(baz).toHaveBeenCalledTimes(1) 85 | }) 86 | }) 87 | }) 88 | --------------------------------------------------------------------------------