UNPKG

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