├── .gitignore ├── .npmrc ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── lib ├── copy.js ├── debug.js ├── help.js ├── log.js ├── logger.js ├── perf.js └── storage.js ├── package.json └── test └── logger_test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ 3 | tmp/ 4 | dist/ 5 | npm-debug.log* 6 | .DS_Store 7 | .nyc_output 8 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | node_js: 2 | - 'node' 3 | - 'lts/*' 4 | dist: xenial 5 | language: node_js 6 | script: 7 | - 'npm run test' 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Yoshua Wuyts 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 | # choo-devtools [![stability][0]][1] 2 | [![npm version][2]][3] [![build status][4]][5] 3 | [![downloads][8]][9] [![js-standard-style][10]][11] 4 | 5 | Console devtools for Choo. Useful for inspecting the state of applications, 6 | tuning performance, and quick iteration. No installation needed means it works 7 | in all browsers and Electron! :sparkles: 8 | 9 | ## Usage 10 | ```js 11 | var choo = require('choo') 12 | 13 | var app = choo() 14 | if (process.env.NODE_ENV !== 'production') { 15 | app.use(require('choo-devtools')()) 16 | } 17 | app.mount('body') 18 | ``` 19 | 20 | ## API 21 | ### `var devtoolStore = require('choo-devtools')(opts)` 22 | This module exposes a function that return a choo store. The function accept 23 | a single option argument, with the following properties: 24 | 25 | - **filter**: A function to filter events for the devtools logger. The filter 26 | function accept three arguments `eventName, data, timing` and should return `true` 27 | (the event will be logged) or `false` (the event wont be logged). 28 | 29 | ## Commands 30 | ### `choo.help` 31 | Print out information about all commands. 32 | ```txt 33 | ❯ choo.help 34 | ``` 35 | 36 | ### `choo.state` 37 | Log out the Choo state object. 38 | ```txt 39 | ❯ choo.state 40 | ``` 41 | 42 | ### `choo.debug` 43 | Log all state modificiations using 44 | [object-change-callsite](https://github.com/yoshuawuyts/object-change-callsite/). 45 | Logs out the key, value, and stack trace for the change. Useful when you're 46 | dealing with unexpected values in your state. Especially useful when enabling 47 | asynchronous stack traces in the devtools. 48 | ```txt 49 | // Enable debugging 50 | ❯ choo.debug 51 | 52 | // Disable debugging 53 | ❯ choo.debug = false 54 | ``` 55 | 56 | ### `choo.storage` 57 | Log out information about the browser's storage capabilities. 58 | ```txt 59 | ❯ choo.storage 60 | ``` 61 | 62 | ### `choo.log` 63 | Log out the last 150 events that occured in Choo. Useful during debugging to 64 | quickly figure out which sequences of events were responsible for the current 65 | state. 66 | ```txt 67 | ❯ choo.log 68 | ``` 69 | 70 | To enable `state` snapshots on each event, call `choo.debug`. Be warned that 71 | this may severely impact performance – it's recommended to only use this for 72 | debugging state. 73 | 74 | ### `choo.copy([selector])` 75 | Serialize the current state to JSON and copy it to the clipboard. Can be passed 76 | a selector (such as `href`) to do a partial copy. Useful if you want to create 77 | a test based on the current application state. 78 | ```txt 79 | // Copy all of state. 80 | ❯ choo.copy() 81 | 82 | // Copy `state.href`. 83 | ❯ choo.copy('href') 84 | ``` 85 | 86 | ### `choo.routes` 87 | Get an array with the registered routes. 88 | ```txt 89 | ❯ choo.routes 90 | ``` 91 | 92 | ### `choo.perf[type]` 93 | Log out performance metrics for Choo. There are different types of entries we 94 | can show: 95 | - `choo.perf.all` Log out all data. 96 | - `choo.perf.core` Only log out data about Choo's built-in methods. 97 | - `choo.perf.events` Only log out information about the event bus. 98 | - `choo.perf.components` Only log out information about components. 99 | 100 | ## License 101 | [MIT](https://tldrlegal.com/license/mit-license) 102 | 103 | [0]: https://img.shields.io/badge/stability-experimental-orange.svg?style=flat-square 104 | [1]: https://nodejs.org/api/documentation.html#documentation_stability_index 105 | [2]: https://img.shields.io/npm/v/choo-devtools.svg?style=flat-square 106 | [3]: https://npmjs.org/package/choo-devtools 107 | [4]: https://img.shields.io/travis/choojs/choo-devtools/master.svg?style=flat-square 108 | [5]: https://travis-ci.org/choojs/choo-devtools 109 | [6]: https://img.shields.io/codecov/c/github/choojs/choo-devtools/master.svg?style=flat-square 110 | [7]: https://codecov.io/github/choojs/choo-devtools 111 | [8]: http://img.shields.io/npm/dm/choo-devtools.svg?style=flat-square 112 | [9]: https://npmjs.org/package/choo-devtools 113 | [10]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square 114 | [11]: https://github.com/feross/standard 115 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var EventEmitter = require('events').EventEmitter 2 | 3 | var storage = require('./lib/storage') 4 | var logger = require('./lib/logger') 5 | var debug = require('./lib/debug') 6 | var copy = require('./lib/copy') 7 | var help = require('./lib/help') 8 | var perf = require('./lib/perf') 9 | var log = require('./lib/log') 10 | var getAllRoutes = require('wayfarer/get-all-routes') 11 | 12 | module.exports = expose 13 | 14 | function expose (opts) { 15 | opts = opts || {} 16 | store.storeName = 'choo-devtools' 17 | return store 18 | function store (state, emitter, app) { 19 | var localEmitter = new EventEmitter() 20 | 21 | if (typeof window !== 'undefined') { 22 | logger(state, emitter, opts) 23 | } 24 | 25 | emitter.on('DOMContentLoaded', function () { 26 | if (typeof window === 'undefined') return 27 | window.choo = {} 28 | 29 | window.choo.state = state 30 | window.choo.emit = function () { 31 | emitter.emit.apply(emitter, arguments) 32 | } 33 | window.choo.on = function (eventName, listener) { 34 | emitter.on(eventName, listener) 35 | } 36 | 37 | debug(state, emitter, app, localEmitter) 38 | 39 | log(state, emitter, app, localEmitter) 40 | perf(state, emitter, app, localEmitter) 41 | window.choo.copy = copy 42 | if (app.router && app.router.router) { 43 | window.choo.routes = Object.keys(getAllRoutes(app.router.router)) 44 | } 45 | 46 | storage() 47 | help() 48 | }) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/copy.js: -------------------------------------------------------------------------------- 1 | var stateCopy = require('state-copy') 2 | var pluck = require('plucker') 3 | 4 | module.exports = copy 5 | 6 | function copy (state) { 7 | var isStateString = state && typeof state === 'string' 8 | var isChooPath = isStateString && arguments.length === 1 && state.indexOf('state.') === 0 9 | 10 | if (!state || typeof state === 'function') state = window.choo.state 11 | if (isChooPath) [].push.call(arguments, { state: window.choo.state }) 12 | 13 | stateCopy(isStateString ? pluck.apply(this, arguments) : state) 14 | } 15 | -------------------------------------------------------------------------------- /lib/debug.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable node/no-deprecated-api */ 2 | var onChange = require('object-change-callsite') 3 | var nanologger = require('nanologger') 4 | var assert = require('assert') 5 | 6 | var enabledMessage = 'Debugging enabled. To disable run: `choo.debug = false`' 7 | var disabledMessage = 'Debugging disabled. We hope it was helpful! 🙌' 8 | 9 | module.exports = debug 10 | 11 | function debug (state, emitter, app, localEmitter) { 12 | var log = nanologger('choo-devtools') 13 | var enabled = window.localStorage.logLevel === 'debug' 14 | if (enabled) log.info(enabledMessage) 15 | 16 | state = onChange(state, function (attr, value, callsite) { 17 | if (!enabled) return 18 | callsite = callsite.split('\n')[1].replace(/^ +/, '') 19 | log.info('state.' + attr, value, '\n' + callsite) 20 | }) 21 | 22 | app.state = state 23 | 24 | Object.defineProperty(window.choo, 'debug', { 25 | get: function () { 26 | window.localStorage.logLevel = 'debug' 27 | localEmitter.emit('debug', true) 28 | enabled = true 29 | return enabledMessage 30 | }, 31 | set: function (bool) { 32 | assert.equal(typeof bool, 'boolean', 'choo-devtools.debug: bool should be type boolean') 33 | window.localStorage.logLevel = bool ? 'debug' : 'info' 34 | enabled = bool 35 | localEmitter.emit('debug', enabled) 36 | if (enabled) log.info(enabledMessage) 37 | else log.info(disabledMessage) 38 | } 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /lib/help.js: -------------------------------------------------------------------------------- 1 | module.exports = help 2 | 3 | function help () { 4 | Object.defineProperty(window.choo, 'help', { 5 | get: get, 6 | set: noop 7 | }) 8 | 9 | function get () { 10 | setTimeout(function () { 11 | print('copy', 'Serialize the current state to the clipboard.') 12 | print('debug', 'Enable Choo debug mode.') 13 | print('emit', 'Emit an event in the Choo emitter.') 14 | print('help', 'Print usage information.') 15 | print('log', 'Print the last 150 events emitted.') 16 | print('on', 'Listen for an event in the Choo emitter.') 17 | print('once', 'Listen for an event once in the Choo emitter.') 18 | print('perf', 'Print out performance metrics') 19 | print('state', 'Print the Choo state object.') 20 | print('storage', 'Print browser storage information.') 21 | }, 0) 22 | return 'Choo command overview' 23 | } 24 | } 25 | 26 | function print (cmd, desc) { 27 | var color = '#cc99cc' 28 | console.log(' %cchoo.' + cmd, 'color: ' + color, '— ' + desc) 29 | } 30 | 31 | function noop () {} 32 | -------------------------------------------------------------------------------- /lib/log.js: -------------------------------------------------------------------------------- 1 | var removeItems = require('remove-array-items') 2 | var scheduler = require('nanoscheduler')() 3 | var nanologger = require('nanologger') 4 | var _log = nanologger('choo') 5 | var clone = require('clone') 6 | 7 | var MAX_HISTORY_LENGTH = 150 // How many items we should keep around 8 | 9 | module.exports = log 10 | 11 | function log (state, emitter, app, localEmitter) { 12 | var shouldDebug = window.localStorage.logLevel === 'debug' 13 | var history = [] 14 | var i = 0 15 | var shouldWarn = true 16 | 17 | localEmitter.on('debug', function (bool) { 18 | shouldDebug = bool 19 | }) 20 | 21 | window.choo._history = history 22 | window.choo.history = showHistory 23 | 24 | Object.defineProperty(window.choo, 'log', { get: showHistory, set: noop }) 25 | Object.defineProperty(window.choo, 'history', { get: showHistory, set: noop }) 26 | 27 | emitter.on('*', function (name, data) { 28 | i += 1 29 | var entry = new Event(name, data, state) 30 | history.push(entry) 31 | scheduler.push(function () { 32 | var length = history.length 33 | if (length > MAX_HISTORY_LENGTH) { 34 | removeItems(history, 0, length - MAX_HISTORY_LENGTH) 35 | } 36 | }) 37 | }) 38 | 39 | function showHistory () { 40 | setTimeout(function () { 41 | console.table(history) 42 | }, 0) 43 | var events = i === 1 ? 'event' : 'events' 44 | var msg = i + ' ' + events + ' recorded, showing the last ' + MAX_HISTORY_LENGTH + '.' 45 | if (shouldDebug === false) { 46 | msg += ' Enable state capture by calling `choo.debug`.' 47 | } else { 48 | msg += ' Disable state capture by calling `choo.debug = false`.' 49 | } 50 | return msg 51 | } 52 | 53 | function Event (name, data, state) { 54 | this.name = name 55 | this.data = data === undefined ? '' : data 56 | this.state = shouldDebug 57 | ? tryClone(state) 58 | : '' 59 | } 60 | 61 | function tryClone (state) { 62 | try { 63 | var _state = clone(state) 64 | if (!shouldWarn) shouldWarn = true 65 | return _state 66 | } catch (ex) { 67 | if (shouldWarn) { 68 | _log.warn('Could not clone your app state. Make sure to have a serializable state so it can be cloned') 69 | shouldWarn = false 70 | } 71 | return '' 72 | } 73 | } 74 | } 75 | 76 | function noop () {} 77 | -------------------------------------------------------------------------------- /lib/logger.js: -------------------------------------------------------------------------------- 1 | var scheduler = require('nanoscheduler')() 2 | var nanologger = require('nanologger') 3 | var Hooks = require('choo-hooks') 4 | 5 | module.exports = logger 6 | 7 | function logger (state, emitter, opts) { 8 | var initialRender = true 9 | var hooks = Hooks(emitter) 10 | var log = nanologger('choo') 11 | 12 | hooks.on('log:debug', logger('debug')) 13 | hooks.on('log:info', logger('info')) 14 | hooks.on('log:warn', logger('warn')) 15 | hooks.on('log:error', logger('error')) 16 | hooks.on('log:fatal', logger('fatal')) 17 | 18 | hooks.on('event', function (eventName, data, timing) { 19 | if (opts.filter && !opts.filter(eventName, data, timing)) return 20 | 21 | if (timing) { 22 | var duration = timing.duration.toFixed() 23 | var level = duration < 50 ? 'info' : 'warn' 24 | if (data !== undefined) logger(level)(eventName, data, duration + 'ms') 25 | else logger(level)(eventName, duration + 'ms') 26 | } else { 27 | if (data !== undefined) logger('info')(eventName, data) 28 | else logger('info')(eventName) 29 | } 30 | }) 31 | 32 | hooks.on('unhandled', function (eventName, data) { 33 | logger('error')('No listeners for ' + eventName) 34 | }) 35 | 36 | hooks.on('DOMContentLoaded', function (timing) { 37 | if (!timing) return logger('info')('DOMContentLoaded') 38 | var level = timing.interactive < 1000 ? 'info' : 'warn' 39 | logger(level)('DOMContentLoaded', timing.interactive + 'ms to interactive') 40 | }) 41 | 42 | hooks.on('render', function (timings) { 43 | if (!timings || !timings.render) return logger('info')('render') 44 | var duration = timings.render.duration.toFixed() 45 | var msg = 'render' 46 | 47 | if (initialRender) { 48 | initialRender = false 49 | msg = 'initial ' + msg 50 | } 51 | 52 | // each frame has 10ms available for userland stuff 53 | var fps = Math.min((600 / duration).toFixed(), 60) 54 | 55 | if (fps === 60) { 56 | logger('info')(msg, fps + 'fps', duration + 'ms') 57 | } else { 58 | var times = { 59 | render: timings.render.duration.toFixed() + 'ms' 60 | } 61 | if (timings.morph) times.morph = timings.morph.duration.toFixed() + 'ms' 62 | logger('warn')(msg, fps + 'fps', duration + 'ms', times) 63 | } 64 | }) 65 | 66 | hooks.on('resource-timing-buffer-full', function () { 67 | logger('error')("The browser's Resource Resource timing buffer is full. Cannot store any more timing information") 68 | }) 69 | 70 | hooks.start() 71 | 72 | function logger (level) { 73 | return function () { 74 | var args = [] 75 | for (var i = 0, len = arguments.length; i < len; i++) { 76 | args.push(arguments[i]) 77 | } 78 | scheduler.push(function () { 79 | log[level].apply(log, args) 80 | }) 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lib/perf.js: -------------------------------------------------------------------------------- 1 | var onPerformance = require('on-performance') 2 | 3 | var BAR = '█' 4 | 5 | module.exports = perf 6 | 7 | function perf (state, emitter, app, localEmitter) { 8 | var stats = {} 9 | 10 | window.choo.perf = {} 11 | 12 | // Print all events 13 | var all = new Perf(stats, 'all') 14 | Object.defineProperty(window.choo.perf, 'all', { 15 | get: all.get.bind(all), 16 | set: noop 17 | }) 18 | 19 | // Print only Choo core events 20 | var core = new Perf(stats, 'core', function (name) { 21 | return /^choo/.test(name) 22 | }) 23 | Object.defineProperty(window.choo.perf, 'core', { 24 | get: core.get.bind(core), 25 | set: noop 26 | }) 27 | 28 | // Print component data 29 | var components = new Perf(stats, 'components', function (name) { 30 | return !/^choo/.test(name) && !/^bankai/.test(name) 31 | }) 32 | Object.defineProperty(window.choo.perf, 'components', { 33 | get: components.get.bind(components), 34 | set: noop 35 | }) 36 | 37 | // Print choo userland events (event emitter) 38 | var events = new Perf(stats, 'events', function (name) { 39 | return /^choo\.emit/.test(name) 40 | }, function (name) { 41 | return name.replace(/^choo\.emit\('/, '').replace(/'\)$/, '') 42 | }) 43 | Object.defineProperty(window.choo.perf, 'events', { 44 | get: events.get.bind(events), 45 | set: noop 46 | }) 47 | 48 | onPerformance(function (entry) { 49 | if (entry.entryType !== 'measure') return 50 | var name = entry.name.replace(/ .*$/, '') 51 | 52 | if (!stats[name]) { 53 | stats[name] = { 54 | name: name, 55 | count: 0, 56 | entries: [] 57 | } 58 | } 59 | 60 | var stat = stats[name] 61 | stat.count += 1 62 | stat.entries.push(entry.duration) 63 | }) 64 | } 65 | 66 | // Create a new Perf instance by passing it a filter 67 | function Perf (stats, name, filter, rename) { 68 | this.stats = stats 69 | this.name = name 70 | this.filter = filter || function () { return true } 71 | this.rename = rename || function (name) { return name } 72 | } 73 | 74 | // Compute a table of performance entries based on a filter 75 | Perf.prototype.get = function () { 76 | var filtered = Object.keys(this.stats).filter(this.filter) 77 | var self = this 78 | 79 | var maxTime = 0 80 | var maxMedian = 0 81 | var fmt = filtered.map(function (key) { 82 | var stat = self.stats[key] 83 | var totalTime = Number(stat.entries.reduce(function (time, entry) { 84 | return time + entry 85 | }, 0).toFixed(2)) 86 | if (totalTime > maxTime) maxTime = totalTime 87 | 88 | var median = getMedian(stat.entries) 89 | if (median > maxMedian) maxMedian = median 90 | 91 | var name = self.rename(stat.name) 92 | return new PerfEntry(name, totalTime, median, stat.count) 93 | }) 94 | 95 | var barLength = 10 96 | fmt.forEach(function (entry) { 97 | var totalTime = entry['Total Time (ms)'] 98 | var median = entry['Median (ms)'] 99 | entry[' '] = createBar(totalTime / maxTime * 100 / barLength) 100 | entry[' '] = createBar(median / maxMedian * 100 / barLength) 101 | }) 102 | 103 | function createBar (len) { 104 | var str = '' 105 | for (var i = 0, max = Math.round(len); i < max; i++) { 106 | str += BAR 107 | } 108 | return str 109 | } 110 | 111 | var res = fmt.sort(function (a, b) { 112 | return b['Total Time (ms)'] - a['Total Time (ms)'] 113 | }) 114 | console.table(res) 115 | return "Showing performance events for '" + this.name + "'" 116 | } 117 | 118 | // An entry for the performance timeline. 119 | function PerfEntry (name, totalTime, median, count) { 120 | this.Name = name 121 | this['Total Time (ms)'] = totalTime 122 | this[' '] = 0 123 | this['Median (ms)'] = median 124 | this[' '] = 0 125 | this['Total Count'] = count 126 | } 127 | 128 | // Get the median from an array of numbers. 129 | function getMedian (args) { 130 | if (!args.length) return 0 131 | var numbers = args.slice(0).sort(function (a, b) { return a - b }) 132 | var middle = Math.floor(numbers.length / 2) 133 | var isEven = numbers.length % 2 === 0 134 | var res = isEven ? (numbers[middle] + numbers[middle - 1]) / 2 : numbers[middle] 135 | return Number(res.toFixed(2)) 136 | } 137 | 138 | // Do nothing. 139 | function noop () {} 140 | -------------------------------------------------------------------------------- /lib/storage.js: -------------------------------------------------------------------------------- 1 | var pretty = require('prettier-bytes') 2 | 3 | module.exports = storage 4 | 5 | function storage () { 6 | Object.defineProperty(window.choo, 'storage', { 7 | get: get, 8 | set: noop 9 | }) 10 | 11 | function get () { 12 | if (navigator.storage) { 13 | navigator.storage.estimate().then(function (estimate) { 14 | var value = (estimate.usage / estimate.quota).toFixed() 15 | clr('Max storage:', fmt(estimate.quota)) 16 | clr('Storage used:', fmt(estimate.usage) + ' (' + value + '%)') 17 | navigator.storage.persisted().then(function (bool) { 18 | var val = bool ? 'enabled' : 'disabled' 19 | clr('Persistent storage:', val) 20 | }) 21 | }) 22 | return 'Calculating storage quota…' 23 | } else { 24 | var protocol = window.location.protocol 25 | return (/https/.test(protocol)) 26 | ? "The Storage API is unavailable in this browser. We're sorry!" 27 | : 'The Storage API is unavailable. Serving this site over HTTPS might help enable it!' 28 | } 29 | } 30 | } 31 | 32 | function clr (msg, arg) { 33 | var color = '#cc99cc' 34 | console.log('%c' + msg, 'color: ' + color, arg) 35 | } 36 | 37 | function fmt (num) { 38 | return pretty(num).replace(' ', '') 39 | } 40 | 41 | function noop () {} 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "choo-devtools", 3 | "description": "Console devtools for Choo", 4 | "repository": "choojs/choo-devtools", 5 | "version": "3.0.4", 6 | "scripts": { 7 | "deps": "dependency-check . && dependency-check . --extra --no-dev", 8 | "start": "node .", 9 | "test": "standard && npm run deps" 10 | }, 11 | "dependencies": { 12 | "choo-hooks": "^1.0.0", 13 | "clone": "^2.1.2", 14 | "nanologger": "^2.0.0", 15 | "nanoscheduler": "^1.0.0", 16 | "object-change-callsite": "^1.0.2", 17 | "on-performance": "^1.2.1", 18 | "plucker": "0.0.0", 19 | "prettier-bytes": "^1.0.4", 20 | "remove-array-items": "^2.0.0", 21 | "state-copy": "^1.0.5", 22 | "wayfarer": "^7.0.0" 23 | }, 24 | "devDependencies": { 25 | "choo": "^7.0.0", 26 | "dependency-check": "^2.8.0", 27 | "standard": "^14.3.1", 28 | "tape": "^4.11.0" 29 | }, 30 | "keywords": [ 31 | "choo", 32 | "expose", 33 | "window" 34 | ], 35 | "license": "MIT" 36 | } 37 | -------------------------------------------------------------------------------- /test/logger_test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape') 2 | var choo = require('choo') 3 | var devtools = require('../') 4 | 5 | test('Filtering out logs', function (t) { 6 | t.plan(1) 7 | 8 | var app = choo() 9 | app.use(devtools({ 10 | filter: function (eventName, data, timing) { 11 | if (eventName === 'foo') { 12 | t.pass('Calls filter function') 13 | return false 14 | } 15 | 16 | return true 17 | } 18 | })) 19 | 20 | app.route('*', function () { 21 | return 'Need to call `toString*()` for the app to start so need a route.' 22 | }) 23 | app.toString('/') 24 | 25 | app.emitter.emit('foo') 26 | }) 27 | --------------------------------------------------------------------------------