UNPKG

6.78 kBJavaScriptView Raw
1'use strict';
2
3/**
4 * Pretty print a JSON object to the console, if printNonEnumerables
5 * is set then loops through all properties on an object and print them.
6 * @param {Object} json : the JSON object
7 * @param {boolean} printNonEnumerables : print non enumerable properties
8 */
9
10console.json = function (json, printNonEnumerables) {
11 return prettyPrint(json, {
12 all: printNonEnumerables,
13 json: true,
14 print: true
15 });
16};
17
18/**
19 * Pretty print to the console.
20 * @param anything : any value
21 * @param {Object=} options : print options
22 */
23
24console.pretty = console.pp = function (...anything) {
25 let output = '';
26 for (const item of anything) {
27 output += prettyPrint(item);
28 }
29 return output;
30};
31
32const darkTheme = {
33 Boolean: '#00875f',
34 Date: '#d7d75f',
35 Function: '#5fd7ff',
36 Map: '#5fd787',
37 Null: '#5f00af',
38 Number: '#875fd7',
39 RegExp: '#ff8787',
40 Set: '#afd7d7',
41 String: '#005fd7',
42 Symbol: '#afaf5f',
43 Undefined: '#af0087',
44 Unknown: '#d78700',
45 circular: 'fg: #af0000; style: bold;',
46 decoration: '#1c1c1c',
47 nonEnumerable: '#ff0087',
48 property: '#8a8a8a'
49};
50
51/**
52 * Pretty Print any value with colorization.
53 * @param {*} object : any object or value
54 * @param {{
55 * all : boolean|undefined,
56 * print : boolean|undefined,
57 * json : boolean|undefined,
58 * lineNumbers : boolean|undefined,
59 * showDepth : boolean|undefined,
60 * theme : Object|undefined }} options
61 */
62
63function prettyPrint (object, {
64 all = false,
65 color = true,
66 json = false,
67 lineNumbers = false,
68 print = true,
69 showDepth = true,
70 theme = darkTheme
71} = {}) {
72 let style;
73 if (color && process.stdout && process.stdout.isTTY) {
74 style = require('./style');
75 } else {
76 style = (string) => { return string; };
77 }
78
79 function indent (depth) {
80 return ' '.repeat(depth);
81 }
82
83 function addLineNumbers (output) {
84 const lines = output.split(/\n/);
85 const padding = lines.length.toString().length + 1;
86 let number = 0;
87 return lines.map((line) => {
88 number++;
89 const lineNumber = `${ ' '.repeat(padding - number.toString().length) +
90 style(number, theme.property) } \u2502 `;
91 return lineNumber + line;
92 }).join('\n');
93 }
94
95 function prettyPrinter (value, depth, seen, overrideColor) {
96 let line = indent(depth);
97
98 seen = new Set(seen);
99
100 if (typeof value === 'object' && seen.has(value)) {
101 line += style('[Circular Reference]', theme.circular);
102 } else {
103 if (typeof value === 'object' && value !== null) {
104 seen.add(value);
105 }
106
107 if (typeof value === 'string') {
108 if (overrideColor && !json) {
109 line += style(value, overrideColor);
110 } else if (json) {
111 line += style(`"${ value }"`, overrideColor || theme.String);
112 } else {
113 line += style(`'${ value }'`, overrideColor || theme.String);
114 }
115 } else if (typeof value === 'number') {
116 line += style(value, overrideColor || theme.Number);
117 } else if (typeof value === 'boolean') {
118 line += style(value, overrideColor || theme.Boolean);
119 } else if (value === undefined) {
120 line += style(value, theme.Undefined);
121 } else if (value === null) {
122 line += style(value, theme.Null);
123 } else if (value instanceof Date) {
124 line += style(value.toString(), theme.Date);
125 } else if (value instanceof RegExp) {
126 line += style(value.toString(), theme.RegExp);
127 } else if (typeof value === 'function') {
128 line += style(value.toString(), theme.Function);
129 } else if (typeof value === 'symbol') {
130 line += style(value.toString(), theme.Symbol);
131 } else if (Array.isArray(value)) {
132 line += '[';
133 if (value.length) {
134 line += '\n';
135 }
136
137 depth++;
138 for (let i = 0; i < value.length; i++) {
139 const comma = i < value.length - 1 ? ',' : '';
140 line += `${ prettyPrinter(value[i], depth, seen) + comma }\n`;
141 }
142 depth--;
143 line += `${ indent(depth) }]`;
144 } else if (value instanceof WeakMap) {
145 line += `${ style('WeakMap', theme.Map) } {}`;
146 } else if (value instanceof Map && !json) {
147 line += `${ style('Map', theme.Map) } {`;
148 if (value.size) {
149 line += '\n';
150 }
151
152 depth++;
153 let j = 0;
154 value.forEach((itemValue, key) => {
155 const comma = j < value.size - 1 ? ',' : '';
156 line += `${ prettyPrinter(key, depth, seen) }: `;
157 line += `${ prettyPrinter(itemValue, depth, seen) + comma }\n`;
158 j++;
159 });
160
161 depth--;
162 line += `${ indent(depth) }}`;
163 } else if (value instanceof WeakSet) {
164 line += `${ style('WeakSet', theme.Set) } []`;
165 } else if (value instanceof Set && !json) {
166 line += `${ style('Set', theme.Set) } [`;
167 if (value.size) {
168 line += '\n';
169 }
170
171 depth++;
172 let j = 0;
173 value.forEach((itemValue) => {
174 const comma = j < value.size - 1 ? ',' : '';
175 line += `${ prettyPrinter(itemValue, depth, seen) + comma }\n`;
176 j++;
177 });
178
179 depth--;
180 line += `${ indent(depth) }]`;
181 } else if (typeof value === 'object') {
182 line += '{';
183 let keys = Object.getOwnPropertyNames(value);
184 if (keys.length) {
185 line += '\n';
186 }
187
188 const enumerables = {};
189 keys = keys.filter((key) => {
190 const descriptor = Object.getOwnPropertyDescriptor(value, key);
191 enumerables[key] = descriptor.enumerable;
192 return descriptor.enumerable === true || all === true;
193 });
194
195 depth++;
196 for (let j = 0; j < keys.length; j++) {
197 const key = keys[j];
198 const comma = j < keys.length - 1 ? ',' : '';
199 const keyColor = enumerables[key] ? theme.property : theme.nonEnumerable;
200 line += `${ prettyPrinter(key, depth, seen, keyColor) }: `;
201 line += `${ prettyPrinter(value[key], depth, seen) + comma }\n`;
202 }
203 depth--;
204 line += `${ indent(depth) }}`;
205 } else {
206 line += style(value.toString(), theme.Unknown);
207 }
208 }
209
210 return line.replace(/:\s+/g, ': ').
211 replace(/([{[])\s+([}\]])/g, '$1$2');
212 }
213 let output = prettyPrinter(object, 0);
214
215 if (showDepth) {
216 output = output.replace(/\n {2}(\s+)/g, (match, spaces) => {
217 return `\n ${ spaces.substring(2).split(/ {2}/).
218 map(() => { return style('\u2502 ', theme.decoration); }).
219 join('') }`;
220 });
221 }
222
223 if (lineNumbers) {
224 output = addLineNumbers(output);
225 }
226
227 if (print !== false) {
228 console.log(output);
229 }
230 return output;
231}
232
233module.exports = prettyPrint;