From ba8a424d41e9dc6129e081ac3aa9715be6a45fbd Mon Sep 17 00:00:00 2001 From: Josh Junon Date: Mon, 10 Sep 2018 22:04:31 -0600 Subject: [PATCH] move to XO (closes #397) --- examples/node/app.js | 23 +- examples/node/colors.js | 10 +- examples/node/stdout.js | 15 +- examples/node/wildcards.js | 14 +- examples/node/worker.js | 17 +- karma.conf.js | 124 +++++----- package.json | 3 + src/browser.js | 276 ++++++++++++++-------- src/common.js | 471 +++++++++++++++++++------------------ src/index.js | 5 +- src/node.js | 236 ++++++++++++------- test/debug-spec.js | 92 ++++++++ test/debug_spec.js | 93 -------- 13 files changed, 766 insertions(+), 613 deletions(-) create mode 100644 test/debug-spec.js delete mode 100644 test/debug_spec.js diff --git a/examples/node/app.js b/examples/node/app.js index d4a19914..08d3f257 100644 --- a/examples/node/app.js +++ b/examples/node/app.js @@ -1,19 +1,20 @@ +const http = require('http'); -var debug = require('../../')('http') - , http = require('http') - , name = 'My App'; +const debug = require('../..')('http'); -// fake app +const name = 'My App'; + +// Fake app debug('booting %o', name); -http.createServer(function(req, res){ - debug(req.method + ' ' + req.url); - res.end('hello\n'); -}).listen(3000, function(){ - debug('listening'); +http.createServer((req, res) => { + debug(req.method + ' ' + req.url); + res.end('hello\n'); +}).listen(3000, () => { + debug('listening'); }); -// fake worker of some kind - +// Fake worker of some kind +// eslint-disable-next-line import/no-unassigned-import require('./worker'); diff --git a/examples/node/colors.js b/examples/node/colors.js index c144ee4e..9cbfa1f9 100644 --- a/examples/node/colors.js +++ b/examples/node/colors.js @@ -1,8 +1,8 @@ -var debug = require('../../') +const debug = require('../..'); -debug.enable('*') +debug.enable('*'); -for (var i=0; i < debug.colors.length; i++) { - const d = debug('example:' + i); - d('The color is %o', d.color); +for (let i = 0; i < debug.colors.length; i++) { + const d = debug('example:' + i); + d('The color is %o', d.color); } diff --git a/examples/node/stdout.js b/examples/node/stdout.js index c999c4c0..17558da0 100644 --- a/examples/node/stdout.js +++ b/examples/node/stdout.js @@ -1,16 +1,17 @@ -var debug = require('../../'); -var error = debug('app:error'); +const debug = require('../..'); -// by default stderr is used +const error = debug('app:error'); + +// By default stderr is used error('goes to stderr!'); -var log = debug('app:log'); -// set this namespace to log via console.log -log.log = console.log.bind(console); // don't forget to bind to console! +const log = debug('app:log'); +// Set this namespace to log via console.log +log.log = console.log.bind(console); // Don't forget to bind to console! log('goes to stdout'); error('still goes to stderr!'); -// set all output to go via console.info +// Set all output to go via console.info // overrides all per-namespace log settings debug.log = console.info.bind(console); error('now goes to stdout via console.info'); diff --git a/examples/node/wildcards.js b/examples/node/wildcards.js index ca99aeff..2cedc2d8 100644 --- a/examples/node/wildcards.js +++ b/examples/node/wildcards.js @@ -1,10 +1,10 @@ -var debug = { - foo: require('../../')('test:foo'), - bar: require('../../')('test:bar'), - baz: require('../../')('test:baz') +const debug = { + foo: require('../..')('test:foo'), + bar: require('../..')('test:bar'), + baz: require('../..')('test:baz') }; -debug.foo('foo') -debug.bar('bar') -debug.baz('baz') +debug.foo('foo'); +debug.bar('bar'); +debug.baz('baz'); diff --git a/examples/node/worker.js b/examples/node/worker.js index 07e3bc59..6f483a66 100644 --- a/examples/node/worker.js +++ b/examples/node/worker.js @@ -4,23 +4,24 @@ // DEBUG=worker:a node example/worker // DEBUG=worker:b node example/worker -var a = require('../../')('worker:a') - , b = require('../../')('worker:b'); +const a = require('../..')('worker:a'); + +const b = require('../..')('worker:b'); function work() { - a('doing lots of uninteresting work'); - setTimeout(work, Math.random() * 1000); + a('doing lots of uninteresting work'); + setTimeout(work, Math.random() * 1000); } work(); function workb() { - b('doing some work'); - setTimeout(workb, Math.random() * 2000); + b('doing some work'); + setTimeout(workb, Math.random() * 2000); } workb(); -setTimeout(function(){ - b(new Error('fail')); +setTimeout(() => { + b(new Error('fail')); }, 5000); diff --git a/karma.conf.js b/karma.conf.js index 103a82d1..82f90691 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -1,70 +1,60 @@ // Karma configuration // Generated on Fri Dec 16 2016 13:09:51 GMT+0000 (UTC) -module.exports = function(config) { - config.set({ - - // base path that will be used to resolve all patterns (eg. files, exclude) - basePath: '', - - - // frameworks to use - // available frameworks: https://npmjs.org/browse/keyword/karma-adapter - frameworks: ['mocha', 'chai', 'sinon'], - - - // list of files / patterns to load in the browser - files: [ - 'dist/debug.js', - 'test/*spec.js' - ], - - - // list of files to exclude - exclude: [ - 'src/node.js' - ], - - - // preprocess matching files before serving them to the browser - // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor - preprocessors: { - }, - - // test results reporter to use - // possible values: 'dots', 'progress' - // available reporters: https://npmjs.org/browse/keyword/karma-reporter - reporters: ['progress'], - - - // web server port - port: 9876, - - - // enable / disable colors in the output (reporters and logs) - colors: true, - - - // level of logging - // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG - logLevel: config.LOG_INFO, - - - // enable / disable watching file and executing tests whenever any file changes - autoWatch: true, - - - // start these browsers - // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher - browsers: ['PhantomJS'], - - - // Continuous Integration mode - // if true, Karma captures browsers, runs the tests and exits - singleRun: false, - - // Concurrency level - // how many browser should be started simultaneous - concurrency: Infinity - }) -} +module.exports = function (config) { + config.set({ + + // Base path that will be used to resolve all patterns (eg. files, exclude) + basePath: '', + + // Frameworks to use + // available frameworks: https://npmjs.org/browse/keyword/karma-adapter + frameworks: ['mocha', 'chai', 'sinon'], + + // List of files / patterns to load in the browser + files: [ + 'dist/debug.js', + 'test/*spec.js' + ], + + // List of files to exclude + exclude: [ + 'src/node.js' + ], + + // Preprocess matching files before serving them to the browser + // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor + preprocessors: { + }, + + // Test results reporter to use + // possible values: 'dots', 'progress' + // available reporters: https://npmjs.org/browse/keyword/karma-reporter + reporters: ['progress'], + + // Web server port + port: 9876, + + // Enable / disable colors in the output (reporters and logs) + colors: true, + + // Level of logging + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + logLevel: config.LOG_INFO, + + // Enable / disable watching file and executing tests whenever any file changes + autoWatch: true, + + // Start these browsers + // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher + browsers: ['PhantomJS'], + + // Continuous Integration mode + // if true, Karma captures browsers, runs the tests and exits + singleRun: false, + + // Concurrency level + // how many browser should be started simultaneous + concurrency: Infinity + }); +}; diff --git a/package.json b/package.json index 4e922381..1f9b3640 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,9 @@ "url": "git://github.com/visionmedia/debug.git" }, "description": "small debugging utility", + "scripts": { + "test": "xo && mocha" + }, "keywords": [ "debug", "log", diff --git a/src/browser.js b/src/browser.js index b6d94e17..5f34c0d0 100644 --- a/src/browser.js +++ b/src/browser.js @@ -1,3 +1,5 @@ +/* eslint-env browser */ + /** * This is the web browser implementation of `debug()`. */ @@ -14,17 +16,82 @@ exports.storage = localstorage(); */ exports.colors = [ - '#0000CC', '#0000FF', '#0033CC', '#0033FF', '#0066CC', '#0066FF', '#0099CC', - '#0099FF', '#00CC00', '#00CC33', '#00CC66', '#00CC99', '#00CCCC', '#00CCFF', - '#3300CC', '#3300FF', '#3333CC', '#3333FF', '#3366CC', '#3366FF', '#3399CC', - '#3399FF', '#33CC00', '#33CC33', '#33CC66', '#33CC99', '#33CCCC', '#33CCFF', - '#6600CC', '#6600FF', '#6633CC', '#6633FF', '#66CC00', '#66CC33', '#9900CC', - '#9900FF', '#9933CC', '#9933FF', '#99CC00', '#99CC33', '#CC0000', '#CC0033', - '#CC0066', '#CC0099', '#CC00CC', '#CC00FF', '#CC3300', '#CC3333', '#CC3366', - '#CC3399', '#CC33CC', '#CC33FF', '#CC6600', '#CC6633', '#CC9900', '#CC9933', - '#CCCC00', '#CCCC33', '#FF0000', '#FF0033', '#FF0066', '#FF0099', '#FF00CC', - '#FF00FF', '#FF3300', '#FF3333', '#FF3366', '#FF3399', '#FF33CC', '#FF33FF', - '#FF6600', '#FF6633', '#FF9900', '#FF9933', '#FFCC00', '#FFCC33' + '#0000CC', + '#0000FF', + '#0033CC', + '#0033FF', + '#0066CC', + '#0066FF', + '#0099CC', + '#0099FF', + '#00CC00', + '#00CC33', + '#00CC66', + '#00CC99', + '#00CCCC', + '#00CCFF', + '#3300CC', + '#3300FF', + '#3333CC', + '#3333FF', + '#3366CC', + '#3366FF', + '#3399CC', + '#3399FF', + '#33CC00', + '#33CC33', + '#33CC66', + '#33CC99', + '#33CCCC', + '#33CCFF', + '#6600CC', + '#6600FF', + '#6633CC', + '#6633FF', + '#66CC00', + '#66CC33', + '#9900CC', + '#9900FF', + '#9933CC', + '#9933FF', + '#99CC00', + '#99CC33', + '#CC0000', + '#CC0033', + '#CC0066', + '#CC0099', + '#CC00CC', + '#CC00FF', + '#CC3300', + '#CC3333', + '#CC3366', + '#CC3399', + '#CC33CC', + '#CC33FF', + '#CC6600', + '#CC6633', + '#CC9900', + '#CC9933', + '#CCCC00', + '#CCCC33', + '#FF0000', + '#FF0033', + '#FF0066', + '#FF0099', + '#FF00CC', + '#FF00FF', + '#FF3300', + '#FF3333', + '#FF3366', + '#FF3399', + '#FF33CC', + '#FF33FF', + '#FF6600', + '#FF6633', + '#FF9900', + '#FF9933', + '#FFCC00', + '#FFCC33' ]; /** @@ -35,29 +102,30 @@ exports.colors = [ * TODO: add a `localStorage` variable to explicitly enable/disable colors */ +// eslint-disable-next-line complexity function useColors() { - // NB: In an Electron preload script, document will be defined but not fully - // initialized. Since we know we're in Chrome, we'll just detect this case - // explicitly - if (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) { - return true; - } - - // Internet Explorer and Edge do not support colors. - if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) { - return false; - } - - // is webkit? http://stackoverflow.com/a/16459606/376773 - // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 - return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) || - // is firebug? http://stackoverflow.com/a/398120/376773 - (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) || - // is firefox >= v31? - // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages - (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) || - // double check webkit in userAgent just in case we are in a worker - (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)); + // NB: In an Electron preload script, document will be defined but not fully + // initialized. Since we know we're in Chrome, we'll just detect this case + // explicitly + if (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) { + return true; + } + + // Internet Explorer and Edge do not support colors. + if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) { + return false; + } + + // Is webkit? http://stackoverflow.com/a/16459606/376773 + // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 + return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) || + // Is firebug? http://stackoverflow.com/a/398120/376773 + (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) || + // Is firefox >= v31? + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages + (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) || + // Double check webkit in userAgent just in case we are in a worker + (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)); } /** @@ -67,36 +135,38 @@ function useColors() { */ function formatArgs(args) { - var useColors = this.useColors; - - args[0] = (useColors ? '%c' : '') - + this.namespace - + (useColors ? ' %c' : ' ') - + args[0] - + (useColors ? '%c ' : ' ') - + '+' + module.exports.humanize(this.diff); - - if (!useColors) return; - - var c = 'color: ' + this.color; - args.splice(1, 0, c, 'color: inherit') - - // the final "%c" is somewhat tricky, because there could be other - // arguments passed either before or after the %c, so we need to - // figure out the correct index to insert the CSS into - var index = 0; - var lastC = 0; - args[0].replace(/%[a-zA-Z%]/g, function(match) { - if ('%%' === match) return; - index++; - if ('%c' === match) { - // we only are interested in the *last* %c - // (the user may have provided their own) - lastC = index; - } - }); - - args.splice(lastC, 0, c); + args[0] = (this.useColors ? '%c' : '') + + this.namespace + + (this.useColors ? ' %c' : ' ') + + args[0] + + (this.useColors ? '%c ' : ' ') + + '+' + module.exports.humanize(this.diff); + + if (!this.useColors) { + return; + } + + const c = 'color: ' + this.color; + args.splice(1, 0, c, 'color: inherit'); + + // The final "%c" is somewhat tricky, because there could be other + // arguments passed either before or after the %c, so we need to + // figure out the correct index to insert the CSS into + let index = 0; + let lastC = 0; + args[0].replace(/%[a-zA-Z%]/g, match => { + if (match === '%%') { + return; + } + index++; + if (match === '%c') { + // We only are interested in the *last* %c + // (the user may have provided their own) + lastC = index; + } + }); + + args.splice(lastC, 0, c); } /** @@ -105,13 +175,12 @@ function formatArgs(args) { * * @api public */ - -function log() { - // this hackery is required for IE8/9, where - // the `console.log` function doesn't have 'apply' - return 'object' === typeof console - && console.log - && Function.prototype.apply.call(console.log, console, arguments); +function log(...args) { + // This hackery is required for IE8/9, where + // the `console.log` function doesn't have 'apply' + return typeof console === 'object' && + console.log && + console.log(...args); } /** @@ -120,15 +189,17 @@ function log() { * @param {String} namespaces * @api private */ - function save(namespaces) { - try { - if (null == namespaces) { - exports.storage.removeItem('debug'); - } else { - exports.storage.setItem('debug', namespaces); - } - } catch(e) {} + try { + if (namespaces) { + exports.storage.setItem('debug', namespaces); + } else { + exports.storage.removeItem('debug'); + } + } catch (error) { + // Swallow + // XXX (@Qix-) should we be logging these? + } } /** @@ -137,19 +208,21 @@ function save(namespaces) { * @return {String} returns the previously persisted debug modes * @api private */ - function load() { - var r; - try { - r = exports.storage.getItem('debug'); - } catch(e) {} + let r; + try { + r = exports.storage.getItem('debug'); + } catch (error) { + // Swallow + // XXX (@Qix-) should we be logging these? + } - // If debug isn't set in LS, and we're in Electron, try to load $DEBUG - if (!r && typeof process !== 'undefined' && 'env' in process) { - r = process.env.DEBUG; - } + // If debug isn't set in LS, and we're in Electron, try to load $DEBUG + if (!r && typeof process !== 'undefined' && 'env' in process) { + r = process.env.DEBUG; + } - return r; + return r; } /** @@ -164,25 +237,28 @@ function load() { */ function localstorage() { - try { - // TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context - // The Browser also has localStorage in the global context. - return localStorage; - } catch (e) {} + try { + // TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context + // The Browser also has localStorage in the global context. + return localStorage; + } catch (error) { + // Swallow + // XXX (@Qix-) should we be logging these? + } } module.exports = require('./common')(exports); -var formatters = module.exports.formatters; +const {formatters} = module.exports; /** * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. */ -formatters.j = function(v) { - try { - return JSON.stringify(v); - } catch (err) { - return '[UnexpectedJSONParseError]: ' + err.message; - } +formatters.j = function (v) { + try { + return JSON.stringify(v); + } catch (error) { + return '[UnexpectedJSONParseError]: ' + error.message; + } }; diff --git a/src/common.js b/src/common.js index 65713e4d..dab9e480 100644 --- a/src/common.js +++ b/src/common.js @@ -4,237 +4,242 @@ * implementations of `debug()`. */ -module.exports = function setup(env) { - createDebug.debug = createDebug['default'] = createDebug; - createDebug.coerce = coerce; - createDebug.disable = disable; - createDebug.enable = enable; - createDebug.enabled = enabled; - createDebug.humanize = require('ms'); - - Object.keys(env).forEach(function(key) { - createDebug[key] = env[key]; - }); - - /** - * Active `debug` instances. - */ - createDebug.instances = []; - - /** - * The currently active debug mode names, and names to skip. - */ - - createDebug.names = []; - createDebug.skips = []; - - /** - * Map of special "%n" handling functions, for the debug "format" argument. - * - * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". - */ - - createDebug.formatters = {}; - - /** - * Select a color. - * @param {String} namespace - * @return {Number} - * @api private - */ - - function selectColor(namespace) { - var hash = 0, i; - - for (i in namespace) { - hash = ((hash << 5) - hash) + namespace.charCodeAt(i); - hash |= 0; // Convert to 32bit integer - } - - return createDebug.colors[Math.abs(hash) % createDebug.colors.length]; - } - createDebug.selectColor = selectColor; - - /** - * Create a debugger with the given `namespace`. - * - * @param {String} namespace - * @return {Function} - * @api public - */ - - function createDebug(namespace) { - var prevTime; - - function debug() { - // disabled? - if (!debug.enabled) return; - - var self = debug; - - // set `diff` timestamp - var curr = +new Date(); - var ms = curr - (prevTime || curr); - self.diff = ms; - self.prev = prevTime; - self.curr = curr; - prevTime = curr; - - // turn the `arguments` into a proper Array - var args = new Array(arguments.length); - for (var i = 0; i < args.length; i++) { - args[i] = arguments[i]; - } - - args[0] = createDebug.coerce(args[0]); - - if ('string' !== typeof args[0]) { - // anything else let's inspect with %O - args.unshift('%O'); - } - - // apply any `formatters` transformations - var index = 0; - args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) { - // if we encounter an escaped % then don't increase the array index - if (match === '%%') return match; - index++; - var formatter = createDebug.formatters[format]; - if ('function' === typeof formatter) { - var val = args[index]; - match = formatter.call(self, val); - - // now we need to remove `args[index]` since it's inlined in the `format` - args.splice(index, 1); - index--; - } - return match; - }); - - // apply env-specific formatting (colors, etc.) - createDebug.formatArgs.call(self, args); - - var logFn = self.log || createDebug.log; - logFn.apply(self, args); - } - - debug.namespace = namespace; - debug.enabled = createDebug.enabled(namespace); - debug.useColors = createDebug.useColors(); - debug.color = selectColor(namespace); - debug.destroy = destroy; - debug.extend = extend; - //debug.formatArgs = formatArgs; - //debug.rawLog = rawLog; - - // env-specific initialization logic for debug instances - if ('function' === typeof createDebug.init) { - createDebug.init(debug); - } - - createDebug.instances.push(debug); - - return debug; - } - - function destroy () { - var index = createDebug.instances.indexOf(this); - if (index !== -1) { - createDebug.instances.splice(index, 1); - return true; - } else { - return false; - } - } - - function extend (namespace, delimiter) { - return createDebug(this.namespace + (typeof delimiter !== 'undefined' ? delimiter : ':') + namespace); - } - - /** - * Enables a debug mode by namespaces. This can include modes - * separated by a colon and wildcards. - * - * @param {String} namespaces - * @api public - */ - - function enable(namespaces) { - createDebug.save(namespaces); - - createDebug.names = []; - createDebug.skips = []; - - var i; - var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); - var len = split.length; - - for (i = 0; i < len; i++) { - if (!split[i]) continue; // ignore empty strings - namespaces = split[i].replace(/\*/g, '.*?'); - if (namespaces[0] === '-') { - createDebug.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); - } else { - createDebug.names.push(new RegExp('^' + namespaces + '$')); - } - } - - for (i = 0; i < createDebug.instances.length; i++) { - var instance = createDebug.instances[i]; - instance.enabled = createDebug.enabled(instance.namespace); - } - } - - /** - * Disable debug output. - * - * @api public - */ - - function disable() { - createDebug.enable(''); - } - - /** - * Returns true if the given mode name is enabled, false otherwise. - * - * @param {String} name - * @return {Boolean} - * @api public - */ - - function enabled(name) { - if (name[name.length - 1] === '*') { - return true; - } - var i, len; - for (i = 0, len = createDebug.skips.length; i < len; i++) { - if (createDebug.skips[i].test(name)) { - return false; - } - } - for (i = 0, len = createDebug.names.length; i < len; i++) { - if (createDebug.names[i].test(name)) { - return true; - } - } - return false; - } - - /** - * Coerce `val`. - * - * @param {Mixed} val - * @return {Mixed} - * @api private - */ - - function coerce(val) { - if (val instanceof Error) return val.stack || val.message; - return val; - } - - createDebug.enable(createDebug.load()); - - return createDebug; +function setup(env) { + createDebug.debug = createDebug; + createDebug.default = createDebug; + createDebug.coerce = coerce; + createDebug.disable = disable; + createDebug.enable = enable; + createDebug.enabled = enabled; + createDebug.humanize = require('ms'); + + Object.keys(env).forEach(key => { + createDebug[key] = env[key]; + }); + + /** + * Active `debug` instances. + */ + createDebug.instances = []; + + /** + * The currently active debug mode names, and names to skip. + */ + + createDebug.names = []; + createDebug.skips = []; + + /** + * Map of special "%n" handling functions, for the debug "format" argument. + * + * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". + */ + createDebug.formatters = {}; + + /** + * Selects a color for a debug namespace + * @param {String} namespace The namespace string for the for the debug instance to be colored + * @return {Number|String} An ANSI color code for the given namespace + * @api private + */ + function selectColor(namespace) { + let hash = 0; + + for (let i = 0; i < namespace.length; i++) { + hash = ((hash << 5) - hash) + namespace.charCodeAt(i); + hash |= 0; // Convert to 32bit integer + } + + return createDebug.colors[Math.abs(hash) % createDebug.colors.length]; + } + createDebug.selectColor = selectColor; + + /** + * Create a debugger with the given `namespace`. + * + * @param {String} namespace + * @return {Function} + * @api public + */ + function createDebug(namespace) { + let prevTime; + + function debug(...args) { + // Disabled? + if (!debug.enabled) { + return; + } + + const self = debug; + + // Set `diff` timestamp + const curr = Number(new Date()); + const ms = curr - (prevTime || curr); + self.diff = ms; + self.prev = prevTime; + self.curr = curr; + prevTime = curr; + + args[0] = createDebug.coerce(args[0]); + + if (typeof args[0] !== 'string') { + // Anything else let's inspect with %O + args.unshift('%O'); + } + + // Apply any `formatters` transformations + let index = 0; + args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => { + // If we encounter an escaped % then don't increase the array index + if (match === '%%') { + return match; + } + index++; + const formatter = createDebug.formatters[format]; + if (typeof formatter === 'function') { + const val = args[index]; + match = formatter.call(self, val); + + // Now we need to remove `args[index]` since it's inlined in the `format` + args.splice(index, 1); + index--; + } + return match; + }); + + // Apply env-specific formatting (colors, etc.) + createDebug.formatArgs.call(self, args); + + const logFn = self.log || createDebug.log; + logFn.apply(self, args); + } + + debug.namespace = namespace; + debug.enabled = createDebug.enabled(namespace); + debug.useColors = createDebug.useColors(); + debug.color = selectColor(namespace); + debug.destroy = destroy; + debug.extend = extend; + // Debug.formatArgs = formatArgs; + // debug.rawLog = rawLog; + + // env-specific initialization logic for debug instances + if (typeof createDebug.init === 'function') { + createDebug.init(debug); + } + + createDebug.instances.push(debug); + + return debug; + } + + function destroy() { + const index = createDebug.instances.indexOf(this); + if (index !== -1) { + createDebug.instances.splice(index, 1); + return true; + } + return false; + } + + function extend(namespace, delimiter) { + return createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace); + } + + /** + * Enables a debug mode by namespaces. This can include modes + * separated by a colon and wildcards. + * + * @param {String} namespaces + * @api public + */ + function enable(namespaces) { + createDebug.save(namespaces); + + createDebug.names = []; + createDebug.skips = []; + + let i; + const split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); + const len = split.length; + + for (i = 0; i < len; i++) { + if (!split[i]) { + // ignore empty strings + continue; + } + + namespaces = split[i].replace(/\*/g, '.*?'); + + if (namespaces[0] === '-') { + createDebug.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); + } else { + createDebug.names.push(new RegExp('^' + namespaces + '$')); + } + } + + for (i = 0; i < createDebug.instances.length; i++) { + const instance = createDebug.instances[i]; + instance.enabled = createDebug.enabled(instance.namespace); + } + } + + /** + * Disable debug output. + * + * @api public + */ + function disable() { + createDebug.enable(''); + } + + /** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + function enabled(name) { + if (name[name.length - 1] === '*') { + return true; + } + + let i; + let len; + + for (i = 0, len = createDebug.skips.length; i < len; i++) { + if (createDebug.skips[i].test(name)) { + return false; + } + } + + for (i = 0, len = createDebug.names.length; i < len; i++) { + if (createDebug.names[i].test(name)) { + return true; + } + } + + return false; + } + + /** + * Coerce `val`. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + function coerce(val) { + if (val instanceof Error) { + return val.stack || val.message; + } + return val; + } + + createDebug.enable(createDebug.load()); + + return createDebug; } + +module.exports = setup; diff --git a/src/index.js b/src/index.js index a8792cc1..bf4c57f2 100644 --- a/src/index.js +++ b/src/index.js @@ -3,9 +3,8 @@ * treat as a browser. */ - if (typeof process === 'undefined' || process.type === 'renderer' || process.browser === true || process.__nwjs) { - module.exports = require('./browser.js'); + module.exports = require('./browser.js'); } else { - module.exports = require('./node.js'); + module.exports = require('./node.js'); } diff --git a/src/node.js b/src/node.js index c13b932c..5e1f1541 100644 --- a/src/node.js +++ b/src/node.js @@ -2,8 +2,8 @@ * Module dependencies. */ -var tty = require('tty'); -var util = require('util'); +const tty = require('tty'); +const util = require('util'); /** * This is the Node.js implementation of `debug()`. @@ -20,21 +20,95 @@ exports.useColors = useColors; * Colors. */ -exports.colors = [ 6, 2, 3, 4, 5, 1 ]; +exports.colors = [6, 2, 3, 4, 5, 1]; try { - var supportsColor = require('supports-color'); - if (supportsColor && (supportsColor.stderr || supportsColor).level >= 2) { - exports.colors = [ - 20, 21, 26, 27, 32, 33, 38, 39, 40, 41, 42, 43, 44, 45, 56, 57, 62, 63, 68, - 69, 74, 75, 76, 77, 78, 79, 80, 81, 92, 93, 98, 99, 112, 113, 128, 129, 134, - 135, 148, 149, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, - 172, 173, 178, 179, 184, 185, 196, 197, 198, 199, 200, 201, 202, 203, 204, - 205, 206, 207, 208, 209, 214, 215, 220, 221 - ]; - } -} catch (err) { - // swallow - we only care if `supports-color` is available; it doesn't have to be. + // Optional dependency (as in, doesn't need to be installed, NOT like optionalDependencies in package.json) + // eslint-disable-next-line import/no-extraneous-dependencies + const supportsColor = require('supports-color'); + + if (supportsColor && (supportsColor.stderr || supportsColor).level >= 2) { + exports.colors = [ + 20, + 21, + 26, + 27, + 32, + 33, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 56, + 57, + 62, + 63, + 68, + 69, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 92, + 93, + 98, + 99, + 112, + 113, + 128, + 129, + 134, + 135, + 148, + 149, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 178, + 179, + 184, + 185, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 214, + 215, + 220, + 221 + ]; + } +} catch (error) { + // Swallow - we only care if `supports-color` is available; it doesn't have to be. } /** @@ -43,24 +117,31 @@ try { * $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js */ -exports.inspectOpts = Object.keys(process.env).filter(function (key) { - return /^debug_/i.test(key); -}).reduce(function (obj, key) { - // camel-case - var prop = key - .substring(6) - .toLowerCase() - .replace(/_([a-z])/g, function (_, k) { return k.toUpperCase() }); - - // coerce string value into JS value - var val = process.env[key]; - if (/^(yes|on|true|enabled)$/i.test(val)) val = true; - else if (/^(no|off|false|disabled)$/i.test(val)) val = false; - else if (val === 'null') val = null; - else val = Number(val); - - obj[prop] = val; - return obj; +exports.inspectOpts = Object.keys(process.env).filter(key => { + return /^debug_/i.test(key); +}).reduce((obj, key) => { + // Camel-case + const prop = key + .substring(6) + .toLowerCase() + .replace(/_([a-z])/g, (_, k) => { + return k.toUpperCase(); + }); + + // Coerce string value into JS value + let val = process.env[key]; + if (/^(yes|on|true|enabled)$/i.test(val)) { + val = true; + } else if (/^(no|off|false|disabled)$/i.test(val)) { + val = false; + } else if (val === 'null') { + val = null; + } else { + val = Number(val); + } + + obj[prop] = val; + return obj; }, {}); /** @@ -68,9 +149,9 @@ exports.inspectOpts = Object.keys(process.env).filter(function (key) { */ function useColors() { - return 'colors' in exports.inspectOpts - ? Boolean(exports.inspectOpts.colors) - : tty.isatty(process.stderr.fd); + return 'colors' in exports.inspectOpts ? + Boolean(exports.inspectOpts.colors) : + tty.isatty(process.stderr.fd); } /** @@ -80,35 +161,33 @@ function useColors() { */ function formatArgs(args) { - var name = this.namespace; - var useColors = this.useColors; - - if (useColors) { - var c = this.color; - var colorCode = '\u001b[3' + (c < 8 ? c : '8;5;' + c); - var prefix = ' ' + colorCode + ';1m' + name + ' ' + '\u001b[0m'; - - args[0] = prefix + args[0].split('\n').join('\n' + prefix); - args.push(colorCode + 'm+' + module.exports.humanize(this.diff) + '\u001b[0m'); - } else { - args[0] = getDate() + name + ' ' + args[0]; - } + const {namespace: name, useColors} = this; + + if (useColors) { + const c = this.color; + const colorCode = '\u001B[3' + (c < 8 ? c : '8;5;' + c); + const prefix = ` ${colorCode};1m${name} \u001B[0m`; + + args[0] = prefix + args[0].split('\n').join('\n' + prefix); + args.push(colorCode + 'm+' + module.exports.humanize(this.diff) + '\u001B[0m'); + } else { + args[0] = getDate() + name + ' ' + args[0]; + } } function getDate() { - if (exports.inspectOpts.hideDate) { - return ''; - } else { - return new Date().toISOString() + ' '; - } + if (exports.inspectOpts.hideDate) { + return ''; + } + return new Date().toISOString() + ' '; } /** * Invokes `util.format()` with the specified arguments and writes to stderr. */ -function log() { - return process.stderr.write(util.format.apply(util, arguments) + '\n'); +function log(...args) { + return process.stderr.write(util.format(...args) + '\n'); } /** @@ -117,15 +196,14 @@ function log() { * @param {String} namespaces * @api private */ - function save(namespaces) { - if (null == namespaces) { - // If you set a process.env field to null or undefined, it gets cast to the - // string 'null' or 'undefined'. Just delete instead. - delete process.env.DEBUG; - } else { - process.env.DEBUG = namespaces; - } + if (namespaces) { + process.env.DEBUG = namespaces; + } else { + // If you set a process.env field to null or undefined, it gets cast to the + // string 'null' or 'undefined'. Just delete instead. + delete process.env.DEBUG; + } } /** @@ -136,7 +214,7 @@ function save(namespaces) { */ function load() { - return process.env.DEBUG; + return process.env.DEBUG; } /** @@ -146,34 +224,34 @@ function load() { * differently for a particular `debug` instance. */ -function init (debug) { - debug.inspectOpts = {}; +function init(debug) { + debug.inspectOpts = {}; - var keys = Object.keys(exports.inspectOpts); - for (var i = 0; i < keys.length; i++) { - debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]]; - } + const keys = Object.keys(exports.inspectOpts); + for (let i = 0; i < keys.length; i++) { + debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]]; + } } module.exports = require('./common')(exports); -var formatters = module.exports.formatters; +const {formatters} = module.exports; /** * Map %o to `util.inspect()`, all on a single line. */ -formatters.o = function(v) { - this.inspectOpts.colors = this.useColors; - return util.inspect(v, this.inspectOpts) - .replace(/\s*\n\s*/g, ' '); +formatters.o = function (v) { + this.inspectOpts.colors = this.useColors; + return util.inspect(v, this.inspectOpts) + .replace(/\s*\n\s*/g, ' '); }; /** * Map %O to `util.inspect()`, allowing multiple lines if needed. */ -formatters.O = function(v) { - this.inspectOpts.colors = this.useColors; - return util.inspect(v, this.inspectOpts); +formatters.O = function (v) { + this.inspectOpts.colors = this.useColors; + return util.inspect(v, this.inspectOpts); }; diff --git a/test/debug-spec.js b/test/debug-spec.js new file mode 100644 index 00000000..08edb3b3 --- /dev/null +++ b/test/debug-spec.js @@ -0,0 +1,92 @@ +/* global describe, it, context, beforeEach */ +'use strict'; + +let chai; + +let expect; + +let debug; + +let sinon; + +let sinonChai; + +if (typeof module !== 'undefined') { + chai = require('chai'); + expect = chai.expect; + + debug = require('../src'); + sinon = require('sinon'); + sinonChai = require('sinon-chai'); + chai.use(sinonChai); +} + +describe('debug', () => { + let log = debug('test'); + + log.log = sinon.stub(); + + it('passes a basic sanity check', () => { + expect(log('hello world')).to.not.throw(); + }); + + it('allows namespaces to be a non-string value', () => { + expect(debug.enable(true)).to.not.throw(); + }); + + context('with log function', () => { + beforeEach(() => { + debug.enable('test'); + log = debug('test'); + }); + + it('uses it', () => { + log.log = sinon.stub(); + log('using custom log function'); + + expect(log.log).to.have.been.calledOnce(); + }); + }); + + describe('custom functions', () => { + let log; + + beforeEach(() => { + debug.enable('test'); + log = debug('test'); + }); + + context('with log function', () => { + it('uses it', () => { + log.log = sinon.spy(); + log('using custom log function'); + + expect(log.log).to.have.been.calledOnce(); + }); + }); + }); + + describe('extend namespace', () => { + let log; + + beforeEach(() => { + debug.enable('foo'); + log = debug('foo'); + }); + + it('should extend namespace', () => { + const logBar = log.extend('bar'); + expect(logBar.namespace).to.be.equal('foo:bar'); + }); + + it('should extend namespace with custom delimiter', () => { + const logBar = log.extend('bar', '--'); + expect(logBar.namespace).to.be.equal('foo--bar'); + }); + + it('should extend namespace with empty delimiter', () => { + const logBar = log.extend('bar', ''); + expect(logBar.namespace).to.be.equal('foobar'); + }); + }); +}); diff --git a/test/debug_spec.js b/test/debug_spec.js deleted file mode 100644 index 7ac6d7cd..00000000 --- a/test/debug_spec.js +++ /dev/null @@ -1,93 +0,0 @@ -/* global describe, it, context, beforeEach */ -'use strict'; - -var chai - , expect - , debug - , sinon - , sinonChai; - -if (typeof module !== 'undefined') { - chai = require('chai'); - expect = chai.expect; - - debug = require('../src/index'); - sinon = require('sinon'); - sinonChai = require("sinon-chai"); - chai.use(sinonChai); -} - - -describe('debug', function () { - var log = debug('test'); - - log.log = sinon.stub(); - - it('passes a basic sanity check', function () { - expect(log('hello world')).to.not.throw; - }); - - it('allows namespaces to be a non-string value', function () { - expect(debug.enable(true)).to.not.throw; - }); - - context('with log function', function () { - - beforeEach(function () { - debug.enable('test'); - log = debug('test'); - }); - - it('uses it', function () { - log.log = sinon.stub(); - log('using custom log function'); - - expect(log.log).to.have.been.calledOnce; - }); - }); - - describe('custom functions', function () { - var log; - - beforeEach(function () { - debug.enable('test'); - log = debug('test'); - }); - - context('with log function', function () { - it('uses it', function () { - log.log = sinon.spy(); - log('using custom log function'); - - expect(log.log).to.have.been.calledOnce; - }); - }); - }); - - - describe('extend namespace', function () { - var log; - - beforeEach(function () { - debug.enable('foo'); - log = debug('foo'); - }); - - it('should extend namespace', function () { - var logBar = log.extend('bar'); - expect(logBar.namespace).to.be.equal('foo:bar'); - }); - - it('should extend namespace with custom delimiter', function () { - var logBar = log.extend('bar', '--'); - expect(logBar.namespace).to.be.equal('foo--bar'); - }); - - it('should extend namespace with empty delimiter', function () { - var logBar = log.extend('bar', ''); - expect(logBar.namespace).to.be.equal('foobar'); - }); - - }); - -});