prettyjson.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. 'use strict';
  2. // ### Module dependencies
  3. require('colors');
  4. var Utils = require('./utils');
  5. exports.version = require('../package.json').version;
  6. // ### Render function
  7. // *Parameters:*
  8. //
  9. // * **`data`**: Data to render
  10. // * **`options`**: Hash with different options to configure the parser
  11. // * **`indentation`**: Base indentation of the parsed output
  12. //
  13. // *Example of options hash:*
  14. //
  15. // {
  16. // emptyArrayMsg: '(empty)', // Rendered message on empty strings
  17. // keysColor: 'blue', // Color for keys in hashes
  18. // dashColor: 'red', // Color for the dashes in arrays
  19. // stringColor: 'grey', // Color for strings
  20. // defaultIndentation: 2 // Indentation on nested objects
  21. // }
  22. exports.render = function render(data, options, indentation) {
  23. // Default values
  24. indentation = indentation || 0;
  25. options = options || {};
  26. options.emptyArrayMsg = options.emptyArrayMsg || '(empty array)';
  27. options.keysColor = options.keysColor || 'green';
  28. options.dashColor = options.dashColor || 'green';
  29. options.numberColor = options.numberColor || 'blue';
  30. options.defaultIndentation = options.defaultIndentation || 2;
  31. options.noColor = !!options.noColor;
  32. options.stringColor = options.stringColor || null;
  33. var output = [];
  34. // Helper function to detect if an object can be directly serializable
  35. var isSerializable = function(input, onlyPrimitives) {
  36. if (typeof input === 'boolean' ||
  37. typeof input === 'number' || input === null ||
  38. input instanceof Date) {
  39. return true;
  40. }
  41. if (typeof input === 'string' && input.indexOf('\n') === -1) {
  42. return true;
  43. }
  44. if (options.inlineArrays && !onlyPrimitives) {
  45. if (Array.isArray(input) && isSerializable(input[0], true)) {
  46. return true;
  47. }
  48. }
  49. return false;
  50. };
  51. var indentLines = function(string, spaces){
  52. var lines = string.split('\n');
  53. lines = lines.map(function(line){
  54. return Utils.indent(spaces) + line;
  55. });
  56. return lines.join('\n');
  57. };
  58. var addColorToData = function(input) {
  59. if (options.noColor) {
  60. return input;
  61. }
  62. if (typeof input === 'string') {
  63. // Print strings in regular terminal color
  64. return options.stringColor ? input[options.stringColor] : input;
  65. }
  66. var sInput = input + '';
  67. if (input === true) {
  68. return sInput.green;
  69. }
  70. if (input === false) {
  71. return sInput.red;
  72. }
  73. if (input === null) {
  74. return sInput.grey;
  75. }
  76. if (typeof input === 'number') {
  77. return sInput[options.numberColor];
  78. }
  79. if (Array.isArray(input)) {
  80. return input.join(', ');
  81. }
  82. return sInput;
  83. };
  84. // Render a string exactly equal
  85. if (isSerializable(data)) {
  86. output.push(Utils.indent(indentation) + addColorToData(data));
  87. }
  88. else if (typeof data === 'string') {
  89. //unserializable string means it's multiline
  90. output.push(Utils.indent(indentation) + '"""');
  91. output.push(indentLines(data, indentation + options.defaultIndentation));
  92. output.push(Utils.indent(indentation) + '"""');
  93. }
  94. else if (Array.isArray(data)) {
  95. // If the array is empty, render the `emptyArrayMsg`
  96. if (data.length === 0) {
  97. output.push(Utils.indent(indentation) + options.emptyArrayMsg);
  98. } else {
  99. data.forEach(function(element) {
  100. // Prepend the dash at the begining of each array's element line
  101. var line = ('- ');
  102. if (!options.noColor) {
  103. line = line[options.dashColor];
  104. }
  105. line = Utils.indent(indentation) + line;
  106. // If the element of the array is a string, bool, number, or null
  107. // render it in the same line
  108. if (isSerializable(element)) {
  109. line += exports.render(element, options);
  110. output.push(line);
  111. // If the element is an array or object, render it in next line
  112. } else {
  113. output.push(line);
  114. output.push(exports.render(
  115. element, options, indentation + options.defaultIndentation
  116. ));
  117. }
  118. });
  119. }
  120. }
  121. else if (typeof data === 'object') {
  122. // Get the size of the longest index to align all the values
  123. var maxIndexLength = Utils.getMaxIndexLength(data);
  124. var key;
  125. var isError = data instanceof Error;
  126. Object.getOwnPropertyNames(data).forEach(function(i) {
  127. // Prepend the index at the beginning of the line
  128. key = (i + ': ');
  129. if (!options.noColor) {
  130. key = key[options.keysColor];
  131. }
  132. key = Utils.indent(indentation) + key;
  133. // Skip `undefined`, it's not a valid JSON value.
  134. if (data[i] === undefined) {
  135. return;
  136. }
  137. // If the value is serializable, render it in the same line
  138. if (isSerializable(data[i]) && (!isError || i !== 'stack')) {
  139. key += exports.render(data[i], options, maxIndexLength - i.length);
  140. output.push(key);
  141. // If the index is an array or object, render it in next line
  142. } else {
  143. output.push(key);
  144. output.push(
  145. exports.render(
  146. isError && i === 'stack' ? data[i].split('\n') : data[i],
  147. options,
  148. indentation + options.defaultIndentation
  149. )
  150. );
  151. }
  152. });
  153. }
  154. // Return all the lines as a string
  155. return output.join('\n');
  156. };
  157. // ### Render from string function
  158. // *Parameters:*
  159. //
  160. // * **`data`**: Data to render as a string
  161. // * **`options`**: Hash with different options to configure the parser
  162. // * **`indentation`**: Base indentation of the parsed output
  163. //
  164. // *Example of options hash:*
  165. //
  166. // {
  167. // emptyArrayMsg: '(empty)', // Rendered message on empty strings
  168. // keysColor: 'blue', // Color for keys in hashes
  169. // dashColor: 'red', // Color for the dashes in arrays
  170. // defaultIndentation: 2 // Indentation on nested objects
  171. // }
  172. exports.renderString = function renderString(data, options, indentation) {
  173. var output = '';
  174. var parsedData;
  175. // If the input is not a string or if it's empty, just return an empty string
  176. if (typeof data !== 'string' || data === '') {
  177. return '';
  178. }
  179. // Remove non-JSON characters from the beginning string
  180. if (data[0] !== '{' && data[0] !== '[') {
  181. var beginingOfJson;
  182. if (data.indexOf('{') === -1) {
  183. beginingOfJson = data.indexOf('[');
  184. } else if (data.indexOf('[') === -1) {
  185. beginingOfJson = data.indexOf('{');
  186. } else if (data.indexOf('{') < data.indexOf('[')) {
  187. beginingOfJson = data.indexOf('{');
  188. } else {
  189. beginingOfJson = data.indexOf('[');
  190. }
  191. output += data.substr(0, beginingOfJson) + '\n';
  192. data = data.substr(beginingOfJson);
  193. }
  194. try {
  195. parsedData = JSON.parse(data);
  196. } catch (e) {
  197. // Return an error in case of an invalid JSON
  198. return 'Error:'.red + ' Not valid JSON!';
  199. }
  200. // Call the real render() method
  201. output += exports.render(parsedData, options, indentation);
  202. return output;
  203. };