Skip to content

Commit cc43c8f

Browse files
committed
console: add console.count() and console.clear()
Both are simple utility functions defined by the WHATWG console spec (https://console.spec.whatwg.org/). PR-URL: #12678 Ref: #12675 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Daijiro Wachi <[email protected]> Reviewed-By: Timothy Gu <[email protected]> Reviewed-By: Tobias Nießen <[email protected]>
1 parent 98bae29 commit cc43c8f

File tree

4 files changed

+194
-0
lines changed

4 files changed

+194
-0
lines changed

doc/api/console.md

+70
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,76 @@ console.assert(false, 'this message will print, but no error thrown');
167167
console.log('this will also print');
168168
```
169169

170+
### console.clear()
171+
<!-- YAML
172+
added: REPLACEME
173+
-->
174+
175+
When `stdout` is a TTY, calling `console.clear()` will attempt to clear the
176+
TTY. When `stdout` is not a TTY, this method does nothing.
177+
178+
*Note*: The specific operation of `console.clear()` can vary across operating
179+
systems and terminal types. For most Linux operating systems, `console.clear()`
180+
operates similarly to the `clear` shell command. On Windows, `console.clear()`
181+
will clear only the output in the current terminal viewport for the Node.js
182+
binary.
183+
184+
### console.count([label])
185+
<!-- YAML
186+
added: REPLACEME
187+
-->
188+
189+
* `label` {string} The display label for the counter. Defaults to `'default'`.
190+
191+
Maintains an internal counter specific to `label` and outputs to `stdout` the
192+
number of times `console.count()` has been called with the given `label`.
193+
194+
<!-- eslint-skip -->
195+
```js
196+
> console.count()
197+
default: 1
198+
undefined
199+
> console.count('default')
200+
default: 2
201+
undefined
202+
> console.count('abc')
203+
abc: 1
204+
undefined
205+
> console.count('xyz')
206+
xyz: 1
207+
undefined
208+
> console.count('abc')
209+
abc: 2
210+
undefined
211+
> console.count()
212+
default: 3
213+
undefined
214+
>
215+
```
216+
217+
### console.countReset([label = 'default'])
218+
<!-- YAML
219+
added: REPLACEME
220+
-->
221+
222+
* `label` {string} The display label for the counter. Defaults to `'default'`.
223+
224+
Resets the internal counter specific to `label`.
225+
226+
<!-- eslint-skip -->
227+
```js
228+
> console.count('abc');
229+
abc: 1
230+
undefined
231+
> console.countReset('abc');
232+
undefined
233+
> console.count('abc');
234+
abc: 1
235+
undefined
236+
>
237+
```
238+
<!-- eslint-enable -->
239+
170240
### console.dir(obj[, options])
171241
<!-- YAML
172242
added: v0.1.101

lib/console.js

+39
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
const errors = require('internal/errors');
2525
const util = require('util');
26+
const kCounts = Symbol('counts');
2627

2728
function Console(stdout, stderr, ignoreErrors = true) {
2829
if (!(this instanceof Console)) {
@@ -55,6 +56,8 @@ function Console(stdout, stderr, ignoreErrors = true) {
5556
prop.value = createWriteErrorHandler(stderr);
5657
Object.defineProperty(this, '_stderrErrorHandler', prop);
5758

59+
this[kCounts] = new Map();
60+
5861
// bind the prototype functions to this Console instance
5962
var keys = Object.keys(Console.prototype);
6063
for (var v = 0; v < keys.length; v++) {
@@ -166,6 +169,42 @@ Console.prototype.assert = function assert(expression, ...args) {
166169
}
167170
};
168171

172+
// Defined by: https://console.spec.whatwg.org/#clear
173+
Console.prototype.clear = function clear() {
174+
// It only makes sense to clear if _stdout is a TTY.
175+
// Otherwise, do nothing.
176+
if (this._stdout.isTTY) {
177+
// The require is here intentionally to avoid readline being
178+
// required too early when console is first loaded.
179+
const { cursorTo, clearScreenDown } = require('readline');
180+
cursorTo(this._stdout, 0, 0);
181+
clearScreenDown(this._stdout);
182+
}
183+
};
184+
185+
// Defined by: https://console.spec.whatwg.org/#count
186+
Console.prototype.count = function count(label = 'default') {
187+
// Ensures that label is a string, and only things that can be
188+
// coerced to strings. e.g. Symbol is not allowed
189+
label = `${label}`;
190+
const counts = this[kCounts];
191+
let count = counts.get(label);
192+
if (count === undefined)
193+
count = 1;
194+
else
195+
count++;
196+
counts.set(label, count);
197+
this.log(`${label}: ${count}`);
198+
};
199+
200+
// Not yet defined by the https://console.spec.whatwg.org, but
201+
// proposed to be added and currently implemented by Edge. Having
202+
// the ability to reset counters is important to help prevent
203+
// the counter from being a memory leak.
204+
Console.prototype.countReset = function countReset(label = 'default') {
205+
const counts = this[kCounts];
206+
counts.delete(`${label}`);
207+
};
169208

170209
module.exports = new Console(process.stdout, process.stderr);
171210
module.exports.Console = Console;

test/parallel/test-console-clear.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use strict';
2+
3+
require('../common');
4+
const assert = require('assert');
5+
6+
const stdoutWrite = process.stdout.write;
7+
8+
// The sequence for moving the cursor to 0,0 and clearing screen down
9+
const check = '\u001b[1;1H\u001b[0J';
10+
11+
function doTest(isTTY, check) {
12+
let buf = '';
13+
process.stdout.isTTY = isTTY;
14+
process.stdout.write = (string) => buf += string;
15+
console.clear();
16+
process.stdout.write = stdoutWrite;
17+
assert.strictEqual(buf, check);
18+
}
19+
20+
// Fake TTY
21+
doTest(true, check);
22+
doTest(false, '');

test/parallel/test-console-count.js

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
'use strict';
2+
3+
require('../common');
4+
const assert = require('assert');
5+
6+
const stdoutWrite = process.stdout.write;
7+
8+
let buf = '';
9+
10+
process.stdout.write = (string) => buf = string;
11+
12+
console.count();
13+
assert.strictEqual(buf, 'default: 1\n');
14+
15+
// 'default' and undefined are equivalent
16+
console.count('default');
17+
assert.strictEqual(buf, 'default: 2\n');
18+
19+
console.count('a');
20+
assert.strictEqual(buf, 'a: 1\n');
21+
22+
console.count('b');
23+
assert.strictEqual(buf, 'b: 1\n');
24+
25+
console.count('a');
26+
assert.strictEqual(buf, 'a: 2\n');
27+
28+
console.count();
29+
assert.strictEqual(buf, 'default: 3\n');
30+
31+
console.count({});
32+
assert.strictEqual(buf, '[object Object]: 1\n');
33+
34+
console.count(1);
35+
assert.strictEqual(buf, '1: 1\n');
36+
37+
console.count(null);
38+
assert.strictEqual(buf, 'null: 1\n');
39+
40+
console.count('null');
41+
assert.strictEqual(buf, 'null: 2\n');
42+
43+
console.countReset();
44+
console.count();
45+
assert.strictEqual(buf, 'default: 1\n');
46+
47+
console.countReset('a');
48+
console.count('a');
49+
assert.strictEqual(buf, 'a: 1\n');
50+
51+
// countReset('a') only reset the a counter
52+
console.count();
53+
assert.strictEqual(buf, 'default: 2\n');
54+
55+
process.stdout.write = stdoutWrite;
56+
57+
// Symbol labels do not work
58+
assert.throws(
59+
() => console.count(Symbol('test')),
60+
/^TypeError: Cannot convert a Symbol value to a string$/);
61+
assert.throws(
62+
() => console.countReset(Symbol('test')),
63+
/^TypeError: Cannot convert a Symbol value to a string$/);

0 commit comments

Comments
 (0)