304 lines
8.5 KiB
JavaScript
304 lines
8.5 KiB
JavaScript
'use strict';
|
|
|
|
// ### Module dependencies
|
|
var colors = require('colors/safe');
|
|
var Utils = require('./utils');
|
|
|
|
var conflictChars = /[^\w\s\n\r\v\t\.,]/i;
|
|
|
|
exports.version = require('../package.json').version;
|
|
|
|
// Helper function to detect if an object should be printed or ignored
|
|
var isPrintable = function(input, options) {
|
|
return input !== undefined || options.renderUndefined;
|
|
};
|
|
|
|
// Helper function to detect if an object can be directly serializable
|
|
var isSerializable = function(input, onlyPrimitives, options) {
|
|
if (
|
|
typeof input === 'boolean' ||
|
|
typeof input === 'number' ||
|
|
typeof input === 'function' ||
|
|
input === null ||
|
|
input === undefined ||
|
|
input instanceof Date
|
|
) {
|
|
return true;
|
|
}
|
|
if (typeof input === 'string' && input.indexOf('\n') === -1) {
|
|
return true;
|
|
}
|
|
|
|
if (options.inlineArrays && !onlyPrimitives) {
|
|
if (Array.isArray(input) && isSerializable(input[0], true, options)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
var addColorToData = function(input, options) {
|
|
if (options.noColor) {
|
|
return input;
|
|
}
|
|
|
|
if (typeof input === 'string') {
|
|
// Print strings in regular terminal color
|
|
return options.stringColor ? colors[options.stringColor](input) : input;
|
|
}
|
|
|
|
var sInput = input + '';
|
|
|
|
if (input === true) {
|
|
return colors.green(sInput);
|
|
}
|
|
if (input === false) {
|
|
return colors.red(sInput);
|
|
}
|
|
if (input === null || input === undefined) {
|
|
return colors.grey(sInput);
|
|
}
|
|
if (typeof input === 'number') {
|
|
if (input >= 0) {
|
|
return colors[options.positiveNumberColor](sInput);
|
|
} else {
|
|
return colors[options.negativeNumberColor](sInput);
|
|
}
|
|
}
|
|
if (typeof input === 'function') {
|
|
return 'function() {}';
|
|
}
|
|
|
|
if (Array.isArray(input)) {
|
|
return input.join(', ');
|
|
}
|
|
|
|
return sInput;
|
|
};
|
|
|
|
var colorMultilineString = function(options, line) {
|
|
if (options.multilineStringColor === null || options.noColor) {
|
|
return line;
|
|
} else {
|
|
return colors[options.multilineStringColor](line);
|
|
}
|
|
};
|
|
|
|
var indentLines = function(string, spaces, options){
|
|
var lines = string.split('\n');
|
|
lines = lines.map(function(line){
|
|
return Utils.indent(spaces) + colorMultilineString(options, line);
|
|
});
|
|
return lines.join('\n');
|
|
};
|
|
|
|
var renderToArray = function(data, options, indentation) {
|
|
|
|
if (typeof data === 'string' && data.match(conflictChars) &&
|
|
options.escape) {
|
|
data = JSON.stringify(data);
|
|
}
|
|
|
|
if (!isPrintable(data, options)) {
|
|
return [];
|
|
}
|
|
|
|
if (isSerializable(data, false, options)) {
|
|
return [Utils.indent(indentation) + addColorToData(data, options)];
|
|
}
|
|
|
|
// Unserializable string means it's multiline
|
|
if (typeof data === 'string') {
|
|
return [
|
|
Utils.indent(indentation) + colorMultilineString(options, '"""'),
|
|
indentLines(data, indentation + options.defaultIndentation, options),
|
|
Utils.indent(indentation) + colorMultilineString(options, '"""')
|
|
];
|
|
}
|
|
|
|
if (Array.isArray(data)) {
|
|
// If the array is empty, render the `emptyArrayMsg`
|
|
if (data.length === 0) {
|
|
return [Utils.indent(indentation) + options.emptyArrayMsg];
|
|
}
|
|
|
|
var outputArray = [];
|
|
|
|
data.forEach(function(element) {
|
|
if (!isPrintable(element, options)) {
|
|
return;
|
|
}
|
|
|
|
// Prepend the dash at the begining of each array's element line
|
|
var line = '- ';
|
|
if (!options.noColor) {
|
|
line = colors[options.dashColor](line);
|
|
}
|
|
line = Utils.indent(indentation) + line;
|
|
|
|
// If the element of the array is a string, bool, number, or null
|
|
// render it in the same line
|
|
if (isSerializable(element, false, options)) {
|
|
line += renderToArray(element, options, 0)[0];
|
|
outputArray.push(line);
|
|
|
|
// If the element is an array or object, render it in next line
|
|
} else {
|
|
outputArray.push(line);
|
|
outputArray.push.apply(
|
|
outputArray,
|
|
renderToArray(
|
|
element, options, indentation + options.defaultIndentation
|
|
)
|
|
);
|
|
}
|
|
});
|
|
|
|
return outputArray;
|
|
}
|
|
|
|
if (data instanceof Error) {
|
|
return renderToArray(
|
|
{
|
|
message: data.message,
|
|
stack: data.stack.split('\n')
|
|
},
|
|
options,
|
|
indentation
|
|
);
|
|
}
|
|
|
|
// If values alignment is enabled, get the size of the longest index
|
|
// to align all the values
|
|
var maxIndexLength = options.noAlign ? 0 : Utils.getMaxIndexLength(data);
|
|
var key;
|
|
var output = [];
|
|
|
|
Object.getOwnPropertyNames(data).forEach(function(i) {
|
|
if (!isPrintable(data[i], options)) {
|
|
return;
|
|
}
|
|
|
|
// Prepend the index at the beginning of the line
|
|
key = (i + ': ');
|
|
if (!options.noColor) {
|
|
key = colors[options.keysColor](key);
|
|
}
|
|
key = Utils.indent(indentation) + key;
|
|
|
|
// If the value is serializable, render it in the same line
|
|
if (isSerializable(data[i], false, options)) {
|
|
var nextIndentation = options.noAlign ? 0 : maxIndexLength - i.length;
|
|
key += renderToArray(data[i], options, nextIndentation)[0];
|
|
output.push(key);
|
|
|
|
// If the index is an array or object, render it in next line
|
|
} else {
|
|
output.push(key);
|
|
output.push.apply(
|
|
output,
|
|
renderToArray(
|
|
data[i],
|
|
options,
|
|
indentation + options.defaultIndentation
|
|
)
|
|
);
|
|
}
|
|
});
|
|
return output;
|
|
};
|
|
|
|
// ### Render function
|
|
// *Parameters:*
|
|
//
|
|
// * **`data`**: Data to render
|
|
// * **`options`**: Hash with different options to configure the parser
|
|
// * **`indentation`**: Base indentation of the parsed output
|
|
//
|
|
// *Example of options hash:*
|
|
//
|
|
// {
|
|
// emptyArrayMsg: '(empty)', // Rendered message on empty strings
|
|
// keysColor: 'blue', // Color for keys in hashes
|
|
// dashColor: 'red', // Color for the dashes in arrays
|
|
// stringColor: 'grey', // Color for strings
|
|
// multilineStringColor: 'cyan' // Color for multiline strings
|
|
// defaultIndentation: 2 // Indentation on nested objects
|
|
// }
|
|
exports.render = function render(data, options, indentation) {
|
|
// Default values
|
|
indentation = indentation || 0;
|
|
options = options || {};
|
|
options.emptyArrayMsg = options.emptyArrayMsg || '(empty array)';
|
|
options.keysColor = options.keysColor || 'green';
|
|
options.dashColor = options.dashColor || 'green';
|
|
options.numberColor = options.numberColor || 'blue';
|
|
options.positiveNumberColor = options.positiveNumberColor
|
|
|| options.numberColor;
|
|
options.negativeNumberColor = options.negativeNumberColor
|
|
|| options.numberColor;
|
|
options.defaultIndentation = options.defaultIndentation || 2;
|
|
options.noColor = !!options.noColor;
|
|
options.noAlign = !!options.noAlign;
|
|
options.escape = !!options.escape;
|
|
options.renderUndefined = !!options.renderUndefined;
|
|
|
|
options.stringColor = options.stringColor || null;
|
|
options.multilineStringColor = options.multilineStringColor || null;
|
|
|
|
return renderToArray(data, options, indentation).join('\n');
|
|
};
|
|
|
|
// ### Render from string function
|
|
// *Parameters:*
|
|
//
|
|
// * **`data`**: Data to render as a string
|
|
// * **`options`**: Hash with different options to configure the parser
|
|
// * **`indentation`**: Base indentation of the parsed output
|
|
//
|
|
// *Example of options hash:*
|
|
//
|
|
// {
|
|
// emptyArrayMsg: '(empty)', // Rendered message on empty strings
|
|
// keysColor: 'blue', // Color for keys in hashes
|
|
// dashColor: 'red', // Color for the dashes in arrays
|
|
// defaultIndentation: 2 // Indentation on nested objects
|
|
// }
|
|
exports.renderString = function renderString(data, options, indentation) {
|
|
|
|
var output = '';
|
|
var parsedData;
|
|
// If the input is not a string or if it's empty, just return an empty string
|
|
if (typeof data !== 'string' || data === '') {
|
|
return '';
|
|
}
|
|
|
|
// Remove non-JSON characters from the beginning string
|
|
if (data[0] !== '{' && data[0] !== '[') {
|
|
var beginingOfJson;
|
|
if (data.indexOf('{') === -1) {
|
|
beginingOfJson = data.indexOf('[');
|
|
} else if (data.indexOf('[') === -1) {
|
|
beginingOfJson = data.indexOf('{');
|
|
} else if (data.indexOf('{') < data.indexOf('[')) {
|
|
beginingOfJson = data.indexOf('{');
|
|
} else {
|
|
beginingOfJson = data.indexOf('[');
|
|
}
|
|
output += data.substr(0, beginingOfJson) + '\n';
|
|
data = data.substr(beginingOfJson);
|
|
}
|
|
|
|
try {
|
|
parsedData = JSON.parse(data);
|
|
} catch (e) {
|
|
// Return an error in case of an invalid JSON
|
|
return colors.red('Error:') + ' Not valid JSON!';
|
|
}
|
|
|
|
// Call the real render() method
|
|
output += exports.render(parsedData, options, indentation);
|
|
return output;
|
|
};
|