UNPKG

11.2 kBJavaScriptView Raw
1'use strict';
2
3const printString = require('./printString');
4
5const toString = Object.prototype.toString;
6const toISOString = Date.prototype.toISOString;
7const errorToString = Error.prototype.toString;
8const regExpToString = RegExp.prototype.toString;
9const symbolToString = Symbol.prototype.toString;
10
11const SYMBOL_REGEXP = /^Symbol\((.*)\)(.*)$/;
12const NEWLINE_REGEXP = /\n/ig;
13
14const getSymbols = Object.getOwnPropertySymbols || (obj => []);
15
16function isToStringedArrayType(toStringed) {
17 return (
18 toStringed === '[object Array]' ||
19 toStringed === '[object ArrayBuffer]' ||
20 toStringed === '[object DataView]' ||
21 toStringed === '[object Float32Array]' ||
22 toStringed === '[object Float64Array]' ||
23 toStringed === '[object Int8Array]' ||
24 toStringed === '[object Int16Array]' ||
25 toStringed === '[object Int32Array]' ||
26 toStringed === '[object Uint8Array]' ||
27 toStringed === '[object Uint8ClampedArray]' ||
28 toStringed === '[object Uint16Array]' ||
29 toStringed === '[object Uint32Array]'
30 );
31}
32
33function printNumber(val) {
34 if (val != +val) return 'NaN';
35 const isNegativeZero = val === 0 && (1 / val) < 0;
36 return isNegativeZero ? '-0' : '' + val;
37}
38
39function printFunction(val) {
40 if (val.name === '') {
41 return '[Function anonymous]'
42 } else {
43 return '[Function ' + val.name + ']';
44 }
45}
46
47function printSymbol(val) {
48 return symbolToString.call(val).replace(SYMBOL_REGEXP, 'Symbol($1)');
49}
50
51function printError(val) {
52 return '[' + errorToString.call(val) + ']';
53}
54
55function printBasicValue(val) {
56 if (val === true || val === false) return '' + val;
57 if (val === undefined) return 'undefined';
58 if (val === null) return 'null';
59
60 const typeOf = typeof val;
61
62 if (typeOf === 'number') return printNumber(val);
63 if (typeOf === 'string') return '"' + printString(val) + '"';
64 if (typeOf === 'function') return printFunction(val);
65 if (typeOf === 'symbol') return printSymbol(val);
66
67 const toStringed = toString.call(val);
68
69 if (toStringed === '[object WeakMap]') return 'WeakMap {}';
70 if (toStringed === '[object WeakSet]') return 'WeakSet {}';
71 if (toStringed === '[object Function]' || toStringed === '[object GeneratorFunction]') return printFunction(val, min);
72 if (toStringed === '[object Symbol]') return printSymbol(val);
73 if (toStringed === '[object Date]') return toISOString.call(val);
74 if (toStringed === '[object Error]') return printError(val);
75 if (toStringed === '[object RegExp]') return regExpToString.call(val);
76 if (toStringed === '[object Arguments]' && val.length === 0) return 'Arguments []';
77 if (isToStringedArrayType(toStringed) && val.length === 0) return val.constructor.name + ' []';
78
79 if (val instanceof Error) return printError(val);
80
81 return false;
82}
83
84function printList(list, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON) {
85 let body = '';
86
87 if (list.length) {
88 body += edgeSpacing;
89
90 const innerIndent = prevIndent + indent;
91
92 for (let i = 0; i < list.length; i++) {
93 body += innerIndent + print(list[i], indent, innerIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON);
94
95 if (i < list.length - 1) {
96 body += ',' + spacing;
97 }
98 }
99
100 body += (min ? '' : ',') + edgeSpacing + prevIndent;
101 }
102
103 return '[' + body + ']';
104}
105
106function printArguments(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON) {
107 return (min ? '' : 'Arguments ') + printList(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON);
108}
109
110function printArray(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON) {
111 return (min ? '' : val.constructor.name + ' ') + printList(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON);
112}
113
114function printMap(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON) {
115 let result = 'Map {';
116 const iterator = val.entries();
117 let current = iterator.next();
118
119 if (!current.done) {
120 result += edgeSpacing;
121
122 const innerIndent = prevIndent + indent;
123
124 while (!current.done) {
125 const key = print(current.value[0], indent, innerIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON);
126 const value = print(current.value[1], indent, innerIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON);
127
128 result += innerIndent + key + ' => ' + value;
129
130 current = iterator.next();
131
132 if (!current.done) {
133 result += ',' + spacing;
134 }
135 }
136
137 result += (min ? '' : ',') + edgeSpacing + prevIndent;
138 }
139
140 return result + '}';
141}
142
143function printObject(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON) {
144 const constructor = min ? '' : (val.constructor ? val.constructor.name + ' ' : 'Object ');
145 let result = constructor + '{';
146 let keys = Object.keys(val).sort();
147 const symbols = getSymbols(val);
148
149 if (symbols.length) {
150 keys = keys
151 .filter(key => !(typeof key === 'symbol' || toString.call(key) === '[object Symbol]'))
152 .concat(symbols);
153 }
154
155 if (keys.length) {
156 result += edgeSpacing;
157
158 const innerIndent = prevIndent + indent;
159
160 for (let i = 0; i < keys.length; i++) {
161 const key = keys[i];
162 const name = print(key, indent, innerIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON);
163 const value = print(val[key], indent, innerIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON);
164
165 result += innerIndent + name + ': ' + value;
166
167 if (i < keys.length - 1) {
168 result += ',' + spacing;
169 }
170 }
171
172 result += (min ? '' : ',') + edgeSpacing + prevIndent;
173 }
174
175 return result + '}';
176}
177
178function printSet(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON) {
179 let result = 'Set {';
180 const iterator = val.entries();
181 let current = iterator.next();
182
183 if (!current.done) {
184 result += edgeSpacing;
185
186 const innerIndent = prevIndent + indent;
187
188 while (!current.done) {
189 result += innerIndent + print(current.value[1], indent, innerIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON);
190
191 current = iterator.next();
192
193 if (!current.done) {
194 result += ',' + spacing;
195 }
196 }
197
198 result += (min ? '' : ',') + edgeSpacing + prevIndent;
199 }
200
201 return result + '}';
202}
203
204function printComplexValue(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON) {
205 refs = refs.slice();
206 if (refs.indexOf(val) > -1) {
207 return '[Circular]';
208 } else {
209 refs.push(val);
210 }
211
212 currentDepth++;
213
214 const hitMaxDepth = currentDepth > maxDepth;
215
216 if (callToJSON && !hitMaxDepth && val.toJSON && typeof val.toJSON === 'function') {
217 return print(val.toJSON(), indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON);
218 }
219
220 const toStringed = toString.call(val);
221 if (toStringed === '[object Arguments]') {
222 return hitMaxDepth ? '[Arguments]' : printArguments(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON);
223 } else if (isToStringedArrayType(toStringed)) {
224 return hitMaxDepth ? '[Array]' : printArray(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON);
225 } else if (toStringed === '[object Map]') {
226 return hitMaxDepth ? '[Map]' : printMap(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON);
227 } else if (toStringed === '[object Set]') {
228 return hitMaxDepth ? '[Set]' : printSet(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON);
229 } else if (typeof val === 'object') {
230 return hitMaxDepth ? '[Object]' : printObject(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON);
231 }
232}
233
234function printPlugin(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON) {
235 let match = false;
236 let plugin;
237
238 for (let p = 0; p < plugins.length; p++) {
239 plugin = plugins[p];
240
241 if (plugin.test(val)) {
242 match = true;
243 break;
244 }
245 }
246
247 if (!match) {
248 return false;
249 }
250
251 function boundPrint(val) {
252 return print(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON);
253 }
254
255 function boundIndent(str) {
256 const indentation = prevIndent + indent;
257 return indentation + str.replace(NEWLINE_REGEXP, '\n' + indentation);
258 }
259
260 return plugin.print(val, boundPrint, boundIndent, {
261 edgeSpacing: edgeSpacing,
262 spacing: spacing
263 });
264}
265
266function print(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON) {
267 const basic = printBasicValue(val);
268 if (basic) return basic;
269
270 const plugin = printPlugin(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON);
271 if (plugin) return plugin;
272
273 return printComplexValue(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON);
274}
275
276const DEFAULTS = {
277 callToJSON: true,
278 indent: 2,
279 maxDepth: Infinity,
280 min: false,
281 plugins: [],
282};
283
284function validateOptions(opts) {
285 Object.keys(opts).forEach(key => {
286 if (!DEFAULTS.hasOwnProperty(key)) {
287 throw new Error('prettyFormat: Invalid option: ' + key);
288 }
289 });
290
291 if (opts.min && opts.indent !== undefined && opts.indent !== 0) {
292 throw new Error('prettyFormat: Cannot run with min option and indent');
293 }
294}
295
296function normalizeOptions(opts) {
297 const result = {};
298
299 Object.keys(DEFAULTS).forEach(key =>
300 result[key] = opts.hasOwnProperty(key) ? opts[key] : DEFAULTS[key]
301 );
302
303 if (result.min) {
304 result.indent = 0;
305 }
306
307 result.callToJSON = !!opts.callToJSON;
308
309 return result;
310}
311
312function createIndent(indent) {
313 return new Array(indent + 1).join(' ');
314}
315
316function prettyFormat(val, opts) {
317 if (!opts) {
318 opts = DEFAULTS;
319 } else {
320 validateOptions(opts)
321 opts = normalizeOptions(opts);
322 }
323
324 let indent;
325 let refs;
326 const prevIndent = '';
327 const currentDepth = 0;
328 const spacing = opts.min ? ' ' : '\n';
329 const edgeSpacing = opts.min ? '' : '\n';
330
331 if (opts && opts.plugins.length) {
332 indent = createIndent(opts.indent);
333 refs = [];
334 var pluginsResult = printPlugin(val, indent, prevIndent, spacing, edgeSpacing, refs, opts.maxDepth, currentDepth, opts.plugins, opts.min, opts.callToJSON);
335 if (pluginsResult) return pluginsResult;
336 }
337
338 var basicResult = printBasicValue(val);
339 if (basicResult) return basicResult;
340
341 if (!indent) indent = createIndent(opts.indent);
342 if (!refs) refs = [];
343 return printComplexValue(val, indent, prevIndent, spacing, edgeSpacing, refs, opts.maxDepth, currentDepth, opts.plugins, opts.min, opts.callToJSON);
344}
345
346module.exports = prettyFormat;