Skip to content

Instantly share code, notes, and snippets.

@cookiengineer
Last active August 25, 2021 15:06
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save cookiengineer/05581bc59f5d7d3dbaa09e6018678740 to your computer and use it in GitHub Desktop.
Save cookiengineer/05581bc59f5d7d3dbaa09e6018678740 to your computer and use it in GitHub Desktop.
A better console API for node.js, with support for all data types and pretty-printing using shell color codes
(function() {
let _std_out = '';
let _std_err = '';
/*
* HELPERS
*/
const _INDENT = ' ';
const _WHITESPACE = new Array(512).fill(' ').join('');
const _format_date = function(n) {
return n < 10 ? '0' + n : '' + n;
};
const _stringify = function(data, indent) {
indent = typeof indent === 'string' ? indent : '';
let str = '';
if (
typeof data === 'boolean'
|| data === null
|| data === undefined
|| (
typeof data === 'number'
&& (
data === Infinity
|| data === -Infinity
|| isNaN(data) === true
)
)
) {
if (data === null) {
str = indent + 'null';
} else if (data === undefined) {
str = indent + 'undefined';
} else if (data === false) {
str = indent + 'false';
} else if (data === true) {
str = indent + 'true';
} else if (data === Infinity) {
str = indent + 'Infinity';
} else if (data === -Infinity) {
str = indent + '-Infinity';
} else if (isNaN(data) === true) {
str = indent + 'NaN';
}
} else if (typeof data === 'number') {
str = indent + data.toString();
} else if (typeof data === 'string') {
str = indent + '"' + data + '"';
} else if (typeof data === 'function') {
let body = data.toString().split('\n');
let offset = 0;
let first = body.find(function(ch) {
return ch.startsWith('\t');
}) || null;
if (first !== null) {
let check = /(^\t+)/g.exec(first);
if (check !== null) {
offset = Math.max(0, check[0].length - indent.length);
}
}
for (let b = 0, bl = body.length; b < bl; b++) {
let line = body[b];
if (line.startsWith('\t')) {
str += indent + line.substr(offset);
} else {
str += indent + line;
}
str += '\n';
}
} else if (data instanceof Array) {
let is_primitive = data.find(function(val) {
return val instanceof Object || typeof val === 'function';
}) === undefined;
let dimension = Math.sqrt(data.length, 2);
let is_matrix = dimension === (dimension | 0);
if (data.length === 0) {
str = indent + '[]';
} else if (is_primitive === true && is_matrix === true) {
let max = 0;
for (let d = 0, dl = data.length; d < dl; d++) {
max = Math.max(max, (data[d]).toString().length);
}
str = indent;
str += '[\n';
for (let y = 0; y < dimension; y++) {
str += '\t' + indent;
for (let x = 0; x < dimension; x++) {
let tmp = (data[x + y * dimension]).toString();
if (tmp.length < max) {
str += _WHITESPACE.substr(0, max - tmp.length);
}
str += tmp;
if (x < dimension - 1) {
str += ', ';
}
}
if (y < dimension - 1) {
str += ',';
}
str += '\n';
}
str += indent + ']';
} else if (is_primitive === true) {
str = indent;
str += '[';
for (let d = 0, dl = data.length; d < dl; d++) {
if (d === 0) {
str += ' ';
}
str += _stringify(data[d]);
if (d < dl - 1) {
str += ', ';
} else {
str += ' ';
}
}
str += ']';
} else {
str = indent;
str += '[\n';
for (let d = 0, dl = data.length; d < dl; d++) {
str += _stringify(data[d], '\t' + indent);
if (d < dl - 1) {
str += ',';
}
str += '\n';
}
str += indent + ']';
}
} else if (data instanceof Date) {
str = indent;
str += data.getUTCFullYear() + '-';
str += _format_date(data.getUTCMonth() + 1) + '-';
str += _format_date(data.getUTCDate()) + 'T';
str += _format_date(data.getUTCHours()) + ':';
str += _format_date(data.getUTCMinutes()) + ':';
str += _format_date(data.getUTCSeconds()) + 'Z';
} else if (data instanceof Object) {
let keys = Object.keys(data);
if (keys.length === 0) {
str = indent + '{}';
} else {
str = indent;
str += '{\n';
for (let k = 0, kl = keys.length; k < kl; k++) {
let key = keys[k];
str += '\t' + indent + '"' + key + '": ';
str += _stringify(data[key], '\t' + indent).trim();
if (k < kl - 1) {
str += ',';
}
str += '\n';
}
str += indent + '}';
}
}
return str;
};
const _args_to_string = function(args, offset) {
offset = typeof offset === 'number' ? offset : null;
let output = [];
let columns = process.stdout.columns;
for (let a = 0, al = args.length; a < al; a++) {
let value = args[a];
let o = 0;
if (typeof value === 'function') {
let tmp = (value).toString().split('\n');
for (let t = 0, tl = tmp.length; t < tl; t++) {
output.push(tmp[t].replace(/\t/g, _INDENT));
}
o = output.length - 1;
} else if (value instanceof Object) {
let tmp = _stringify(value).split('\n');
if (tmp.length > 1) {
for (let t = 0, tl = tmp.length; t < tl; t++) {
output.push(tmp[t].replace(/\t/g, _INDENT));
}
o = output.length - 1;
} else {
let chunk = output[o];
if (chunk === undefined) {
output[o] = tmp[0].trim();
} else {
output[o] = (chunk + ' ' + tmp[0]).trim();
}
}
} else if (typeof value === 'string' && value.includes('\n')) {
let tmp = value.split('\n');
for (let t = 0, tl = tmp.length; t < tl; t++) {
output.push(tmp[t].replace(/\t/g, _INDENT));
}
o = output.length - 1;
} else {
let chunk = output[o];
if (chunk === undefined) {
output[o] = ('' + value).replace(/\t/g, _INDENT).trim();
} else {
output[o] = (chunk + (' ' + value).replace(/\t/g, _INDENT)).trim();
}
}
}
let ol = output.length;
if (ol > 1) {
if (offset !== null) {
for (let o = 0; o < ol; o++) {
let line = output[o];
let maxl = (o === 0 || o === ol - 1) ? (columns - offset) : columns;
if (line.length > maxl) {
output[o] = line.substr(0, maxl);
} else {
output[o] = line + _WHITESPACE.substr(0, maxl - line.length);
}
}
}
return output.join('\n');
} else {
if (offset !== null) {
let line = output[0];
let maxl = columns - offset * 2;
if (line.length > maxl) {
line = line.substr(0, maxl);
} else {
line = line + _WHITESPACE.substr(0, maxl - line.length);
}
return line;
}
return output[0];
}
};
/*
* IMPLEMENTATION
*/
console.clear = function() {
// clear screen
// process.stdout.write('\x1B[2J');
// clear screen and reset cursor
process.stdout.write('\x1B[2J\x1B[0f');
// clear scroll buffer
process.stdout.write('\u001b[3J');
};
console.log = function() {
let al = arguments.length;
let args = [ '(L)' ];
for (let a = 0; a < al; a++) {
args.push(arguments[a]);
}
_std_out += _args_to_string(args) + '\n';
process.stdout.write('\u001b[49m\u001b[97m ' + _args_to_string(args, 1) + ' \u001b[39m\u001b[49m\u001b[0m\n');
};
console.info = function() {
let al = arguments.length;
let args = [ '(I)' ];
for (let a = 0; a < al; a++) {
args.push(arguments[a]);
}
_std_out += _args_to_string(args) + '\n';
process.stdout.write('\u001b[42m\u001b[97m ' + _args_to_string(args, 1) + ' \u001b[39m\u001b[49m\u001b[0m\n');
};
console.warn = function() {
let al = arguments.length;
let args = [ '(W)' ];
for (let a = 0; a < al; a++) {
args.push(arguments[a]);
}
_std_out += _args_to_string(args) + '\n';
process.stdout.write('\u001b[43m\u001b[97m ' + _args_to_string(args, 1) + ' \u001b[39m\u001b[49m\u001b[0m\n');
};
console.error = function() {
let al = arguments.length;
let args = [ '(E)' ];
for (let a = 0; a < al; a++) {
args.push(arguments[a]);
}
_std_err += _args_to_string(args) + '\n';
process.stderr.write('\u001b[41m\u001b[97m ' + _args_to_string(args, 1) + ' \u001b[39m\u001b[49m\u001b[0m\n');
};
console.deserialize = function(blob) {
if (typeof blob.stdout === 'string') {
_std_out = blob.stdout;
}
if (typeof blob.stderr === 'string') {
_std_err = blob.stderr;
}
};
console.serialize = function() {
let blob = {};
if (_std_out.length > 0) blob.stdout = _std_out;
if (_std_err.length > 0) blob.stderr = _std_err;
return {
'reference': 'console',
'blob': Object.keys(blob).length > 0 ? blob : null
};
};
})();
#!/usr/bin/env node
require('./console-polyfill.js');
let data1 = {
foo: {
bar: {
qux: [
1,
2.3,
true,
false,
Infinity,
-Infinity
]
}
}
};
let data2 = {
time: new Date(),
method: function(a, b, c) {
// Look ma, it's correctly indented!
if (a === b) {
return c + 1;
}
return a + b;
},
matrix: [
1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16
]
};
console.log('Better console API demo');
console.info(new Date());
console.log(data1);
console.warn(data2.time);
console.log(data2);
console.info(new Date());
@mandaputtra
Copy link

Nice one thanks! 💯

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment