-
Notifications
You must be signed in to change notification settings - Fork 30.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
console: add table method #18137
console: add table method #18137
Conversation
322d8dd
to
e0eb3af
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for doing this. Can you update the documentation for console.table()
in console.md
?
e0eb3af
to
460001c
Compare
lib/console.js
Outdated
@@ -249,6 +249,75 @@ Console.prototype.groupEnd = function groupEnd() { | |||
this[kGroupIndent].slice(0, this[kGroupIndent].length - 2); | |||
}; | |||
|
|||
const untablableTypes = [ | |||
'symbol', 'undefined', 'boolean', 'number', 'string', 'function', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
function
works just fine in Chrome: console.table([function a() {}, function b() {}])
.
lib/console.js
Outdated
]; | ||
|
||
const untablableValues = [ | ||
undefined, null, NaN, true, false, global, process, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are global
and process
singled out?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
they are objectively easier to read when not run through the table method, and the spec says Fall back to just logging the argument if it can’t be parsed as tabular.
which i think is fairly true for both of those.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree in this case that it is going to be easier to read. The question is if we want to have a special handling here. And there could theoretically be other things that are difficult to read as well because they have lots of properties.
In chrome window
works as tableable and I think that is comparable. I guess most people will not add process
or global
so I would say either we should not special handle them at all or we add a limit that by checking the property number.
lib/console.js
Outdated
if (isSet(O)) | ||
return Array.from({ length: O.size }, (e, i) => i); | ||
if (isSetIterator(O)) | ||
return Array.from(O).map((i) => i - 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will exhaust O
, preventing it from further usage; use the previewMapIterator
and previewSetIterator
from internal/v8
instead. Make sure to set a limit in how many elements to allow. See how it's used in util.format
(the limit is 100 in this case):
Lines 821 to 840 in c84582c
function formatCollectionIterator(preview, ctx, value, recurseTimes, | |
visibleKeys, keys) { | |
var nextRecurseTimes = recurseTimes === null ? null : recurseTimes - 1; | |
var vals = preview(value, 100); | |
var output = []; | |
for (const o of vals) { | |
output.push(formatValue(ctx, o, nextRecurseTimes)); | |
} | |
return output; | |
} | |
function formatMapIterator(ctx, value, recurseTimes, visibleKeys, keys) { | |
return formatCollectionIterator(previewMapIterator, ctx, value, recurseTimes, | |
visibleKeys, keys); | |
} | |
function formatSetIterator(ctx, value, recurseTimes, visibleKeys, keys) { | |
return formatCollectionIterator(previewSetIterator, ctx, value, recurseTimes, | |
visibleKeys, keys); | |
} |
lib/console.js
Outdated
if (isSetIterator(O)) | ||
return Array.from(O).map((i) => i - 1); | ||
if (isMap(O)) | ||
O = O.keys(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you mean return [...O.keys()];
?
lib/console.js
Outdated
if (isMap(O)) | ||
O = O.keys(); | ||
if (isMapIterator(O)) | ||
return Array.from(O); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as SetIterator
lib/console.js
Outdated
untablableValues.includes(value) || | ||
untablableTypes.includes(typeof value); | ||
|
||
const { isSet, isMap, isSetIterator, isMapIterator } = process.binding('util'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move to the top
test/parallel/test-console-table.js
Outdated
assert.strictEqual(queue.shift(), `(index) a b | ||
0 1 undefined | ||
1 undefined 2 | ||
`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs more tests:
console.table([3, 4, { a: 3 }]);
console.table([3, 4, { a: 3 }], ['nonexistent']);
console.table([3, 4, { a: 3 }], ['Value']);
console.table([3, 4, { Value: 3 }], ['Value']);
console.table([,,,,,,,,,4]);
console.table([[,,,,,,,,,4]]);
console.table([[,,,,,,,,,{}]]);
// etc.
// Check browsers for sensible behaviors.
Also needed to test are Map objects, Map Iterator objects, Set objects, Set Iterator objects.
Ping @devsnek |
yeah sorry i've been meaning to get to this, its on my list |
460001c
to
71a8035
Compare
@Trott @TimothyGu changes have been made |
doc/api/console.md
Outdated
``` | ||
|
||
```js | ||
console.log([{ a: 1, b: 'Y' }, { a: 'Z', b: 2 }], ['a']); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess console.log
should actually be console.table
.
lib/console.js
Outdated
@@ -249,6 +252,74 @@ Console.prototype.groupEnd = function groupEnd() { | |||
this[kGroupIndent].slice(0, this[kGroupIndent].length - 2); | |||
}; | |||
|
|||
const untablableTypes = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shall we maybe call it untableable
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doesn't really make sense, the method below is already named that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You misunderstood my comment: I did not mean the whole variable name but only the beginning. And that for all of these variables.
The difference is the e
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh. i have no idea. pretty sure tableable
/tablable
isn't english anyway so i have no idea where to go with that. i think following other words it would become tablible
but that looks really wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe @Trott has a opinion about it?
lib/console.js
Outdated
]; | ||
|
||
const untablableValues = [ | ||
undefined, null, NaN, true, false, global, process, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree in this case that it is going to be easier to read. The question is if we want to have a special handling here. And there could theoretically be other things that are difficult to read as well because they have lots of properties.
In chrome window
works as tableable and I think that is comparable. I guess most people will not add process
or global
so I would say either we should not special handle them at all or we add a limit that by checking the property number.
lib/console.js
Outdated
|
||
function getTablableKeysOf(O) { | ||
if (isSet(O)) | ||
return Array.from({ length: O.size }, (e, i) => i); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The spec says it should log the properties. We could show those and add a special handling for them but e.g. Chrome does not show them.
The same applies to the entries below.
lib/console.js
Outdated
else | ||
item = item[column]; | ||
} | ||
r += `\t${item}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will always print \tundefined
for Map
and Set
but it is possible to get the value from those.
lib/console.js
Outdated
for (const column of columns) { | ||
let item = data[row]; | ||
if (deep) { | ||
if (isMap(item)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can probably never be reached. The same applies to isSet
.
test/parallel/test-console-table.js
Outdated
2 3 | ||
`); | ||
|
||
test([3, 4, { a: 3 }], ['nonexistent'], `(index) a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Chrome shows this as the following that I think would be great to have:
(index) Value a
0 3
1 4
2 3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i don't understand the output of that table
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no undefined
value for a
in index 0 and 1. The value does not exist. But there is a value and we can show that with 3
and 4
.
Just enter console.table([3, 4, { a: 3 }])
in chrome to see the output :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BridgeAR There isn’t really a standard on what should be printed. As long as the output makes sense, which IMO it does, it’s good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@TimothyGu I am aware that there is no standard for it but I feel like the output used in chrome does make more sense and provides a relatively good user experience.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm in favour of the change requested by @BridgeAR here, in relation to missing vs undefined
values. I think it would go a long way towards making this feature more useful.
test/parallel/test-console-table.js
Outdated
|
||
test([{ a: 1 }, { b: 2 }], `(index) a b | ||
0 1 undefined | ||
1 undefined 2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be best to add extra padding up to the value length that has the highest length of the column. That way it will improve the readability massively.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i was considering that but i decided that as this isn't really intended to do more than humor the spec, and in general tables in terminals have a history of being formatted with single tabs without compensation (especially in configs and stuff) so i wanted to stay consistent with that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like it improves the readability a lot, so I would really like to add that. I do not see any issues about inconsistencies here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Showing the value undefined
is actually wrong here out of my perspective because there is just no value. So it should be empty instead of showing undefined. Otherwise this can not be distinguished from the value undefined
.
As an extra test I would add:
test(
[{ a: 1 }, { b: undefined }],
'(index) a b' +
'0 1 ' +
'1 undefined'
)
lib/console.js
Outdated
if (isSet(O)) | ||
return Array.from({ length: O.size }, (e, i) => i); | ||
if (isSetIterator(O)) | ||
return previewSetIterator(O, 50); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like we should handle iterators as not tableable
.
7c583c7
to
0f8b7d7
Compare
Ping @devsnek |
lib/console.js
Outdated
'symbol', 'undefined', 'boolean', 'number', 'string', | ||
]; | ||
|
||
const untablableValues = [undefined, null, NaN, true, false]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The booleans, undefined and NaN are actually redundant to the untablableTypes
. So it would be best to remove the untablableValues
and just explicitely test for null
in the untableable
function.
lib/console.js
Outdated
@@ -249,6 +252,74 @@ Console.prototype.groupEnd = function groupEnd() { | |||
this[kGroupIndent].slice(0, this[kGroupIndent].length - 2); | |||
}; | |||
|
|||
const untablableTypes = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe @Trott has a opinion about it?
lib/console.js
Outdated
const untablableValues = [undefined, null, NaN, true, false]; | ||
|
||
const untablable = (value) => | ||
Object.keys(value).length > 20 || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check has to be last. Otherwise it will fail for null
and undefined
. Please add a test for this as well.
lib/console.js
Outdated
const item = data[row]; | ||
if (!item || (typeof item !== 'object' && typeof item !== 'function')) | ||
continue; | ||
for (const key of getTablableKeysOf(data[row])) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
data[row]
should be replaced with item
here.
test/parallel/test-console-table.js
Outdated
|
||
test([{ a: 1 }, { b: 2 }], `(index) a b | ||
0 1 undefined | ||
1 undefined 2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Showing the value undefined
is actually wrong here out of my perspective because there is just no value. So it should be empty instead of showing undefined. Otherwise this can not be distinguished from the value undefined
.
As an extra test I would add:
test(
[{ a: 1 }, { b: undefined }],
'(index) a b' +
'0 1 ' +
'1 undefined'
)
bdb7755
to
6399953
Compare
@BridgeAR new code is here, i'm planning to clean up cli_table.js a bit but the behavior is where i want it to be |
2b8769b
to
3288a89
Compare
be31b6a
to
c36dd94
Compare
lib/console.js
Outdated
@@ -249,6 +253,86 @@ Console.prototype.groupEnd = function groupEnd() { | |||
this[kGroupIndent].slice(0, this[kGroupIndent].length - 2); | |||
}; | |||
|
|||
const untablable = (value) => | |||
value == null || | |||
['symbol', 'undefined', 'boolean', 'number', 'string'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
undefined
is redundant to value == null
. Either the check above should be strict equal or the undefined
should be removed here.
c579032
to
8382250
Compare
87ea21f
to
5eea243
Compare
doc/api/console.md
Outdated
--> | ||
|
||
* `tabularData` {any} | ||
* `properties` {Array<String>} Alternate properties for constructing the table. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
string[]
|
||
test(new Map([ ['a', 1], [Symbol(), [2]] ]), ` | ||
┌───────────────────┬──────────┬────────┐ | ||
│ (iteration index) │ Key │ Values │ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Keys
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually: we might want to change Values
to Value
instead. Because it is a single value at the specific index position.
b50f098
to
600c45b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not want to block this any further from landing. So LGTM. I would like to get some further adjustments in but I can open a PR for that at some point in the future.
600c45b
to
77b8fd2
Compare
77b8fd2
to
8a0babe
Compare
as a heads-up i will be landing this in a few hours |
landed in 97ace04 |
PR-URL: #18137 Reviewed-By: Tiancheng "Timothy" Gu <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]>
Don't forget the |
Should this be backported to |
PR-URL: nodejs#18137 Reviewed-By: Tiancheng "Timothy" Gu <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]>
Method |
@vitaly-t I can think of two ways: 1) check for the version. Everything from 10.x on does something. It will likely be backported as well. So that might not be a reliable check. 2) Hook into stdout while a module is loaded and do a feature check. |
PR-URL: nodejs#18137 Reviewed-By: Tiancheng "Timothy" Gu <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]>
Refs https://console.spec.whatwg.org/#table
new super fine console.table method
i've been using firefox as my model for how the outputs should look
some column ordering stuff:
properties
is passedValues
column is always last, independent fromproperties
Key
column always directly beforeValues
column, independent fromproperties
(index)
and(iteration index)
columns are always first, independent fromproperties
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesAffected core subsystem(s)
console