Skip to content

Commit 9c1199e

Browse files
jasnelladdaleax
authored andcommitted
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 <anna@addaleax.net> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Daijiro Wachi <daijiro.wachi@gmail.com> Reviewed-By: Timothy Gu <timothygu99@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
1 parent 91b7843 commit 9c1199e

File tree

4 files changed

+194
-0
lines changed

4 files changed

+194
-0
lines changed

doc/api/console.md

Lines changed: 70 additions & 0 deletions
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

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
'use strict';
2323

2424
const util = require('util');
25+
const kCounts = Symbol('counts');
2526

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

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

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

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

test/parallel/test-console-clear.js

Lines changed: 22 additions & 0 deletions
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

Lines changed: 63 additions & 0 deletions
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)