├── .gitignore ├── History.md ├── Readme.md ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 2 | 0.1.8 / 2014-02-14 3 | ================== 4 | 5 | * index: fix `document.all` type stuff 6 | 7 | 0.1.7 / 2014-02-14 8 | ================== 9 | 10 | * index: avoid double call to `Object.keys` 11 | 12 | 0.1.6 / 2014-02-14 13 | ================== 14 | 15 | * index: fix usage of `Array#reduce` for old browsers 16 | 17 | 0.1.5 / 2014-02-14 18 | ================== 19 | 20 | * index: fix usage of `indexOf` for older browsers 21 | 22 | 0.1.4 / 2014-02-14 23 | ================== 24 | 25 | * index: make `getOwnPropertyNames` and `getOwnPropertyDescriptor` 26 | optional for old browsers 27 | 28 | 0.1.3 / 2014-02-14 29 | ================== 30 | 31 | * index: add support for Array#map on older browsers 32 | 33 | 0.1.2 / 2014-02-14 34 | ================== 35 | 36 | * index: added support for Array#forEach on older browsers 37 | 38 | 0.1.1 / 2014-02-14 39 | ================== 40 | 41 | * index: added missing colors and styles 42 | 43 | 0.1.0 / 2014-02-14 44 | ================== 45 | 46 | * index: fix `_extend` usage 47 | 48 | 0.0.1 / 2014-02-14 49 | ================== 50 | 51 | * initial release 52 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | 2 | # util-inspect 3 | 4 | This is an extraction of Node's `inspect` utility from the `util` 5 | module, with two fundamental advantages: 6 | 7 | - Single, focused module 8 | - Compatible with all browsers and environments. 9 | 10 | ## How to use 11 | 12 | With browserify or node: 13 | 14 | ```js 15 | var inspect = require('util-inspect'); 16 | console.log(inspect({})); 17 | ``` 18 | 19 | ## License 20 | 21 | MIT – Copyright (c) 2010-2014 Joyent, Inc. 22 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var map = require('array-map'); 7 | var indexOf = require('indexof'); 8 | var isArray = require('isarray'); 9 | var forEach = require('foreach'); 10 | var reduce = require('array-reduce'); 11 | var getObjectKeys = require('object-keys'); 12 | var JSON = require('json3'); 13 | 14 | /** 15 | * Make sure `Object.keys` work for `undefined` 16 | * values that are still there, like `document.all`. 17 | * http://lists.w3.org/Archives/Public/public-html/2009Jun/0546.html 18 | * 19 | * @api private 20 | */ 21 | 22 | function objectKeys(val){ 23 | if (Object.keys) return Object.keys(val); 24 | return getObjectKeys(val); 25 | } 26 | 27 | /** 28 | * Module exports. 29 | */ 30 | 31 | module.exports = inspect; 32 | 33 | /** 34 | * Echos the value of a value. Trys to print the value out 35 | * in the best way possible given the different types. 36 | * 37 | * @param {Object} obj The object to print out. 38 | * @param {Object} opts Optional options object that alters the output. 39 | * @license MIT (© Joyent) 40 | */ 41 | /* legacy: obj, showHidden, depth, colors*/ 42 | 43 | function inspect(obj, opts) { 44 | // default options 45 | var ctx = { 46 | seen: [], 47 | stylize: stylizeNoColor 48 | }; 49 | // legacy... 50 | if (arguments.length >= 3) ctx.depth = arguments[2]; 51 | if (arguments.length >= 4) ctx.colors = arguments[3]; 52 | if (isBoolean(opts)) { 53 | // legacy... 54 | ctx.showHidden = opts; 55 | } else if (opts) { 56 | // got an "options" object 57 | _extend(ctx, opts); 58 | } 59 | // set default options 60 | if (isUndefined(ctx.showHidden)) ctx.showHidden = false; 61 | if (isUndefined(ctx.depth)) ctx.depth = 2; 62 | if (isUndefined(ctx.colors)) ctx.colors = false; 63 | if (isUndefined(ctx.customInspect)) ctx.customInspect = true; 64 | if (ctx.colors) ctx.stylize = stylizeWithColor; 65 | return formatValue(ctx, obj, ctx.depth); 66 | } 67 | 68 | // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics 69 | inspect.colors = { 70 | 'bold' : [1, 22], 71 | 'italic' : [3, 23], 72 | 'underline' : [4, 24], 73 | 'inverse' : [7, 27], 74 | 'white' : [37, 39], 75 | 'grey' : [90, 39], 76 | 'black' : [30, 39], 77 | 'blue' : [34, 39], 78 | 'cyan' : [36, 39], 79 | 'green' : [32, 39], 80 | 'magenta' : [35, 39], 81 | 'red' : [31, 39], 82 | 'yellow' : [33, 39] 83 | }; 84 | 85 | // Don't use 'blue' not visible on cmd.exe 86 | inspect.styles = { 87 | 'special': 'cyan', 88 | 'number': 'yellow', 89 | 'boolean': 'yellow', 90 | 'undefined': 'grey', 91 | 'null': 'bold', 92 | 'string': 'green', 93 | 'date': 'magenta', 94 | // "name": intentionally not styling 95 | 'regexp': 'red' 96 | }; 97 | 98 | function stylizeNoColor(str, styleType) { 99 | return str; 100 | } 101 | 102 | function isBoolean(arg) { 103 | return typeof arg === 'boolean'; 104 | } 105 | 106 | function isUndefined(arg) { 107 | return arg === void 0; 108 | } 109 | 110 | function stylizeWithColor(str, styleType) { 111 | var style = inspect.styles[styleType]; 112 | 113 | if (style) { 114 | return '\u001b[' + inspect.colors[style][0] + 'm' + str + 115 | '\u001b[' + inspect.colors[style][1] + 'm'; 116 | } else { 117 | return str; 118 | } 119 | } 120 | 121 | function isFunction(arg) { 122 | return typeof arg === 'function'; 123 | } 124 | 125 | function isString(arg) { 126 | return typeof arg === 'string'; 127 | } 128 | 129 | function isNumber(arg) { 130 | return typeof arg === 'number'; 131 | } 132 | 133 | function isNull(arg) { 134 | return arg === null; 135 | } 136 | 137 | function hasOwn(obj, prop) { 138 | return Object.prototype.hasOwnProperty.call(obj, prop); 139 | } 140 | 141 | function isRegExp(re) { 142 | return isObject(re) && objectToString(re) === '[object RegExp]'; 143 | } 144 | 145 | function isObject(arg) { 146 | return typeof arg === 'object' && arg !== null; 147 | } 148 | 149 | function isError(e) { 150 | return isObject(e) && 151 | (objectToString(e) === '[object Error]' || e instanceof Error); 152 | } 153 | 154 | function isDate(d) { 155 | return isObject(d) && objectToString(d) === '[object Date]'; 156 | } 157 | 158 | function objectToString(o) { 159 | return Object.prototype.toString.call(o); 160 | } 161 | 162 | function arrayToHash(array) { 163 | var hash = {}; 164 | 165 | forEach(array, function(val, idx) { 166 | hash[val] = true; 167 | }); 168 | 169 | return hash; 170 | } 171 | 172 | function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { 173 | var output = []; 174 | for (var i = 0, l = value.length; i < l; ++i) { 175 | if (hasOwn(value, String(i))) { 176 | output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, 177 | String(i), true)); 178 | } else { 179 | output.push(''); 180 | } 181 | } 182 | forEach(keys, function(key) { 183 | if (!key.match(/^\d+$/)) { 184 | output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, 185 | key, true)); 186 | } 187 | }); 188 | return output; 189 | } 190 | 191 | function formatError(value) { 192 | return '[' + Error.prototype.toString.call(value) + ']'; 193 | } 194 | 195 | function formatValue(ctx, value, recurseTimes) { 196 | // Provide a hook for user-specified inspect functions. 197 | // Check that value is an object with an inspect function on it 198 | if (ctx.customInspect && 199 | value && 200 | isFunction(value.inspect) && 201 | // Filter out the util module, it's inspect function is special 202 | value.inspect !== inspect && 203 | // Also filter out any prototype objects using the circular check. 204 | !(value.constructor && value.constructor.prototype === value)) { 205 | var ret = value.inspect(recurseTimes, ctx); 206 | if (!isString(ret)) { 207 | ret = formatValue(ctx, ret, recurseTimes); 208 | } 209 | return ret; 210 | } 211 | 212 | // Primitive types cannot have properties 213 | var primitive = formatPrimitive(ctx, value); 214 | if (primitive) { 215 | return primitive; 216 | } 217 | 218 | // Look up the keys of the object. 219 | var keys = objectKeys(value); 220 | var visibleKeys = arrayToHash(keys); 221 | 222 | try { 223 | if (ctx.showHidden && Object.getOwnPropertyNames) { 224 | keys = Object.getOwnPropertyNames(value); 225 | } 226 | } catch (e) { 227 | // ignore 228 | } 229 | 230 | // IE doesn't make error fields non-enumerable 231 | // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx 232 | if (isError(value) 233 | && (indexOf(keys, 'message') >= 0 || indexOf(keys, 'description') >= 0)) { 234 | return formatError(value); 235 | } 236 | 237 | // Some type of object without properties can be shortcutted. 238 | if (keys.length === 0) { 239 | if (isFunction(value)) { 240 | var name = value.name ? ': ' + value.name : ''; 241 | return ctx.stylize('[Function' + name + ']', 'special'); 242 | } 243 | if (isRegExp(value)) { 244 | return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); 245 | } 246 | if (isDate(value)) { 247 | return ctx.stylize(Date.prototype.toString.call(value), 'date'); 248 | } 249 | if (isError(value)) { 250 | return formatError(value); 251 | } 252 | } 253 | 254 | var base = '', array = false, braces = ['{', '}']; 255 | 256 | // Make Array say that they are Array 257 | if (isArray(value)) { 258 | array = true; 259 | braces = ['[', ']']; 260 | } 261 | 262 | // Make functions say that they are functions 263 | if (isFunction(value)) { 264 | var n = value.name ? ': ' + value.name : ''; 265 | base = ' [Function' + n + ']'; 266 | } 267 | 268 | // Make RegExps say that they are RegExps 269 | if (isRegExp(value)) { 270 | base = ' ' + RegExp.prototype.toString.call(value); 271 | } 272 | 273 | // Make dates with properties first say the date 274 | if (isDate(value)) { 275 | base = ' ' + Date.prototype.toUTCString.call(value); 276 | } 277 | 278 | // Make error with message first say the error 279 | if (isError(value)) { 280 | base = ' ' + formatError(value); 281 | } 282 | 283 | if (keys.length === 0 && (!array || value.length == 0)) { 284 | return braces[0] + base + braces[1]; 285 | } 286 | 287 | if (recurseTimes < 0) { 288 | if (isRegExp(value)) { 289 | return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); 290 | } else { 291 | return ctx.stylize('[Object]', 'special'); 292 | } 293 | } 294 | 295 | ctx.seen.push(value); 296 | 297 | var output; 298 | if (array) { 299 | output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); 300 | } else { 301 | output = map(keys, function(key) { 302 | return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); 303 | }); 304 | } 305 | 306 | ctx.seen.pop(); 307 | 308 | return reduceToSingleString(output, base, braces); 309 | } 310 | 311 | function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { 312 | var name, str, desc; 313 | desc = { value: void 0 }; 314 | try { 315 | // ie6 › navigator.toString 316 | // throws Error: Object doesn't support this property or method 317 | desc.value = value[key]; 318 | } catch (e) { 319 | // ignore 320 | } 321 | try { 322 | // ie10 › Object.getOwnPropertyDescriptor(window.location, 'hash') 323 | // throws TypeError: Object doesn't support this action 324 | if (Object.getOwnPropertyDescriptor) { 325 | desc = Object.getOwnPropertyDescriptor(value, key) || desc; 326 | } 327 | } catch (e) { 328 | // ignore 329 | } 330 | if (desc.get) { 331 | if (desc.set) { 332 | str = ctx.stylize('[Getter/Setter]', 'special'); 333 | } else { 334 | str = ctx.stylize('[Getter]', 'special'); 335 | } 336 | } else { 337 | if (desc.set) { 338 | str = ctx.stylize('[Setter]', 'special'); 339 | } 340 | } 341 | if (!hasOwn(visibleKeys, key)) { 342 | name = '[' + key + ']'; 343 | } 344 | if (!str) { 345 | if (indexOf(ctx.seen, desc.value) < 0) { 346 | if (isNull(recurseTimes)) { 347 | str = formatValue(ctx, desc.value, null); 348 | } else { 349 | str = formatValue(ctx, desc.value, recurseTimes - 1); 350 | } 351 | if (str.indexOf('\n') > -1) { 352 | if (array) { 353 | str = map(str.split('\n'), function(line) { 354 | return ' ' + line; 355 | }).join('\n').substr(2); 356 | } else { 357 | str = '\n' + map(str.split('\n'), function(line) { 358 | return ' ' + line; 359 | }).join('\n'); 360 | } 361 | } 362 | } else { 363 | str = ctx.stylize('[Circular]', 'special'); 364 | } 365 | } 366 | if (isUndefined(name)) { 367 | if (array && key.match(/^\d+$/)) { 368 | return str; 369 | } 370 | name = JSON.stringify('' + key); 371 | if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { 372 | name = name.substr(1, name.length - 2); 373 | name = ctx.stylize(name, 'name'); 374 | } else { 375 | name = name.replace(/'/g, "\\'") 376 | .replace(/\\"/g, '"') 377 | .replace(/(^"|"$)/g, "'"); 378 | name = ctx.stylize(name, 'string'); 379 | } 380 | } 381 | 382 | return name + ': ' + str; 383 | } 384 | 385 | function formatPrimitive(ctx, value) { 386 | if (isUndefined(value)) 387 | return ctx.stylize('undefined', 'undefined'); 388 | if (isString(value)) { 389 | var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') 390 | .replace(/'/g, "\\'") 391 | .replace(/\\"/g, '"') + '\''; 392 | return ctx.stylize(simple, 'string'); 393 | } 394 | if (isNumber(value)) 395 | return ctx.stylize('' + value, 'number'); 396 | if (isBoolean(value)) 397 | return ctx.stylize('' + value, 'boolean'); 398 | // For some reason typeof null is "object", so special case here. 399 | if (isNull(value)) 400 | return ctx.stylize('null', 'null'); 401 | } 402 | 403 | function reduceToSingleString(output, base, braces) { 404 | var numLinesEst = 0; 405 | var length = reduce(output, function(prev, cur) { 406 | numLinesEst++; 407 | if (cur.indexOf('\n') >= 0) numLinesEst++; 408 | return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; 409 | }, 0); 410 | 411 | if (length > 60) { 412 | return braces[0] + 413 | (base === '' ? '' : base + '\n ') + 414 | ' ' + 415 | output.join(',\n ') + 416 | ' ' + 417 | braces[1]; 418 | } 419 | 420 | return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; 421 | } 422 | 423 | function _extend(origin, add) { 424 | // Don't do anything if add isn't an object 425 | if (!add || !isObject(add)) return origin; 426 | 427 | var keys = objectKeys(add); 428 | var i = keys.length; 429 | while (i--) { 430 | origin[keys[i]] = add[keys[i]]; 431 | } 432 | return origin; 433 | } 434 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "util-inspect", 3 | "version": "0.1.8", 4 | "description": "cross-browser node.js `util.inspect`", 5 | "repository": "Automattic/util-inspect", 6 | "dependencies": { 7 | "isarray": "0.0.1", 8 | "object-keys": "0.5.0", 9 | "json3": "3.3.0", 10 | "foreach": "2.0.4", 11 | "array-map": "0.0.0", 12 | "indexof": "0.0.1", 13 | "array-reduce": "0.0.0" 14 | } 15 | } 16 | --------------------------------------------------------------------------------