Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 112 additions & 10 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ var hasMap = typeof Map === 'function' && Map.prototype;
var mapSizeDescriptor = Object.getOwnPropertyDescriptor && hasMap ? Object.getOwnPropertyDescriptor(Map.prototype, 'size') : null;
var mapSize = hasMap && mapSizeDescriptor && typeof mapSizeDescriptor.get === 'function' ? mapSizeDescriptor.get : null;
var mapForEach = hasMap && Map.prototype.forEach;
var mapEntries = hasMap && Map.prototype.entries;
var hasSet = typeof Set === 'function' && Set.prototype;
var setSizeDescriptor = Object.getOwnPropertyDescriptor && hasSet ? Object.getOwnPropertyDescriptor(Set.prototype, 'size') : null;
var setSize = hasSet && setSizeDescriptor && typeof setSizeDescriptor.get === 'function' ? setSizeDescriptor.get : null;
var setForEach = hasSet && Set.prototype.forEach;
var setValues = hasSet && Set.prototype.values;
var hasWeakMap = typeof WeakMap === 'function' && WeakMap.prototype;
var weakMapHas = hasWeakMap ? WeakMap.prototype.has : null;
var hasWeakSet = typeof WeakSet === 'function' && WeakSet.prototype;
Expand Down Expand Up @@ -112,6 +114,21 @@ module.exports = function inspect_(obj, options, depth, seen) {
}
var numericSeparator = opts.numericSeparator;

if (
has(opts, 'maxArrayLength')
&& opts.maxArrayLength !== null
&& opts.maxArrayLength !== Infinity
&& (
typeof opts.maxArrayLength !== 'number'
|| opts.maxArrayLength < 0
|| opts.maxArrayLength !== opts.maxArrayLength // NaN
|| parseInt(opts.maxArrayLength, 10) !== opts.maxArrayLength // non-integer
)
) {
throw new TypeError('option "maxArrayLength", if provided, must be a non-negative integer, Infinity, or `null`');
}
var maxArrayLength = typeof opts.maxArrayLength === 'number' ? opts.maxArrayLength : Infinity;

