diff --git a/src/browser.js b/src/browser.js index ca18b6f..1f206dc 100644 --- a/src/browser.js +++ b/src/browser.js @@ -1,4 +1,4 @@ -import createDebug from './createDebug' +import init from './init' import common from './common' /** @@ -21,174 +21,179 @@ function safeStringify (content) { }) } -export default { - ...common, - /** - * Colors. - */ - 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' - ], - - /** - * Colorize log arguments if enabled. - * - * NOTE: only 'this' in formatArgs is scoped in 'debug' instance, - * not in 'createDebug' scope. - * - * @api public - */ - formatArgs (args) { - const cTag = this.useColors ? '%c' : '' - args[0] = - cTag + this.namespace + ' ' + - cTag + args[0] + ' ' + - cTag + '+' + createDebug.humanize(this.diff) - - if (!this.useColors) return - const currentStyle = `color: ${this.color}` - args.splice(1, 0, currentStyle, '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 +export function inject (createDebug) { + return { + ...common(createDebug), + + /** + * Colors. + */ + 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' + ], + + /** + * Colorize log arguments if enabled. + * + * NOTE: only 'this' in formatArgs is scoped in 'debug' instance, + * not in 'createDebug' scope. + * + * @api public + */ + formatArgs (args) { + const cTag = this.useColors ? '%c' : '' + args[0] = + cTag + this.namespace + ' ' + + cTag + args[0] + ' ' + + cTag + '+' + createDebug.humanize(this.diff) + + if (!this.useColors) return + const currentStyle = `color: ${this.color}` + args.splice(1, 0, currentStyle, '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, currentStyle) + }, + + /** + * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. + */ + formatters: { + j (value) { + try { + return safeStringify(value) + } catch (error) { + return `[UnexpectedJSONParseError]: ${error.message}` + } } - }) - args.splice(lastC, 0, currentStyle) - }, - - /** - * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. - */ - formatters: { - j (value) { + }, + + /** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + load () { + let namespaces try { - return safeStringify(value) - } catch (error) { - return `[UnexpectedJSONParseError]: ${error.message}` - } - } - }, - - /** - * Load `namespaces`. - * - * @return {String} returns the previously persisted debug modes - * @api private - */ - load () { - let namespaces - try { - namespaces = createDebug.storage.debug - } catch (error) {} - - // If debug isn't set in LS, and we're in Electron/nwjs, try to load $DEBUG - if (!namespaces && typeof process !== 'undefined' && 'env' in process) { - namespaces = process.env.DEBUG - } + namespaces = createDebug.storage.debug + } catch (error) {} - return namespaces - }, - - /** - * Invokes `console.log()` when available. - * No-op when `console.log` is not a "function". - * - * @api public - */ - /* eslint-disable no-console */ - log () { - return ( - typeof console === 'object' && - console.log && - Function.prototype.apply.call(console.log, console, arguments) - ) - }, - /* eslint-enable no-console */ - - /** - * Save `namespaces`. - * - * @param {String} namespaces - * @api private - */ - save (namespaces) { - try { - if (namespaces == null) { - createDebug.storage.removeItem('debug') - } else { - createDebug.storage.debug = namespaces + // If debug isn't set in LS, and we're in Electron/nwjs, try to load $DEBUG + if (!namespaces && typeof process !== 'undefined' && 'env' in process) { + namespaces = process.env.DEBUG } - } catch (error) {} - }, - - /** - * LocalStorage attempts to return the LocalStorage. - * - * This is necessary because safari throws - * when a user disables cookies/LocalStorage - * and you attempt to access it. - * - * @return {LocalStorage|null} - * @api private - */ - storage: (() => { - try { - return window.localStorage - } catch (error) {} - })(), - - /** - * TODO: add a `localStorage` variable to explicitly enable/disable colors - */ - 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 - - // check userAgent - if (typeof navigator !== 'undefined' && navigator.userAgent) { - const UA = navigator.userAgent.toLowerCase() - - // Internet Explorer do not support colors. - if (UA.match(/trident\/\d+/)) return false - - // Microsoft Edge < 16.16215 do not support colors. - if (UA.match(/edge\/(\d+)\.(\d+)/)) { - return (parseInt(RegExp.$1, 10) >= 16) && (parseInt(RegExp.$2, 10) >= 16215) - } - - // is firefox >= v31? - // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages - if (UA.match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) return true - // double check webkit in userAgent just in case we are in a worker - if (UA.match(/applewebkit\/\d+/)) return true + return namespaces + }, + + /** + * Invokes `console.log()` when available. + * No-op when `console.log` is not a "function". + * + * @api public + */ + /* eslint-disable no-console */ + log () { + return ( + typeof console === 'object' && + console.log && + Function.prototype.apply.call(console.log, console, arguments) + ) + }, + /* eslint-enable no-console */ + + /** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + save (namespaces) { + try { + if (namespaces == null) { + createDebug.storage.removeItem('debug') + } else { + createDebug.storage.debug = namespaces + } + } catch (error) {} + }, + + /** + * LocalStorage attempts to return the LocalStorage. + * + * This is necessary because safari throws + * when a user disables cookies/LocalStorage + * and you attempt to access it. + * + * @return {LocalStorage|null} + * @api private + */ + storage: (() => { + try { + return window.localStorage + } catch (error) {} + })(), + + /** + * TODO: add a `localStorage` variable to explicitly enable/disable colors + */ + 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 + + // check userAgent + if (typeof navigator !== 'undefined' && navigator.userAgent) { + const UA = navigator.userAgent.toLowerCase() + + // Internet Explorer do not support colors. + if (UA.match(/trident\/\d+/)) return false + + // Microsoft Edge < 16.16215 do not support colors. + if (UA.match(/edge\/(\d+)\.(\d+)/)) { + return (parseInt(RegExp.$1, 10) >= 16) && (parseInt(RegExp.$2, 10) >= 16215) + } + + // is firefox >= v31? + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages + if (UA.match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) return true + + // double check webkit in userAgent just in case we are in a worker + if (UA.match(/applewebkit\/\d+/)) return true + } + return false } - return false } } + +export default () => init(inject) diff --git a/src/common.js b/src/common.js index 45e02d8..cdee434 100644 --- a/src/common.js +++ b/src/common.js @@ -1,8 +1,6 @@ -import humanize from 'ms' +import ms from 'ms' -import createDebug from './createDebug' - -export default { +export default createDebug => ({ colors: [], /** @@ -23,7 +21,7 @@ export default { */ formatters: {}, - humanize, + humanize: ms, /** * Coerce `value`. @@ -58,7 +56,6 @@ export default { createDebug.names = [] createDebug.skips = [] - ;(typeof namespaces === 'string' ? namespaces : '') .split(/[\s,]+/) .forEach(item => { @@ -101,9 +98,9 @@ export default { selectColor (namespace) { let hash = 0 for (let i in namespace) { - hash = ((hash << 5) - hash) + namespace.charCodeAt(i) + hash = (hash << 5) - hash + namespace.charCodeAt(i) hash |= 0 // Convert to 32bit integer } return createDebug.colors[Math.abs(hash) % createDebug.colors.length] } -} +}) diff --git a/src/createDebug.js b/src/createDebug.js index 6c7f553..4316ef3 100644 --- a/src/createDebug.js +++ b/src/createDebug.js @@ -1,23 +1,24 @@ import newInstance from './debug' -export default function createDebug (namespace) { - const newDebug = Object.assign(newInstance(createDebug), { - namespace, - enabled: createDebug.enabler(namespace), - useColors: createDebug.useColors(), - color: createDebug.selectColor(namespace), - destroy: () => { - const index = createDebug.instances.indexOf(newDebug) - if (index !== -1) { - createDebug.instances.splice(index, 1) - return true +export default function () { + return function createDebug (namespace) { + const newDebug = Object.assign(newInstance(createDebug), { + namespace, + enabled: createDebug.enabler(namespace), + useColors: createDebug.useColors(), + color: createDebug.selectColor(namespace), + destroy: () => { + const index = createDebug.instances.indexOf(newDebug) + if (index !== -1) { + createDebug.instances.splice(index, 1) + return true + } + return false } - return false - } - }) + }) - // typeof this.injectInstance === 'function' && this.injectInstance(newDebug) - createDebug.instances.push(newDebug) + createDebug.instances.push(newDebug) - return newDebug + return newDebug + } } diff --git a/src/index.js b/src/index.js index 4f611f4..8b7a70b 100644 --- a/src/index.js +++ b/src/index.js @@ -1,11 +1,10 @@ -import createDebug from './createDebug' import browser from './browser' import node from './node' /** * Detect environment */ -const env = +const debug = // web browsers typeof process === 'undefined' || // Electron @@ -14,10 +13,7 @@ const env = process.__nwjs || // 'process' package process.browser - ? browser - : node - -const debug = Object.assign(createDebug, env) -debug.enable(debug.load()) + ? browser() + : node() export default debug diff --git a/src/init.js b/src/init.js new file mode 100644 index 0000000..c1c31a2 --- /dev/null +++ b/src/init.js @@ -0,0 +1,8 @@ +import createDebug from './createDebug' + +export default (inject) => { + const debug = createDebug() + Object.assign(debug, inject(debug)) + debug.enable(debug.load()) + return debug +} diff --git a/src/node.js b/src/node.js index 755d078..b44c381 100644 --- a/src/node.js +++ b/src/node.js @@ -2,141 +2,146 @@ import tty from 'tty' import util from 'util' import supportsColor from 'supports-color' -import createDebug from './createDebug' +import init from './init' import common from './common' -export default { - ...common, - - /** - * Colors. - */ - colors: (() => { - try { - if (supportsColor.stderr && supportsColor.stderr.has256) { - return [ - 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 - ] +export function inject (createDebug) { + return { + ...common(createDebug), + + /** + * Colors. + */ + colors: (() => { + try { + if (supportsColor.stderr && supportsColor.stderr.has256) { + return [ + 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) {} + + return [ 6, 2, 3, 4, 5, 1 ] + })(), + + /** + * Adds ANSI color escape codes if enabled. + * + * NOTE: only 'this' in formatArgs is scoped in 'debug' instance, + * not in 'createDebug' scope. + * + * @api public + */ + formatArgs (args) { + const name = this.namespace + + if (this.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+${createDebug.humanize(this.diff)}\u001b[0m`) + } else { + const date = createDebug.inspectOpts.hideDate + ? '' + : new Date().toISOString() + ' ' + args[0] = `${date}${name} ${args[0]}` } - } catch (error) {} - - return [ 6, 2, 3, 4, 5, 1 ] - })(), - - /** - * Adds ANSI color escape codes if enabled. - * - * NOTE: only 'this' in formatArgs is scoped in 'debug' instance, - * not in 'createDebug' scope. - * - * @api public - */ - formatArgs (args) { - const name = this.namespace - - if (this.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+${createDebug.humanize(this.diff)}\u001b[0m`) - } else { - const date = createDebug.inspectOpts.hideDate - ? '' - : new Date().toISOString() + ' ' - args[0] = `${date}${name} ${args[0]}` - } - }, + }, + + formatters: { + /** + * Map %o to `util.inspect()`, all on a single line. + */ + o (value) { + createDebug.inspectOpts.colors = createDebug.useColors + return util.inspect(value, createDebug.inspectOpts) + .replace(/\s*\n\s*/g, ' ') + }, + /** + * Map %O to `util.inspect()`, allowing multiple lines if needed. + */ + O (value) { + createDebug.inspectOpts.colors = createDebug.useColors + return util.inspect(value, createDebug.inspectOpts) + } + }, + + /** + * Build up the default `inspectOpts` object from the environment variables. + * + * $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js + */ + inspectOpts: Object.keys(process.env) + .filter(key => /^debug_/i.test(key)) + .reduce((obj, key) => { + // camel-case + const prop = key + .substring(6) + .toLowerCase() + .replace(/_([a-z])/g, (_, k) => k.toUpperCase()) + + // coerce string value into JS value + let value = process.env[key] + if (/^(yes|on|true|enabled)$/i.test(value)) value = true + else if (/^(no|off|false|disabled)$/i.test(value)) value = false + else if (value === 'null') value = null + else value = Number(value) + + obj[prop] = value + return obj + }, {}), - formatters: { /** - * Map %o to `util.inspect()`, all on a single line. + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private */ - o (value) { - createDebug.inspectOpts.colors = createDebug.useColors - return util.inspect(value, createDebug.inspectOpts) - .replace(/\s*\n\s*/g, ' ') + load () { + return process.env.DEBUG }, + /** - * Map %O to `util.inspect()`, allowing multiple lines if needed. + * Invokes `util.format()` with the specified arguments and writes to stderr. */ - O (value) { - createDebug.inspectOpts.colors = createDebug.useColors - return util.inspect(value, createDebug.inspectOpts) - } - }, - - /** - * Build up the default `inspectOpts` object from the environment variables. - * - * $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js - */ - inspectOpts: Object.keys(process.env) - .filter(key => /^debug_/i.test(key)) - .reduce((obj, key) => { - // camel-case - const prop = key - .substring(6) - .toLowerCase() - .replace(/_([a-z])/g, (_, k) => k.toUpperCase()) - - // coerce string value into JS value - let value = process.env[key] - if (/^(yes|on|true|enabled)$/i.test(value)) value = true - else if (/^(no|off|false|disabled)$/i.test(value)) value = false - else if (value === 'null') value = null - else value = Number(value) - - obj[prop] = value - return obj - }, {}), - - /** - * Load `namespaces`. - * - * @return {String} returns the previously persisted debug modes - * @api private - */ - load () { - return process.env.DEBUG - }, - - /** - * Invokes `util.format()` with the specified arguments and writes to stderr. - */ - - log () { - return process.stderr.write(util.format.apply(util, arguments) + '\n') - }, - - /** - * Save `namespaces`. - * - * @param {String} namespaces - * @api private - */ - - save (namespaces) { - if (namespaces == null) { - // 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 + + log () { + return process.stderr.write(util.format.apply(util, arguments) + '\n') + }, + + /** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + + save (namespaces) { + if (namespaces == null) { + // 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 + } + }, + + /** + * Is stdout a TTY? Colored output is enabled when `true`. + */ + useColors () { + return 'colors' in this.inspectOpts + ? Boolean(this.inspectOpts.colors) + : tty.isatty(process.stderr.fd) } - }, - - /** - * Is stdout a TTY? Colored output is enabled when `true`. - */ - useColors () { - return 'colors' in this.inspectOpts - ? Boolean(this.inspectOpts.colors) - : tty.isatty(process.stderr.fd) } } + +export default () => init(inject)