/* eslint-disable no-console, no-shadow */
import System from '../System';
// increase error stack trace limit
Error.stackTraceLimit = 30;
const chalk = require('chalk');
const _ = require('lodash');
const stackTrace = require('./stack_trace');
const nodeUtil = require('./node_util_copy');
function errorFrameToString(frame, background = 'magenta', foreground = 'white') {
const fileName = frame.getRelativeFileName();
if (!fileName) {
return chalk.bgYellow(
' ' +
chalk.black.bold(
_.repeat('-', process.stdout.columns - 2)
)
);
}
const lineNumber = frame.getLineNumber();
const columnNumber = frame.getColumnNumber();
const functionName = frame.getFunctionNameSanitized() || 'module';
const header = ` ${fileName}:${lineNumber}:${columnNumber} in ${functionName}`;
const headerSpace = _.repeat(' ', process.stdout.columns - header.length);
return chalk[`bg${_.upperFirst(background)}`](
' ' +
chalk[foreground](
chalk.bold(fileName) +
':' +
chalk.bold(lineNumber) +
':' +
columnNumber +
' in ' +
chalk.bold(functionName) +
headerSpace
)
);
}
function trace(error = '') {
let h1 = 'Tracing';
let name = error;
if (_.isError(error)) {
h1 = error.name;
name = error.message;
}
let stack;
if (!error) {
stack = stackTrace.get(new Error());
stack.shift();
}
else {
stack = stackTrace.get(error);
}
// eslint-disable-next-line no-control-regex
const h1Length = h1.replace(/\u001b\[\d\d?(;\d\d?)?m/g, '').length;
if (name.length > process.stdout.columns - h1Length - 3 - name.length) {
console.log(chalk.bgBlue(chalk.bold(chalk.white(' ' + h1 + ' \n ') + chalk.yellow(name))));
}
else {
const h1Space = _.repeat(' ', process.stdout.columns - h1Length - 2 - name.length);
console.log(chalk.bgBlue(chalk.white.bold(' ' + h1 + ' ' + chalk.yellow.bold(name) + h1Space)));
}
stack.forEach((frame) => {
const fileName = frame.getRelativeFileName();
const columnNumber = frame.getColumnNumber();
if (!frame.isApp() || fileName.indexOf('node_modules') !== -1) {
console.log(errorFrameToString(frame, 'black', 'white'));
return;
}
const line = frame.getContext(3).line;
if (!line) {
console.log(errorFrameToString(frame, 'black', 'white'));
return;
}
const marker = _.repeat(' ', columnNumber - 1) + '^';
console.log(
errorFrameToString(frame, 'magenta', 'white') +
'\n' +
chalk.blue.bold(' ' + frame.getContext(3).pre.join('\n ').replace(/\t/g, ' ')) +
'\n ' +
chalk.yellow.bold(line.replace(/\t/g, ' ')) +
'\n ' +
chalk.yellow.bold(marker) +
'\n ' +
chalk.blue.bold(frame.getContext(3).post.join('\n ').replace(/\t/g, ' '))
);
});
const lineSep = _.repeat('⁻', process.stdout.columns);
console.log(chalk.blue(lineSep));
}
function dumpSingle(arg, options = {}) {
if (arg instanceof Error) {
trace(arg);
return;
}
const opts = _.assign({
showHidden: false,
depth: null,
colors: true,
breakLength: 1,
}, options);
console.log(nodeUtil.inspect(arg, opts));
}
function dump(...args) {
args.forEach((arg) => {
dumpSingle(arg);
});
}
/**
* Colored Log to console with stack trace
* @param {Array<any>} args Args to log to console
*/
function d(...args) {
const prevStackLimit = Error.stackTraceLimit;
Error.stackTraceLimit = 2;
const stack = stackTrace.get(new Error());
Error.stackTraceLimit = prevStackLimit;
const frame = stack[1];
const fileName = frame.getRelativeFileName();
const lineNumber = frame.getLineNumber();
const columnNumber = frame.getColumnNumber();
const functionName = frame.getFunctionNameSanitized() || 'module';
const line = _.trim(frame.getContext().line, ' \t\n;');
const upperLine = `${fileName}:${lineNumber}:${columnNumber} in ${functionName}`;
const upperExtraSpace = _.repeat(' ', Math.max(0, process.stdout.columns - upperLine.length - 1));
const lowerExtraSpace = _.repeat(' ', Math.max(0, process.stdout.columns - line.length - 1));
console.log(
chalk.bgBlue(
' ' +
chalk.white(
chalk.bold(fileName) +
':' +
chalk.bold(lineNumber) +
':' +
columnNumber +
' in ' +
chalk.bold(functionName)
) +
upperExtraSpace +
'\n ' +
chalk.yellow.bold(line) +
lowerExtraSpace
)
);
args.forEach((arg) => {
dumpSingle(arg);
});
const lineSep = _.repeat('⁻', process.stdout.columns);
console.log(chalk.blue(lineSep));
}
function uncaughtExceptionHandler(err) {
err.name = chalk.bgRed.white(' FATAL ') + ' ' + err.name;
d.trace(err);
System.exit(1);
}
function unhandledRejectionHandler(err) {
err.name = chalk.bgRed.white(' UNHANDLED ') + ' ' + err.name;
d.trace(err);
}
function enableUncaughtHandler({exceptions = true, rejections = true} = {}) {
if (exceptions) process.on('uncaughtException', uncaughtExceptionHandler);
if (rejections) process.on('unhandledRejection', unhandledRejectionHandler);
}
function disableUncaughtHandler({exceptions = true, rejections = true} = {}) {
if (exceptions) process.off('uncaughtException', uncaughtExceptionHandler);
if (rejections) process.off('unhandledRejection', unhandledRejectionHandler);
}
d.trace = trace;
d.getTrace = stackTrace.get;
d.dump = dump;
d.enableUncaughtHandler = enableUncaughtHandler;
d.disableUncaughtHandler = disableUncaughtHandler;
// enable uncaught exception handler by default
d.enableUncaughtHandler();
module.exports = d;