if (typeof obj === 'undefined') {
return 'undefined';
}
Expand Down Expand Up @@ -190,12 +207,45 @@ module.exports = function inspect_(obj, options, depth, seen) {
}
if (isArray(obj)) {
if (obj.length === 0) { return '[]'; }
var xs = arrObjKeys(obj, inspect);
var xs = arrObjKeys(obj, inspect, maxArrayLength);
if (indent && !singleLineValues(xs)) {
return '[' + indentedJoin(xs, indent) + ']';
}
return '[ ' + $join.call(xs, ', ') + ' ]';
}
if (isTypedArray(obj)) {
var typedTag = $slice.call(toStr(obj), 8, -1); // e.g., 'Uint8Array'
if (obj.length === 0) { return typedTag + ' []'; }
var typedXs = [];
var typedLimit = maxArrayLength < obj.length ? maxArrayLength : obj.length;
for (var ti = 0; ti < typedLimit; ti++) {
typedXs.push(String(obj[ti]));
}
if (obj.length > maxArrayLength) {
var typedRemaining = obj.length - maxArrayLength;
typedXs.push('... ' + typedRemaining + ' more item' + (typedRemaining > 1 ? 's' : ''));
}
if (indent && !singleLineValues(typedXs)) {
return typedTag + ' [' + indentedJoin(typedXs, indent) + ']';
}
return typedTag + ' [ ' + $join.call(typedXs, ', ') + ' ]';
}
if (isArguments(obj)) {
if (obj.length === 0) { return 'Arguments []'; }
var argXs = [];
var argLimit = maxArrayLength < obj.length ? maxArrayLength : obj.length;
for (var ai = 0; ai < argLimit; ai++) {
argXs.push(has(obj, ai) ? inspect(obj[ai], obj) : '');
}
if (obj.length > maxArrayLength) {
var argRemaining = obj.length - maxArrayLength;
argXs.push('... ' + argRemaining + ' more item' + (argRemaining > 1 ? 's' : ''));
}
if (indent && !singleLineValues(argXs)) {
return 'Arguments [' + indentedJoin(argXs, indent) + ']';
}
return 'Arguments [ ' + $join.call(argXs, ', ') + ' ]';
}
if (isError(obj)) {
var parts = arrObjKeys(obj, inspect);
if (!('cause' in Error.prototype) && 'cause' in obj && !isEnumerable.call(obj, 'cause')) {
Expand All @@ -213,21 +263,53 @@ module.exports = function inspect_(obj, options, depth, seen) {
}
if (isMap(obj)) {
var mapParts = [];
if (mapForEach) {
var mapLen = mapSize.call(obj);
var mapCount = 0;
if (mapEntries) {
var mapIter = mapEntries.call(obj);
var mapEntry;
while (mapCount < maxArrayLength && !(mapEntry = mapIter.next()).done) {
mapParts.push(inspect(mapEntry.value[0], obj, true) + ' => ' + inspect(mapEntry.value[1], obj));
mapCount += 1;
}
} else if (mapForEach) {
mapForEach.call(obj, function (value, key) {
mapParts.push(inspect(key, obj, true) + ' => ' + inspect(value, obj));
if (mapCount < maxArrayLength) {
mapParts.push(inspect(key, obj, true) + ' => ' + inspect(value, obj));
}
Comment on lines +277 to +279
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The map/set callbacks still run for every entry; they just skip the push when mapCount >= maxArrayLength. For a Map with 100k entries and maxArrayLength: 5, all 100k entries are still visited (and their keys/values are still inspected). We'd need to use a while loop over .next() to actually get a performance benefit here.

mapCount += 1;
});
}
return collectionOf('Map', mapSize.call(obj), mapParts, indent);
if (mapLen > maxArrayLength) {
var mapRemaining = mapLen - maxArrayLength;
mapParts.push('... ' + mapRemaining + ' more item' + (mapRemaining > 1 ? 's' : ''));
}
return collectionOf('Map', mapLen, mapParts, indent);
}
if (isSet(obj)) {
var setParts = [];
if (setForEach) {
var setLen = setSize.call(obj);
var setCount = 0;
if (setValues) {
var setIter = setValues.call(obj);
var setEntry;
while (setCount < maxArrayLength && !(setEntry = setIter.next()).done) {
setParts.push(inspect(setEntry.value, obj));
setCount += 1;
}
} else if (setForEach) {
setForEach.call(obj, function (value) {
setParts.push(inspect(value, obj));
if (setCount < maxArrayLength) {
setParts.push(inspect(value, obj));
}
setCount += 1;
});
}
return collectionOf('Set', setSize.call(obj), setParts, indent);
if (setLen > maxArrayLength) {
var setRemaining = setLen - maxArrayLength;
setParts.push('... ' + setRemaining + ' more item' + (setRemaining > 1 ? 's' : ''));
}
return collectionOf('Set', setLen, setParts, indent);
}
if (isWeakMap(obj)) {
return weakCollectionOf('WeakMap');
Expand Down Expand Up @@ -297,6 +379,21 @@ function isError(obj) { return toStr(obj) === '[object Error]' && canTrustToStri
function isString(obj) { return toStr(obj) === '[object String]' && canTrustToString(obj); }
function isNumber(obj) { return toStr(obj) === '[object Number]' && canTrustToString(obj); }
function isBoolean(obj) { return toStr(obj) === '[object Boolean]' && canTrustToString(obj); }
function isArguments(obj) { return toStr(obj) === '[object Arguments]'; }
function isTypedArray(obj) {
var tag = toStr(obj);
return tag === '[object Int8Array]'
|| tag === '[object Uint8Array]'
|| tag === '[object Uint8ClampedArray]'
|| tag === '[object Int16Array]'
|| tag === '[object Uint16Array]'
|| tag === '[object Int32Array]'
|| tag === '[object Uint32Array]'
|| tag === '[object Float32Array]'
|| tag === '[object Float64Array]'
|| tag === '[object BigInt64Array]'
|| tag === '[object BigUint64Array]';
}

// Symbol and BigInt do have Symbol.toStringTag by spec, so that can't be used to eliminate false positives
function isSymbol(obj) {
Expand Down Expand Up @@ -503,14 +600,19 @@ function indentedJoin(xs, indent) {
return lineJoiner + $join.call(xs, ',' + lineJoiner) + '\n' + indent.prev;
}

function arrObjKeys(obj, inspect) {
function arrObjKeys(obj, inspect, maxLength) {
var isArr = isArray(obj);
var xs = [];
if (isArr) {
xs.length = obj.length;
for (var i = 0; i < obj.length; i++) {
var limit = typeof maxLength === 'number' && maxLength < obj.length ? maxLength : obj.length;
xs.length = limit;
for (var i = 0; i < limit; i++) {
xs[i] = has(obj, i) ? inspect(obj[i], obj) : '';
}
if (typeof maxLength === 'number' && obj.length > maxLength) {
var remaining = obj.length - maxLength;
xs.push('... ' + remaining + ' more item' + (remaining > 1 ? 's' : ''));
}
}
var syms = typeof gOPS === 'function' ? gOPS(obj) : [];
var symMap;
Expand Down
1 change: 1 addition & 0 deletions readme.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ Return a string `s` with the string representation of `obj` up to a depth of `op
Additional options:
- `quoteStyle`: must be "single" or "double", if present. Default `'single'` for strings, `'double'` for HTML elements.
- `maxStringLength`: must be `0`, a positive integer, `Infinity`, or `null`, if present. Default `Infinity`.
- `maxArrayLength`: must be a non-negative integer, `Infinity`, or `null`, if present. Default `Infinity`. Specifies the maximum number of elements to include when formatting Arrays, TypedArrays, arguments objects, Sets, and Maps. Elements beyond this limit will be replaced with `... N more items`.
- `customInspect`: When `true`, a custom inspect method function will be invoked (either undere the `util.inspect.custom` symbol, or the `inspect` property). When the string `'symbol'`, only the symbol method will be invoked. Default `true`.
- `indent`: must be "\t", `null`, or a positive integer. Default `null`.
- `numericSeparator`: must be a boolean, if present. Default `false`. If `true`, all numbers will be printed with numeric separators (eg, `1234.5678` will be printed as `'1_234.567_8'`)
Expand Down
Loading
Loading