should-format
Advanced tools
Comparing version 0.0.7 to 0.1.0
388
index.js
var getType = require('should-type'); | ||
var util = require('./util'); | ||
@@ -8,29 +9,124 @@ function genKeysFunc(f) { | ||
return k; | ||
} | ||
}; | ||
} | ||
//XXX add ability to only inspect some paths | ||
var format = function(value, opts) { | ||
function Formatter(opts) { | ||
opts = opts || {}; | ||
if(!('seen' in opts)) opts.seen = []; | ||
opts.keys = genKeysFunc('keys' in opts && opts.keys === false ? Object.getOwnPropertyNames : Object.keys); | ||
this.seen = []; | ||
this.keys = genKeysFunc(opts.keys === false ? Object.getOwnPropertyNames : Object.keys); | ||
if(!('maxLineLength' in opts)) opts.maxLineLength = 60; | ||
if(!('propSep' in opts)) opts.propSep = ','; | ||
this.maxLineLength = typeof opts.maxLineLength === 'number' ? opts.maxLineLength : 60; | ||
this.propSep = opts.propSep || ','; | ||
} | ||
var type = getType(value); | ||
return (format.formats[type] || format.formats['object'])(value, opts); | ||
Formatter.prototype = { | ||
constructor: Formatter, | ||
format: function(value) { | ||
var t = getType(value); | ||
var name1 = t.type, name2 = t.type; | ||
if(t.cls) { | ||
name1 += '_' + t.cls; | ||
name2 += '_' + t.cls; | ||
} | ||
if(t.sub) { | ||
name2 += '_' + t.sub; | ||
} | ||
var f = this['_format_' + name2] || this['_format_' + name1] || this['_format_' + t.type] || this.defaultFormat; | ||
return f.call(this, value); | ||
}, | ||
formatObject: function(value, prefix, props) { | ||
props = props || this.keys(value); | ||
var len = 0; | ||
this.seen.push(value); | ||
props = props.map(function(prop) { | ||
var f = this.formatProperty(value, prop); | ||
len += f.length; | ||
return f; | ||
}, this); | ||
this.seen.pop(); | ||
if(props.length === 0) return '{}'; | ||
if(len <= this.maxLineLength) { | ||
return '{ ' + (prefix ? prefix + ' ' : '') + props.join(this.propSep + ' ') + ' }'; | ||
} else { | ||
return '{' + '\n' + (prefix ? ' ' + prefix + '\n' : '') + props.map(util.addSpaces).join(this.propSep + '\n') + '\n' + '}'; | ||
} | ||
}, | ||
formatPropertyName: function(name) { | ||
return name.match(/^[a-zA-Z_$][a-zA-Z_$0-9]*$/) ? name : this.format(name); | ||
}, | ||
formatProperty: function(value, prop) { | ||
var desc; | ||
try { | ||
desc = Object.getOwnPropertyDescriptor(value, prop) || {value: value[prop]}; | ||
} catch(e) { | ||
desc = {value: e}; | ||
} | ||
var propName = this.formatPropertyName(prop); | ||
var propValue = desc.get && desc.set ? | ||
'[Getter/Setter]' : desc.get ? | ||
'[Getter]' : desc.set ? | ||
'[Setter]' : this.seen.indexOf(desc.value) >= 0 ? | ||
'[Circular]' : | ||
this.format(desc.value); | ||
return propName + ': ' + propValue; | ||
} | ||
}; | ||
module.exports = format; | ||
Formatter.add = function add(type, cls, sub, f) { | ||
var args = Array.prototype.slice.call(arguments); | ||
f = args.pop(); | ||
Formatter.prototype['_format_' + args.join('_')] = f; | ||
}; | ||
format.formats = {}; | ||
Formatter.formatObjectWithPrefix = function formatObjectWithPrefix(f) { | ||
return function(value) { | ||
var prefix = f.call(this, value); | ||
var props = this.keys(value); | ||
if(props.length == 0) return prefix; | ||
else return this.formatObject(value, prefix, props); | ||
}; | ||
}; | ||
function add(t, f) { | ||
format.formats[t] = f; | ||
} | ||
var functionNameRE = /^\s*function\s*(\S*)\s*\(/; | ||
[ 'undefined', 'boolean', 'null'].forEach(function(name) { | ||
add(name, String); | ||
Formatter.functionName = function functionName(f) { | ||
if(f.name) { | ||
return f.name; | ||
} | ||
var name = f.toString().match(functionNameRE)[1]; | ||
return name; | ||
}; | ||
Formatter.generateFunctionForIndexedArray = function generateFunctionForIndexedArray(lengthProp, name, padding) { | ||
return function(value) { | ||
var str = ''; | ||
var max = 50; | ||
var len = value[lengthProp]; | ||
if(len > 0) { | ||
for(var i = 0; i < max && i < len; i++) { | ||
var b = value[i] || 0; | ||
str += ' ' + util.pad0(b.toString(16), padding); | ||
} | ||
if(len > max) | ||
str += ' ... '; | ||
} | ||
return '[' + (value.constructor.name || name) + (str ? ':' + str : '') + ']'; | ||
}; | ||
}; | ||
['undefined', 'boolean', 'null', 'symbol'].forEach(function(name) { | ||
Formatter.add(name, String); | ||
}); | ||
@@ -40,11 +136,12 @@ | ||
var capName = name.substring(0, 1).toUpperCase() + name.substring(1); | ||
add('object-' + name, formatObjectWithPrefix(function(value) { | ||
return '[' + capName + ': ' + format(value.valueOf()) + ']'; | ||
Formatter.add('object', name, Formatter.formatObjectWithPrefix(function(value) { | ||
return '[' + capName + ': ' + this.format(value.valueOf()) + ']'; | ||
})); | ||
}); | ||
add('object-string', function(value, opts) { | ||
Formatter.add('object', 'string', function(value) { | ||
var realValue = value.valueOf(); | ||
var prefix = '[String: ' + format(realValue) + ']'; | ||
var props = opts.keys(value); | ||
var prefix = '[String: ' + this.format(realValue) + ']'; | ||
var props = this.keys(value); | ||
props = props.filter(function(p) { | ||
@@ -55,8 +152,8 @@ return !(p.match(/\d+/) && parseInt(p, 10) < realValue.length); | ||
if(props.length == 0) return prefix; | ||
else return formatObject(value, opts, prefix, props); | ||
else return this.formatObject(value, prefix, props); | ||
}); | ||
add('regexp', formatObjectWithPrefix(String)); | ||
Formatter.add('object', 'regexp', Formatter.formatObjectWithPrefix(String)); | ||
add('number', function(value) { | ||
Formatter.add('number', function(value) { | ||
if(value === 0 && 1 / value < 0) return '-0'; | ||
@@ -66,3 +163,3 @@ return String(value); | ||
add('string', function(value) { | ||
Formatter.add('string', function(value) { | ||
return '\'' + JSON.stringify(value).replace(/^"|"$/g, '') | ||
@@ -73,9 +170,11 @@ .replace(/'/g, "\\'") | ||
add('object', formatObject); | ||
Formatter.add('object', function(value) { | ||
return this.formatObject(value); | ||
}); | ||
add('array', function(value, opts) { | ||
var keys = opts.keys(value); | ||
Formatter.add('object', 'array', function(value) { | ||
var keys = this.keys(value); | ||
var len = 0; | ||
opts.seen.push(value); | ||
this.seen.push(value); | ||
@@ -92,167 +191,160 @@ var props = keys.map(function(prop) { | ||
if(prop.match(/\d+/)) { | ||
f = format(desc.value, opts); | ||
f = this.format(desc.value); | ||
} else { | ||
f = formatProperty(desc.value, opts, prop) | ||
f = this.formatProperty(desc.value, prop); | ||
} | ||
len += f.length; | ||
return f; | ||
}); | ||
}, this); | ||
opts.seen.pop(); | ||
this.seen.pop(); | ||
if(props.length === 0) return '[]'; | ||
if(len <= opts.maxLineLength) { | ||
return '[ ' + props.join(opts.propSep + ' ') + ' ]'; | ||
if(len <= this.maxLineLength) { | ||
return '[ ' + props.join(this.propSep + ' ') + ' ]'; | ||
} else { | ||
return '[' + '\n' + props.map(addSpaces).join(opts.propSep + '\n') + '\n' + ']'; | ||
return '[' + '\n' + props.map(util.addSpaces).join(this.propSep + '\n') + '\n' + ']'; | ||
} | ||
}); | ||
function addSpaces(v) { | ||
return v.split('\n').map(function(vv) { return ' ' + vv; }).join('\n'); | ||
function formatDate(value) { | ||
var to = value.getTimezoneOffset(); | ||
var absTo = Math.abs(to); | ||
var hours = Math.floor(absTo / 60); | ||
var minutes = absTo - hours * 60; | ||
var tzFormat = 'GMT' + (to < 0 ? '+' : '-') + util.pad0(hours, 2) + util.pad0(minutes, 2); | ||
return value.toLocaleDateString() + ' ' | ||
+ value.toLocaleTimeString() + '.' | ||
+ util.pad0(value.getMilliseconds(), 3) + ' ' + tzFormat; | ||
} | ||
function formatObject(value, opts, prefix, props) { | ||
props = props || opts.keys(value); | ||
Formatter.add('object', 'date', Formatter.formatObjectWithPrefix(formatDate)); | ||
var len = 0; | ||
Formatter.add('function', Formatter.formatObjectWithPrefix(function(value) { | ||
var name = Formatter.functionName(value); | ||
return '[Function' + (name ? ': ' + name : '') + ']'; | ||
})); | ||
opts.seen.push(value); | ||
props = props.map(function(prop) { | ||
var f = formatProperty(value, opts, prop); | ||
len += f.length; | ||
return f; | ||
}); | ||
opts.seen.pop(); | ||
Formatter.add('object', 'error', Formatter.formatObjectWithPrefix(function(value) { | ||
var name = value.name; | ||
var message = value.message; | ||
return '[' + name + (message ? ': ' + message : '') + ']'; | ||
})); | ||
if(props.length === 0) return '{}'; | ||
Formatter.add('object', 'buffer', Formatter.generateFunctionForIndexedArray('length', 'Buffer', 2)); | ||
if(len <= opts.maxLineLength) { | ||
return '{ ' + (prefix ? prefix + ' ' : '') + props.join(opts.propSep + ' ') + ' }'; | ||
} else { | ||
return '{' + '\n' + (prefix ? prefix + '\n' : '') + props.map(addSpaces).join(opts.propSep + '\n') + '\n' + '}'; | ||
} | ||
} | ||
Formatter.add('object', 'array-buffer', Formatter.generateFunctionForIndexedArray('byteLength', 'ArrayBuffer', 2)); | ||
format.formatPropertyName = function(name, opts) { | ||
return name.match(/^[a-zA-Z_$][a-zA-Z_$0-9]*$/) ? name : format(name, opts) | ||
}; | ||
Formatter.add('object', 'typed-array', 'int8', Formatter.generateFunctionForIndexedArray('length', 'Int8Array', 2)); | ||
Formatter.add('object', 'typed-array', 'uint8', Formatter.generateFunctionForIndexedArray('length', 'Uint8Array', 2)); | ||
Formatter.add('object', 'typed-array', 'uint8clamped', Formatter.generateFunctionForIndexedArray('length', 'Uint8ClampedArray', 2)); | ||
Formatter.add('object', 'typed-array', 'int16', Formatter.generateFunctionForIndexedArray('length', 'Int16Array', 4)); | ||
Formatter.add('object', 'typed-array', 'uint16', Formatter.generateFunctionForIndexedArray('length', 'Uint16Array', 4)); | ||
function formatProperty(value, opts, prop) { | ||
var desc; | ||
try { | ||
desc = Object.getOwnPropertyDescriptor(value, prop) || {value: value[prop]}; | ||
} catch(e) { | ||
desc = {value: e}; | ||
} | ||
Formatter.add('object', 'typed-array', 'int32', Formatter.generateFunctionForIndexedArray('length', 'Int32Array', 8)); | ||
Formatter.add('object', 'typed-array', 'uint32', Formatter.generateFunctionForIndexedArray('length', 'Uint32Array', 8)); | ||
var propName = format.formatPropertyName(prop, opts); | ||
//TODO add float32 and float64 | ||
var propValue = desc.get && desc.set ? | ||
'[Getter/Setter]' : desc.get ? | ||
'[Getter]' : desc.set ? | ||
'[Setter]' : opts.seen.indexOf(desc.value) >= 0 ? | ||
'[Circular]' : | ||
format(desc.value, opts); | ||
Formatter.add('object', 'promise', function() { | ||
return '[Promise]';//TODO it could be nice to inspect its state and value | ||
}); | ||
return propName + ': ' + propValue; | ||
} | ||
Formatter.add('object', 'xhr', function() { | ||
return '[XMLHttpRequest]';//TODO it could be nice to inspect its state | ||
}); | ||
Formatter.add('object', 'html-element', function(value) { | ||
return value.outerHTML; | ||
}); | ||
function pad2Zero(n) { | ||
return n < 10 ? '0' + n : '' + n; | ||
} | ||
Formatter.add('object', 'html-element', '#text', function(value) { | ||
return value.nodeValue; | ||
}); | ||
function pad3Zero(n) { | ||
return n < 100 ? '0' + pad2Zero(n) : '' + n; | ||
} | ||
Formatter.add('object', 'html-element', '#document', function(value) { | ||
return value.documentElement.outerHTML; | ||
}); | ||
function formatDate(value) { | ||
var to = value.getTimezoneOffset(); | ||
var absTo = Math.abs(to); | ||
var hours = Math.floor(absTo / 60); | ||
var minutes = absTo - hours * 60; | ||
var tzFormat = 'GMT' + (to < 0 ? '+' : '-') + pad2Zero(hours) + pad2Zero(minutes); | ||
return value.toLocaleDateString() + ' ' + value.toLocaleTimeString() + '.' + pad3Zero(value.getMilliseconds()) + ' ' + tzFormat; | ||
} | ||
Formatter.add('object', 'window', function() { | ||
return '[Window]'; | ||
}); | ||
function formatObjectWithPrefix(f) { | ||
return function(value, opts) { | ||
var prefix = f(value); | ||
var props = opts.keys(value); | ||
if(props.length == 0) return prefix; | ||
else return formatObject(value, opts, prefix, props); | ||
Formatter.add('object', 'set', function(value) { | ||
var iter = value.values(); | ||
var len = 0; | ||
this.seen.push(value); | ||
var props = []; | ||
var next = iter.next(); | ||
while(!next.done) { | ||
var val = next.value; | ||
var f = this.format(val); | ||
len += f.length; | ||
props.push(f); | ||
next = iter.next(); | ||
} | ||
} | ||
add('date', formatObjectWithPrefix(formatDate)); | ||
this.seen.pop(); | ||
var functionNameRE = /^\s*function\s*(\S*)\s*\(/; | ||
if(props.length === 0) return '{ [Set] }'; | ||
function functionName(f) { | ||
if(f.name) { | ||
return f.name; | ||
if(len <= this.maxLineLength) { | ||
return '{ [Set] ' + props.join(this.propSep + ' ') + ' }'; | ||
} else { | ||
return '{\n [Set]\n' + props.map(util.addSpaces).join(this.propSep + '\n') + '\n' + '}'; | ||
} | ||
var name = f.toString().match(functionNameRE)[1]; | ||
return name; | ||
} | ||
}); | ||
add('function', formatObjectWithPrefix(function(value) { | ||
var name = functionName(value); | ||
return '[Function' + (name ? ': ' + name : '') + ']'; | ||
})); | ||
Formatter.add('object', 'map', function(value) { | ||
var iter = value.entries(); | ||
var len = 0; | ||
add('error', formatObjectWithPrefix(function(value) { | ||
var name = value.name; | ||
var message = value.message; | ||
return '[' + name + (message ? ': ' + message : '') + ']'; | ||
})); | ||
this.seen.push(value); | ||
function generateFunctionForIndexedArray(lengthProp, name) { | ||
return function(value) { | ||
var str = ''; | ||
var max = 50; | ||
var len = value[lengthProp]; | ||
if(len > 0) { | ||
for(var i = 0; i < max && i < len; i++) { | ||
var b = value[i] || 0; | ||
str += ' ' + pad2Zero(b.toString(16)); | ||
} | ||
if(len > max) | ||
str += ' ... '; | ||
var props = []; | ||
var next = iter.next(); | ||
while(!next.done) { | ||
var val = next.value; | ||
var fK = this.format(val[0]); | ||
var fV = this.format(val[1]); | ||
var f; | ||
if((fK.length + fV.length + 4) <= this.maxLineLength) { | ||
f = fK + ' => ' + fV; | ||
} else { | ||
f = fK + ' =>\n' + fV; | ||
} | ||
return '[' + (value.constructor.name || name) + (str ? ':' + str : '') + ']'; | ||
} | ||
} | ||
add('buffer', generateFunctionForIndexedArray('length', 'Buffer')); | ||
len += fK.length + fV.length + 4; | ||
props.push(f); | ||
add('array-buffer', generateFunctionForIndexedArray('byteLength')); | ||
next = iter.next(); | ||
} | ||
add('typed-array', generateFunctionForIndexedArray('byteLength')); | ||
this.seen.pop(); | ||
add('promise', function(value) { | ||
return '[Promise]'; | ||
}); | ||
if(props.length === 0) return '{ [Map] }'; | ||
add('xhr', function(value) { | ||
return '[XMLHttpRequest]'; | ||
if(len <= this.maxLineLength) { | ||
return '{ [Map] ' + props.join(this.propSep + ' ') + ' }'; | ||
} else { | ||
return '{\n [Map]\n' + props.map(util.addSpaces).join(this.propSep + '\n') + '\n' + '}'; | ||
} | ||
}); | ||
add('html-element', function(value) { | ||
return value.outerHTML; | ||
}); | ||
Formatter.prototype.defaultFormat = Formatter.prototype._format_object; | ||
add('html-element-text', function(value) { | ||
return value.nodeValue; | ||
}); | ||
function defaultFormat(value, opts) { | ||
return new Formatter(opts).format(value); | ||
} | ||
add('document', function(value) { | ||
return value.documentElement.outerHTML; | ||
}); | ||
add('window', function(value) { | ||
return '[Window]'; | ||
}); | ||
defaultFormat.Formatter = Formatter; | ||
module.exports = defaultFormat; |
{ | ||
"name": "should-format", | ||
"version": "0.0.7", | ||
"version": "0.1.0", | ||
"description": "Formatting of objects for should.js", | ||
@@ -21,9 +21,9 @@ "main": "index.js", | ||
"devDependencies": { | ||
"browserify": "^6.2.0", | ||
"mocha": "^2.0.1", | ||
"mocha-better-spec-reporter": "^1.1.1" | ||
"browserify": "latest", | ||
"mocha": "latest", | ||
"mocha-better-spec-reporter": "latest" | ||
}, | ||
"dependencies": { | ||
"should-type": "0.0.4" | ||
"should-type": "0.1.1" | ||
} | ||
} |
@@ -1,4 +0,5 @@ | ||
var format = require('../'); | ||
var assert = require('assert'); | ||
var format = require('../'); | ||
it('should format numbers', function() { | ||
@@ -146,2 +147,7 @@ assert.equal(format(10), '10'); | ||
var int16 = new Int16Array(3); | ||
int16[0] = 0x201; | ||
int16[1] = 0x2; | ||
assert.equal(format(int16), '[Int16Array: 0201 0002 0000]'); | ||
//var dataView = new DataView(buffer); | ||
@@ -166,2 +172,20 @@ //assert.equal(format(dataView), '[DataView: 00 20 02 00 00 00 00 00]'); | ||
assert.equal(format({ a: { b: 'abc' }, d: 'abc'}, { maxLineLength: 0 }), '{\n a: {\n b: \'abc\'\n },\n d: \'abc\'\n}') | ||
}); | ||
}); | ||
it('should format set', function() { | ||
if(typeof Set !== 'undefined') { | ||
assert.equal(format(new Set([1, 2, { a: 10}, 'abc'])), | ||
'{ [Set] 1, 2, { a: 10 }, \'abc\' }'); | ||
assert.equal(format(new Set([1, 2, { a: 10}, 'abc']), { maxLineLength: 0 }), | ||
'{\n [Set]\n 1,\n 2,\n {\n a: 10\n },\n \'abc\'\n}'); | ||
} | ||
}); | ||
it('should format map', function() { | ||
if(typeof Map !== 'undefined') { | ||
assert.equal(format(new Map([[1, 2], [2, 'abc'], [{ a: 10}, new Set()], ['abc', null]])), | ||
'{ [Map] 1 => 2, 2 => \'abc\', { a: 10 } => { [Set] }, \'abc\' => null }'); | ||
assert.equal(format(new Map([[1, 2], [2, 'abc'], [{ a: 10}, new Set()], ['abc', null]]), { maxLineLength: 10 }), | ||
'{\n [Map]\n 1 => 2,\n 2 => \'abc\',\n { a: 10 } =>\n { [Set] },\n \'abc\' =>\n null\n}'); | ||
} | ||
}); |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
247814
13
8574
+ Addedshould-type@0.1.1(transitive)
- Removedshould-type@0.0.4(transitive)
Updatedshould-type@0.1.1