|
|
@ -95,227 +95,249 @@ var error = exports.error = function(x) { |
|
|
|
* output. Default is false (no coloring). |
|
|
|
*/ |
|
|
|
function inspect(obj, showHidden, depth, colors) { |
|
|
|
var seen = []; |
|
|
|
|
|
|
|
var stylize = function(str, styleType) { |
|
|
|
// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
|
|
|
|
var styles = |
|
|
|
{ 'bold' : [1, 22], |
|
|
|
'italic' : [3, 23], |
|
|
|
'underline' : [4, 24], |
|
|
|
'inverse' : [7, 27], |
|
|
|
'white' : [37, 39], |
|
|
|
'grey' : [90, 39], |
|
|
|
'black' : [30, 39], |
|
|
|
'blue' : [34, 39], |
|
|
|
'cyan' : [36, 39], |
|
|
|
'green' : [32, 39], |
|
|
|
'magenta' : [35, 39], |
|
|
|
'red' : [31, 39], |
|
|
|
'yellow' : [33, 39] }; |
|
|
|
|
|
|
|
var style = |
|
|
|
{ 'special': 'cyan', |
|
|
|
'number': 'blue', |
|
|
|
'boolean': 'yellow', |
|
|
|
'undefined': 'grey', |
|
|
|
'null': 'bold', |
|
|
|
'string': 'green', |
|
|
|
'date': 'magenta', |
|
|
|
// "name": intentionally not styling
|
|
|
|
'regexp': 'red' }[styleType]; |
|
|
|
|
|
|
|
if (style) { |
|
|
|
return '\033[' + styles[style][0] + 'm' + str + |
|
|
|
'\033[' + styles[style][1] + 'm'; |
|
|
|
} else { |
|
|
|
return str; |
|
|
|
} |
|
|
|
var ctx = { |
|
|
|
showHidden: showHidden, |
|
|
|
seen: [], |
|
|
|
stylize: colors ? stylizeWithColor : stylizeNoColor, |
|
|
|
}; |
|
|
|
if (! colors) { |
|
|
|
stylize = function(str, styleType) { return str; }; |
|
|
|
} |
|
|
|
return formatValue(ctx, obj, (typeof depth === 'undefined' ? 2 : depth)); |
|
|
|
} |
|
|
|
exports.inspect = inspect; |
|
|
|
|
|
|
|
function format(value, recurseTimes) { |
|
|
|
// Provide a hook for user-specified inspect functions.
|
|
|
|
// Check that value is an object with an inspect function on it
|
|
|
|
if (value && typeof value.inspect === 'function' && |
|
|
|
// Filter out the util module, it's inspect function is special
|
|
|
|
value !== exports && |
|
|
|
// Also filter out any prototype objects using the circular check.
|
|
|
|
!(value.constructor && value.constructor.prototype === value)) { |
|
|
|
return value.inspect(recurseTimes); |
|
|
|
} |
|
|
|
|
|
|
|
// Primitive types cannot have properties
|
|
|
|
switch (typeof value) { |
|
|
|
case 'undefined': |
|
|
|
return stylize('undefined', 'undefined'); |
|
|
|
// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
|
|
|
|
var colors = { |
|
|
|
'bold' : [1, 22], |
|
|
|
'italic' : [3, 23], |
|
|
|
'underline' : [4, 24], |
|
|
|
'inverse' : [7, 27], |
|
|
|
'white' : [37, 39], |
|
|
|
'grey' : [90, 39], |
|
|
|
'black' : [30, 39], |
|
|
|
'blue' : [34, 39], |
|
|
|
'cyan' : [36, 39], |
|
|
|
'green' : [32, 39], |
|
|
|
'magenta' : [35, 39], |
|
|
|
'red' : [31, 39], |
|
|
|
'yellow' : [33, 39] |
|
|
|
}; |
|
|
|
|
|
|
|
case 'string': |
|
|
|
var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') |
|
|
|
.replace(/'/g, "\\'") |
|
|
|
.replace(/\\"/g, '"') + '\''; |
|
|
|
return stylize(simple, 'string'); |
|
|
|
var styles = { |
|
|
|
'special': 'cyan', |
|
|
|
'number': 'blue', |
|
|
|
'boolean': 'yellow', |
|
|
|
'undefined': 'grey', |
|
|
|
'null': 'bold', |
|
|
|
'string': 'green', |
|
|
|
'date': 'magenta', |
|
|
|
// "name": intentionally not styling
|
|
|
|
'regexp': 'red' |
|
|
|
}; |
|
|
|
|
|
|
|
case 'number': |
|
|
|
return stylize('' + value, 'number'); |
|
|
|
|
|
|
|
case 'boolean': |
|
|
|
return stylize('' + value, 'boolean'); |
|
|
|
} |
|
|
|
// For some reason typeof null is "object", so special case here.
|
|
|
|
if (value === null) { |
|
|
|
return stylize('null', 'null'); |
|
|
|
} |
|
|
|
function stylizeWithColor(str, styleType) { |
|
|
|
var style = styles[styleType]; |
|
|
|
|
|
|
|
// Look up the keys of the object.
|
|
|
|
var visible_keys = Object.keys(value); |
|
|
|
var keys = showHidden ? Object.getOwnPropertyNames(value) : visible_keys; |
|
|
|
if (style) { |
|
|
|
return '\033[' + colors[style][0] + 'm' + str + |
|
|
|
'\033[' + colors[style][1] + 'm'; |
|
|
|
} else { |
|
|
|
return str; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Functions without properties can be shortcutted.
|
|
|
|
if (typeof value === 'function' && keys.length === 0) { |
|
|
|
var name = value.name ? ': ' + value.name : ''; |
|
|
|
return stylize('[Function' + name + ']', 'special'); |
|
|
|
} |
|
|
|
|
|
|
|
// RegExp without properties can be shortcutted
|
|
|
|
if (isRegExp(value) && keys.length === 0) { |
|
|
|
return stylize('' + value, 'regexp'); |
|
|
|
} |
|
|
|
function stylizeNoColor(str, styleType) { |
|
|
|
return str; |
|
|
|
} |
|
|
|
|
|
|
|
// Dates without properties can be shortcutted
|
|
|
|
if (isDate(value) && keys.length === 0) { |
|
|
|
return stylize(value.toUTCString(), 'date'); |
|
|
|
} |
|
|
|
|
|
|
|
var base, type, braces; |
|
|
|
// Determine the object type
|
|
|
|
if (isArray(value)) { |
|
|
|
type = 'Array'; |
|
|
|
braces = ['[', ']']; |
|
|
|
} else { |
|
|
|
type = 'Object'; |
|
|
|
braces = ['{', '}']; |
|
|
|
} |
|
|
|
function formatValue(ctx, value, recurseTimes) { |
|
|
|
// Provide a hook for user-specified inspect functions.
|
|
|
|
// Check that value is an object with an inspect function on it
|
|
|
|
if (value && typeof value.inspect === 'function' && |
|
|
|
// Filter out the util module, it's inspect function is special
|
|
|
|
value !== exports && |
|
|
|
// Also filter out any prototype objects using the circular check.
|
|
|
|
!(value.constructor && value.constructor.prototype === value)) { |
|
|
|
return value.inspect(recurseTimes); |
|
|
|
} |
|
|
|
|
|
|
|
// Make functions say that they are functions
|
|
|
|
// Primitive types cannot have properties
|
|
|
|
var primitive = formatPrimitive(ctx, value); |
|
|
|
if (primitive) { |
|
|
|
return primitive; |
|
|
|
} |
|
|
|
|
|
|
|
// Look up the keys of the object.
|
|
|
|
var visibleKeys = Object.keys(value); |
|
|
|
var keys = ctx.showHidden ? Object.getOwnPropertyNames(value) : visibleKeys; |
|
|
|
|
|
|
|
// Some type of object without properties can be shortcutted.
|
|
|
|
if (keys.length === 0) { |
|
|
|
if (typeof value === 'function') { |
|
|
|
var n = value.name ? ': ' + value.name : ''; |
|
|
|
base = ' [Function' + n + ']'; |
|
|
|
} else { |
|
|
|
base = ''; |
|
|
|
var name = value.name ? ': ' + value.name : ''; |
|
|
|
return ctx.stylize('[Function' + name + ']', 'special'); |
|
|
|
} |
|
|
|
|
|
|
|
// Make RegExps say that they are RegExps
|
|
|
|
if (isRegExp(value)) { |
|
|
|
base = ' ' + value; |
|
|
|
return ctx.stylize('' + value, 'regexp'); |
|
|
|
} |
|
|
|
|
|
|
|
// Make dates with properties first say the date
|
|
|
|
if (isDate(value)) { |
|
|
|
base = ' ' + value.toUTCString(); |
|
|
|
return ctx.stylize(value.toUTCString(), 'date'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (keys.length === 0) { |
|
|
|
return braces[0] + base + braces[1]; |
|
|
|
} |
|
|
|
var base = '', array = false, braces = ['{', '}']; |
|
|
|
|
|
|
|
if (recurseTimes < 0) { |
|
|
|
if (isRegExp(value)) { |
|
|
|
return stylize('' + value, 'regexp'); |
|
|
|
} else { |
|
|
|
return stylize('[Object]', 'special'); |
|
|
|
} |
|
|
|
// Make Array say that they are Array
|
|
|
|
if (isArray(value)) { |
|
|
|
array = true; |
|
|
|
braces = ['[', ']']; |
|
|
|
} |
|
|
|
|
|
|
|
// Make functions say that they are functions
|
|
|
|
if (typeof value === 'function') { |
|
|
|
var n = value.name ? ': ' + value.name : ''; |
|
|
|
base = ' [Function' + n + ']'; |
|
|
|
} |
|
|
|
|
|
|
|
// Make RegExps say that they are RegExps
|
|
|
|
if (isRegExp(value)) { |
|
|
|
base = ' ' + value; |
|
|
|
} |
|
|
|
|
|
|
|
// Make dates with properties first say the date
|
|
|
|
if (isDate(value)) { |
|
|
|
base = ' ' + value.toUTCString(); |
|
|
|
} |
|
|
|
|
|
|
|
if (keys.length === 0) { |
|
|
|
return braces[0] + base + braces[1]; |
|
|
|
} |
|
|
|
|
|
|
|
if (recurseTimes < 0) { |
|
|
|
if (isRegExp(value)) { |
|
|
|
return ctx.stylize('' + value, 'regexp'); |
|
|
|
} else { |
|
|
|
return ctx.stylize('[Object]', 'special'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
seen.push(value); |
|
|
|
|
|
|
|
var output = keys.map(function(key) { |
|
|
|
var name, str; |
|
|
|
if (value.__lookupGetter__) { |
|
|
|
if (value.__lookupGetter__(key)) { |
|
|
|
if (value.__lookupSetter__(key)) { |
|
|
|
str = stylize('[Getter/Setter]', 'special'); |
|
|
|
} else { |
|
|
|
str = stylize('[Getter]', 'special'); |
|
|
|
} |
|
|
|
} else { |
|
|
|
if (value.__lookupSetter__(key)) { |
|
|
|
str = stylize('[Setter]', 'special'); |
|
|
|
} |
|
|
|
} |
|
|
|
ctx.seen.push(value); |
|
|
|
|
|
|
|
var output = keys.map(function(key) { |
|
|
|
return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); |
|
|
|
}); |
|
|
|
|
|
|
|
ctx.seen.pop(); |
|
|
|
|
|
|
|
return reduceToSingleString(output, base, braces); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function formatPrimitive(ctx, value) { |
|
|
|
switch (typeof value) { |
|
|
|
case 'undefined': |
|
|
|
return ctx.stylize('undefined', 'undefined'); |
|
|
|
|
|
|
|
case 'string': |
|
|
|
var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') |
|
|
|
.replace(/'/g, "\\'") |
|
|
|
.replace(/\\"/g, '"') + '\''; |
|
|
|
return ctx.stylize(simple, 'string'); |
|
|
|
|
|
|
|
case 'number': |
|
|
|
return ctx.stylize('' + value, 'number'); |
|
|
|
|
|
|
|
case 'boolean': |
|
|
|
return ctx.stylize('' + value, 'boolean'); |
|
|
|
} |
|
|
|
// For some reason typeof null is "object", so special case here.
|
|
|
|
if (value === null) { |
|
|
|
return ctx.stylize('null', 'null'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { |
|
|
|
var name, str; |
|
|
|
if (value.__lookupGetter__) { |
|
|
|
if (value.__lookupGetter__(key)) { |
|
|
|
if (value.__lookupSetter__(key)) { |
|
|
|
str = ctx.stylize('[Getter/Setter]', 'special'); |
|
|
|
} else { |
|
|
|
str = ctx.stylize('[Getter]', 'special'); |
|
|
|
} |
|
|
|
if (visible_keys.indexOf(key) < 0) { |
|
|
|
name = '[' + key + ']'; |
|
|
|
} else { |
|
|
|
if (value.__lookupSetter__(key)) { |
|
|
|
str = ctx.stylize('[Setter]', 'special'); |
|
|
|
} |
|
|
|
if (!str) { |
|
|
|
if (seen.indexOf(value[key]) < 0) { |
|
|
|
if (recurseTimes === null) { |
|
|
|
str = format(value[key]); |
|
|
|
} else { |
|
|
|
str = format(value[key], recurseTimes - 1); |
|
|
|
} |
|
|
|
if (str.indexOf('\n') > -1) { |
|
|
|
if (isArray(value)) { |
|
|
|
str = str.split('\n').map(function(line) { |
|
|
|
return ' ' + line; |
|
|
|
}).join('\n').substr(2); |
|
|
|
} else { |
|
|
|
str = '\n' + str.split('\n').map(function(line) { |
|
|
|
return ' ' + line; |
|
|
|
}).join('\n'); |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
str = stylize('[Circular]', 'special'); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (visibleKeys.indexOf(key) < 0) { |
|
|
|
name = '[' + key + ']'; |
|
|
|
} |
|
|
|
if (!str) { |
|
|
|
if (ctx.seen.indexOf(value[key]) < 0) { |
|
|
|
if (recurseTimes === null) { |
|
|
|
str = formatValue(ctx, value[key], null); |
|
|
|
} else { |
|
|
|
str = formatValue(ctx, value[key], recurseTimes - 1); |
|
|
|
} |
|
|
|
if (typeof name === 'undefined') { |
|
|
|
if (type === 'Array' && key.match(/^\d+$/)) { |
|
|
|
return str; |
|
|
|
} |
|
|
|
name = JSON.stringify('' + key); |
|
|
|
if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { |
|
|
|
name = name.substr(1, name.length - 2); |
|
|
|
name = stylize(name, 'name'); |
|
|
|
if (str.indexOf('\n') > -1) { |
|
|
|
if (array) { |
|
|
|
str = str.split('\n').map(function(line) { |
|
|
|
return ' ' + line; |
|
|
|
}).join('\n').substr(2); |
|
|
|
} else { |
|
|
|
name = name.replace(/'/g, "\\'") |
|
|
|
.replace(/\\"/g, '"') |
|
|
|
.replace(/(^"|"$)/g, "'"); |
|
|
|
name = stylize(name, 'string'); |
|
|
|
str = '\n' + str.split('\n').map(function(line) { |
|
|
|
return ' ' + line; |
|
|
|
}).join('\n'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return name + ': ' + str; |
|
|
|
}); |
|
|
|
|
|
|
|
seen.pop(); |
|
|
|
|
|
|
|
var numLinesEst = 0; |
|
|
|
var length = output.reduce(function(prev, cur) { |
|
|
|
numLinesEst++; |
|
|
|
if (cur.indexOf('\n') >= 0) numLinesEst++; |
|
|
|
return prev + cur.length + 1; |
|
|
|
}, 0); |
|
|
|
|
|
|
|
if (length > (require('readline').columns || 50)) { |
|
|
|
output = braces[0] + |
|
|
|
(base === '' ? '' : base + '\n ') + |
|
|
|
' ' + |
|
|
|
output.join(',\n ') + |
|
|
|
' ' + |
|
|
|
braces[1]; |
|
|
|
|
|
|
|
} else { |
|
|
|
output = braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; |
|
|
|
str = ctx.stylize('[Circular]', 'special'); |
|
|
|
} |
|
|
|
} |
|
|
|
if (typeof name === 'undefined') { |
|
|
|
if (array && key.match(/^\d+$/)) { |
|
|
|
return str; |
|
|
|
} |
|
|
|
name = JSON.stringify('' + key); |
|
|
|
if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { |
|
|
|
name = name.substr(1, name.length - 2); |
|
|
|
name = ctx.stylize(name, 'name'); |
|
|
|
} else { |
|
|
|
name = name.replace(/'/g, "\\'") |
|
|
|
.replace(/\\"/g, '"') |
|
|
|
.replace(/(^"|"$)/g, "'"); |
|
|
|
name = ctx.stylize(name, 'string'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return output; |
|
|
|
return name + ': ' + str; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function reduceToSingleString(output, base, braces) { |
|
|
|
var numLinesEst = 0; |
|
|
|
var length = output.reduce(function(prev, cur) { |
|
|
|
numLinesEst++; |
|
|
|
if (cur.indexOf('\n') >= 0) numLinesEst++; |
|
|
|
return prev + cur.length + 1; |
|
|
|
}, 0); |
|
|
|
|
|
|
|
if (length > (require('readline').columns || 50)) { |
|
|
|
return braces[0] + |
|
|
|
(base === '' ? '' : base + '\n ') + |
|
|
|
' ' + |
|
|
|
output.join(',\n ') + |
|
|
|
' ' + |
|
|
|
braces[1]; |
|
|
|
} |
|
|
|
return format(obj, (typeof depth === 'undefined' ? 2 : depth)); |
|
|
|
|
|
|
|
return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; |
|
|
|
} |
|
|
|
exports.inspect = inspect; |
|
|
|
|
|
|
|
|
|
|
|
function isArray(ar) { |
|
|
|