Skip to content

Commit

Permalink
console: add console.count() and console.clear()
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
jasnell committed Aug 2, 2017
1 parent 98bae29 commit cc43c8f
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 0 deletions.
70 changes: 70 additions & 0 deletions doc/api/console.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,76 @@ console.assert(false, 'this message will print, but no error thrown');
console.log('this will also print');
```

### console.clear()
<!-- YAML
added: REPLACEME
-->

When `stdout` is a TTY, calling `console.clear()` will attempt to clear the
TTY. When `stdout` is not a TTY, this method does nothing.

*Note*: The specific operation of `console.clear()` can vary across operating
systems and terminal types. For most Linux operating systems, `console.clear()`
operates similarly to the `clear` shell command. On Windows, `console.clear()`
will clear only the output in the current terminal viewport for the Node.js
binary.

### console.count([label])
<!-- YAML
added: REPLACEME
-->

* `label` {string} The display label for the counter. Defaults to `'default'`.

Maintains an internal counter specific to `label` and outputs to `stdout` the
number of times `console.count()` has been called with the given `label`.

<!-- eslint-skip -->
```js
> console.count()
default: 1
undefined
> console.count('default')
default: 2
undefined
> console.count('abc')
abc: 1
undefined
> console.count('xyz')
xyz: 1
undefined
> console.count('abc')
abc: 2
undefined
> console.count()
default: 3
undefined
>
```

### console.countReset([label = 'default'])
<!-- YAML
added: REPLACEME
-->

* `label` {string} The display label for the counter. Defaults to `'default'`.

Resets the internal counter specific to `label`.

<!-- eslint-skip -->
```js
> console.count('abc');
abc: 1
undefined
> console.countReset('abc');
undefined
> console.count('abc');
abc: 1
undefined
>
```
<!-- eslint-enable -->

### console.dir(obj[, options])
<!-- YAML
added: v0.1.101
Expand Down
39 changes: 39 additions & 0 deletions lib/console.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

const errors = require('internal/errors');
const util = require('util');
const kCounts = Symbol('counts');

function Console(stdout, stderr, ignoreErrors = true) {
if (!(this instanceof Console)) {
Expand Down Expand Up @@ -55,6 +56,8 @@ function Console(stdout, stderr, ignoreErrors = true) {
prop.value = createWriteErrorHandler(stderr);
Object.defineProperty(this, '_stderrErrorHandler', prop);

this[kCounts] = new Map();

// bind the prototype functions to this Console instance
var keys = Object.keys(Console.prototype);
for (var v = 0; v < keys.length; v++) {
Expand Down Expand Up @@ -166,6 +169,42 @@ Console.prototype.assert = function assert(expression, ...args) {
}
};

// Defined by: https://console.spec.whatwg.org/#clear
Console.prototype.clear = function clear() {
// It only makes sense to clear if _stdout is a TTY.
// Otherwise, do nothing.
if (this._stdout.isTTY) {
// The require is here intentionally to avoid readline being
// required too early when console is first loaded.
const { cursorTo, clearScreenDown } = require('readline');
cursorTo(this._stdout, 0, 0);
clearScreenDown(this._stdout);
}
};

// Defined by: https://console.spec.whatwg.org/#count
Console.prototype.count = function count(label = 'default') {
// Ensures that label is a string, and only things that can be
// coerced to strings. e.g. Symbol is not allowed
label = `${label}`;
const counts = this[kCounts];
let count = counts.get(label);
if (count === undefined)
count = 1;
else
count++;
counts.set(label, count);
this.log(`${label}: ${count}`);
};

// Not yet defined by the https://console.spec.whatwg.org, but
// proposed to be added and currently implemented by Edge. Having
// the ability to reset counters is important to help prevent
// the counter from being a memory leak.
Console.prototype.countReset = function countReset(label = 'default') {
const counts = this[kCounts];
counts.delete(`${label}`);
};

module.exports = new Console(process.stdout, process.stderr);
module.exports.Console = Console;
Expand Down
22 changes: 22 additions & 0 deletions test/parallel/test-console-clear.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict';

require('../common');
const assert = require('assert');

const stdoutWrite = process.stdout.write;

// The sequence for moving the cursor to 0,0 and clearing screen down
const check = '\u001b[1;1H\u001b[0J';

function doTest(isTTY, check) {
let buf = '';
process.stdout.isTTY = isTTY;
process.stdout.write = (string) => buf += string;
console.clear();
process.stdout.write = stdoutWrite;
assert.strictEqual(buf, check);
}

// Fake TTY
doTest(true, check);
doTest(false, '');
63 changes: 63 additions & 0 deletions test/parallel/test-console-count.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
'use strict';

require('../common');
const assert = require('assert');

const stdoutWrite = process.stdout.write;

let buf = '';

process.stdout.write = (string) => buf = string;

console.count();
assert.strictEqual(buf, 'default: 1\n');

// 'default' and undefined are equivalent
console.count('default');
assert.strictEqual(buf, 'default: 2\n');

console.count('a');
assert.strictEqual(buf, 'a: 1\n');

console.count('b');
assert.strictEqual(buf, 'b: 1\n');

console.count('a');
assert.strictEqual(buf, 'a: 2\n');

console.count();
assert.strictEqual(buf, 'default: 3\n');

console.count({});
assert.strictEqual(buf, '[object Object]: 1\n');

console.count(1);
assert.strictEqual(buf, '1: 1\n');

console.count(null);
assert.strictEqual(buf, 'null: 1\n');

console.count('null');
assert.strictEqual(buf, 'null: 2\n');

console.countReset();
console.count();
assert.strictEqual(buf, 'default: 1\n');

console.countReset('a');
console.count('a');
assert.strictEqual(buf, 'a: 1\n');

// countReset('a') only reset the a counter
console.count();
assert.strictEqual(buf, 'default: 2\n');

process.stdout.write = stdoutWrite;

// Symbol labels do not work
assert.throws(
() => console.count(Symbol('test')),
/^TypeError: Cannot convert a Symbol value to a string$/);
assert.throws(
() => console.countReset(Symbol('test')),
/^TypeError: Cannot convert a Symbol value to a string$/);

0 comments on commit cc43c8f

Please sign in to comment.