UNPKG

14.5 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', {
4 value: true
5});
6exports.format = format;
7exports.default = exports.plugins = exports.DEFAULT_OPTIONS = void 0;
8
9var _ansiStyles = _interopRequireDefault(require('ansi-styles'));
10
11var _collections = require('./collections');
12
13var _AsymmetricMatcher = _interopRequireDefault(
14 require('./plugins/AsymmetricMatcher')
15);
16
17var _ConvertAnsi = _interopRequireDefault(require('./plugins/ConvertAnsi'));
18
19var _DOMCollection = _interopRequireDefault(require('./plugins/DOMCollection'));
20
21var _DOMElement = _interopRequireDefault(require('./plugins/DOMElement'));
22
23var _Immutable = _interopRequireDefault(require('./plugins/Immutable'));
24
25var _ReactElement = _interopRequireDefault(require('./plugins/ReactElement'));
26
27var _ReactTestComponent = _interopRequireDefault(
28 require('./plugins/ReactTestComponent')
29);
30
31function _interopRequireDefault(obj) {
32 return obj && obj.__esModule ? obj : {default: obj};
33}
34
35/**
36 * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
37 *
38 * This source code is licensed under the MIT license found in the
39 * LICENSE file in the root directory of this source tree.
40 */
41
42/* eslint-disable local/ban-types-eventually */
43const toString = Object.prototype.toString;
44const toISOString = Date.prototype.toISOString;
45const errorToString = Error.prototype.toString;
46const regExpToString = RegExp.prototype.toString;
47/**
48 * Explicitly comparing typeof constructor to function avoids undefined as name
49 * when mock identity-obj-proxy returns the key as the value for any key.
50 */
51
52const getConstructorName = val =>
53 (typeof val.constructor === 'function' && val.constructor.name) || 'Object';
54/* global window */
55
56/** Is val is equal to global window object? Works even if it does not exist :) */
57
58const isWindow = val => typeof window !== 'undefined' && val === window;
59
60const SYMBOL_REGEXP = /^Symbol\((.*)\)(.*)$/;
61const NEWLINE_REGEXP = /\n/gi;
62
63class PrettyFormatPluginError extends Error {
64 constructor(message, stack) {
65 super(message);
66 this.stack = stack;
67 this.name = this.constructor.name;
68 }
69}
70
71function isToStringedArrayType(toStringed) {
72 return (
73 toStringed === '[object Array]' ||
74 toStringed === '[object ArrayBuffer]' ||
75 toStringed === '[object DataView]' ||
76 toStringed === '[object Float32Array]' ||
77 toStringed === '[object Float64Array]' ||
78 toStringed === '[object Int8Array]' ||
79 toStringed === '[object Int16Array]' ||
80 toStringed === '[object Int32Array]' ||
81 toStringed === '[object Uint8Array]' ||
82 toStringed === '[object Uint8ClampedArray]' ||
83 toStringed === '[object Uint16Array]' ||
84 toStringed === '[object Uint32Array]'
85 );
86}
87
88function printNumber(val) {
89 return Object.is(val, -0) ? '-0' : String(val);
90}
91
92function printBigInt(val) {
93 return String(`${val}n`);
94}
95
96function printFunction(val, printFunctionName) {
97 if (!printFunctionName) {
98 return '[Function]';
99 }
100
101 return '[Function ' + (val.name || 'anonymous') + ']';
102}
103
104function printSymbol(val) {
105 return String(val).replace(SYMBOL_REGEXP, 'Symbol($1)');
106}
107
108function printError(val) {
109 return '[' + errorToString.call(val) + ']';
110}
111/**
112 * The first port of call for printing an object, handles most of the
113 * data-types in JS.
114 */
115
116function printBasicValue(val, printFunctionName, escapeRegex, escapeString) {
117 if (val === true || val === false) {
118 return '' + val;
119 }
120
121 if (val === undefined) {
122 return 'undefined';
123 }
124
125 if (val === null) {
126 return 'null';
127 }
128
129 const typeOf = typeof val;
130
131 if (typeOf === 'number') {
132 return printNumber(val);
133 }
134
135 if (typeOf === 'bigint') {
136 return printBigInt(val);
137 }
138
139 if (typeOf === 'string') {
140 if (escapeString) {
141 return '"' + val.replace(/"|\\/g, '\\$&') + '"';
142 }
143
144 return '"' + val + '"';
145 }
146
147 if (typeOf === 'function') {
148 return printFunction(val, printFunctionName);
149 }
150
151 if (typeOf === 'symbol') {
152 return printSymbol(val);
153 }
154
155 const toStringed = toString.call(val);
156
157 if (toStringed === '[object WeakMap]') {
158 return 'WeakMap {}';
159 }
160
161 if (toStringed === '[object WeakSet]') {
162 return 'WeakSet {}';
163 }
164
165 if (
166 toStringed === '[object Function]' ||
167 toStringed === '[object GeneratorFunction]'
168 ) {
169 return printFunction(val, printFunctionName);
170 }
171
172 if (toStringed === '[object Symbol]') {
173 return printSymbol(val);
174 }
175
176 if (toStringed === '[object Date]') {
177 return isNaN(+val) ? 'Date { NaN }' : toISOString.call(val);
178 }
179
180 if (toStringed === '[object Error]') {
181 return printError(val);
182 }
183
184 if (toStringed === '[object RegExp]') {
185 if (escapeRegex) {
186 // https://github.com/benjamingr/RegExp.escape/blob/main/polyfill.js
187 return regExpToString.call(val).replace(/[\\^$*+?.()|[\]{}]/g, '\\$&');
188 }
189
190 return regExpToString.call(val);
191 }
192
193 if (val instanceof Error) {
194 return printError(val);
195 }
196
197 return null;
198}
199/**
200 * Handles more complex objects ( such as objects with circular references.
201 * maps and sets etc )
202 */
203
204function printComplexValue(
205 val,
206 config,
207 indentation,
208 depth,
209 refs,
210 hasCalledToJSON
211) {
212 if (refs.indexOf(val) !== -1) {
213 return '[Circular]';
214 }
215
216 refs = refs.slice();
217 refs.push(val);
218 const hitMaxDepth = ++depth > config.maxDepth;
219 const min = config.min;
220
221 if (
222 config.callToJSON &&
223 !hitMaxDepth &&
224 val.toJSON &&
225 typeof val.toJSON === 'function' &&
226 !hasCalledToJSON
227 ) {
228 return printer(val.toJSON(), config, indentation, depth, refs, true);
229 }
230
231 const toStringed = toString.call(val);
232
233 if (toStringed === '[object Arguments]') {
234 return hitMaxDepth
235 ? '[Arguments]'
236 : (min ? '' : 'Arguments ') +
237 '[' +
238 (0, _collections.printListItems)(
239 val,
240 config,
241 indentation,
242 depth,
243 refs,
244 printer
245 ) +
246 ']';
247 }
248
249 if (isToStringedArrayType(toStringed)) {
250 return hitMaxDepth
251 ? '[' + val.constructor.name + ']'
252 : (min
253 ? ''
254 : !config.printBasicPrototype && val.constructor.name === 'Array'
255 ? ''
256 : val.constructor.name + ' ') +
257 '[' +
258 (0, _collections.printListItems)(
259 val,
260 config,
261 indentation,
262 depth,
263 refs,
264 printer
265 ) +
266 ']';
267 }
268
269 if (toStringed === '[object Map]') {
270 return hitMaxDepth
271 ? '[Map]'
272 : 'Map {' +
273 (0, _collections.printIteratorEntries)(
274 val.entries(),
275 config,
276 indentation,
277 depth,
278 refs,
279 printer,
280 ' => '
281 ) +
282 '}';
283 }
284
285 if (toStringed === '[object Set]') {
286 return hitMaxDepth
287 ? '[Set]'
288 : 'Set {' +
289 (0, _collections.printIteratorValues)(
290 val.values(),
291 config,
292 indentation,
293 depth,
294 refs,
295 printer
296 ) +
297 '}';
298 } // Avoid failure to serialize global window object in jsdom test environment.
299 // For example, not even relevant if window is prop of React element.
300
301 return hitMaxDepth || isWindow(val)
302 ? '[' + getConstructorName(val) + ']'
303 : (min
304 ? ''
305 : !config.printBasicPrototype && getConstructorName(val) === 'Object'
306 ? ''
307 : getConstructorName(val) + ' ') +
308 '{' +
309 (0, _collections.printObjectProperties)(
310 val,
311 config,
312 indentation,
313 depth,
314 refs,
315 printer
316 ) +
317 '}';
318}
319
320function isNewPlugin(plugin) {
321 return plugin.serialize != null;
322}
323
324function printPlugin(plugin, val, config, indentation, depth, refs) {
325 let printed;
326
327 try {
328 printed = isNewPlugin(plugin)
329 ? plugin.serialize(val, config, indentation, depth, refs, printer)
330 : plugin.print(
331 val,
332 valChild => printer(valChild, config, indentation, depth, refs),
333 str => {
334 const indentationNext = indentation + config.indent;
335 return (
336 indentationNext +
337 str.replace(NEWLINE_REGEXP, '\n' + indentationNext)
338 );
339 },
340 {
341 edgeSpacing: config.spacingOuter,
342 min: config.min,
343 spacing: config.spacingInner
344 },
345 config.colors
346 );
347 } catch (error) {
348 throw new PrettyFormatPluginError(error.message, error.stack);
349 }
350
351 if (typeof printed !== 'string') {
352 throw new Error(
353 `pretty-format: Plugin must return type "string" but instead returned "${typeof printed}".`
354 );
355 }
356
357 return printed;
358}
359
360function findPlugin(plugins, val) {
361 for (let p = 0; p < plugins.length; p++) {
362 try {
363 if (plugins[p].test(val)) {
364 return plugins[p];
365 }
366 } catch (error) {
367 throw new PrettyFormatPluginError(error.message, error.stack);
368 }
369 }
370
371 return null;
372}
373
374function printer(val, config, indentation, depth, refs, hasCalledToJSON) {
375 const plugin = findPlugin(config.plugins, val);
376
377 if (plugin !== null) {
378 return printPlugin(plugin, val, config, indentation, depth, refs);
379 }
380
381 const basicResult = printBasicValue(
382 val,
383 config.printFunctionName,
384 config.escapeRegex,
385 config.escapeString
386 );
387
388 if (basicResult !== null) {
389 return basicResult;
390 }
391
392 return printComplexValue(
393 val,
394 config,
395 indentation,
396 depth,
397 refs,
398 hasCalledToJSON
399 );
400}
401
402const DEFAULT_THEME = {
403 comment: 'gray',
404 content: 'reset',
405 prop: 'yellow',
406 tag: 'cyan',
407 value: 'green'
408};
409const DEFAULT_THEME_KEYS = Object.keys(DEFAULT_THEME);
410const DEFAULT_OPTIONS = {
411 callToJSON: true,
412 escapeRegex: false,
413 escapeString: true,
414 highlight: false,
415 indent: 2,
416 maxDepth: Infinity,
417 min: false,
418 plugins: [],
419 printBasicPrototype: true,
420 printFunctionName: true,
421 theme: DEFAULT_THEME
422};
423exports.DEFAULT_OPTIONS = DEFAULT_OPTIONS;
424
425function validateOptions(options) {
426 Object.keys(options).forEach(key => {
427 if (!DEFAULT_OPTIONS.hasOwnProperty(key)) {
428 throw new Error(`pretty-format: Unknown option "${key}".`);
429 }
430 });
431
432 if (options.min && options.indent !== undefined && options.indent !== 0) {
433 throw new Error(
434 'pretty-format: Options "min" and "indent" cannot be used together.'
435 );
436 }
437
438 if (options.theme !== undefined) {
439 if (options.theme === null) {
440 throw new Error(`pretty-format: Option "theme" must not be null.`);
441 }
442
443 if (typeof options.theme !== 'object') {
444 throw new Error(
445 `pretty-format: Option "theme" must be of type "object" but instead received "${typeof options.theme}".`
446 );
447 }
448 }
449}
450
451const getColorsHighlight = options =>
452 DEFAULT_THEME_KEYS.reduce((colors, key) => {
453 const value =
454 options.theme && options.theme[key] !== undefined
455 ? options.theme[key]
456 : DEFAULT_THEME[key];
457 const color = value && _ansiStyles.default[value];
458
459 if (
460 color &&
461 typeof color.close === 'string' &&
462 typeof color.open === 'string'
463 ) {
464 colors[key] = color;
465 } else {
466 throw new Error(
467 `pretty-format: Option "theme" has a key "${key}" whose value "${value}" is undefined in ansi-styles.`
468 );
469 }
470
471 return colors;
472 }, Object.create(null));
473
474const getColorsEmpty = () =>
475 DEFAULT_THEME_KEYS.reduce((colors, key) => {
476 colors[key] = {
477 close: '',
478 open: ''
479 };
480 return colors;
481 }, Object.create(null));
482
483const getPrintFunctionName = options =>
484 options && options.printFunctionName !== undefined
485 ? options.printFunctionName
486 : DEFAULT_OPTIONS.printFunctionName;
487
488const getEscapeRegex = options =>
489 options && options.escapeRegex !== undefined
490 ? options.escapeRegex
491 : DEFAULT_OPTIONS.escapeRegex;
492
493const getEscapeString = options =>
494 options && options.escapeString !== undefined
495 ? options.escapeString
496 : DEFAULT_OPTIONS.escapeString;
497
498const getConfig = options => {
499 var _options$printBasicPr;
500
501 return {
502 callToJSON:
503 options && options.callToJSON !== undefined
504 ? options.callToJSON
505 : DEFAULT_OPTIONS.callToJSON,
506 colors:
507 options && options.highlight
508 ? getColorsHighlight(options)
509 : getColorsEmpty(),
510 escapeRegex: getEscapeRegex(options),
511 escapeString: getEscapeString(options),
512 indent:
513 options && options.min
514 ? ''
515 : createIndent(
516 options && options.indent !== undefined
517 ? options.indent
518 : DEFAULT_OPTIONS.indent
519 ),
520 maxDepth:
521 options && options.maxDepth !== undefined
522 ? options.maxDepth
523 : DEFAULT_OPTIONS.maxDepth,
524 min:
525 options && options.min !== undefined ? options.min : DEFAULT_OPTIONS.min,
526 plugins:
527 options && options.plugins !== undefined
528 ? options.plugins
529 : DEFAULT_OPTIONS.plugins,
530 printBasicPrototype:
531 (_options$printBasicPr =
532 options === null || options === void 0
533 ? void 0
534 : options.printBasicPrototype) !== null &&
535 _options$printBasicPr !== void 0
536 ? _options$printBasicPr
537 : true,
538 printFunctionName: getPrintFunctionName(options),
539 spacingInner: options && options.min ? ' ' : '\n',
540 spacingOuter: options && options.min ? '' : '\n'
541 };
542};
543
544function createIndent(indent) {
545 return new Array(indent + 1).join(' ');
546}
547/**
548 * Returns a presentation string of your `val` object
549 * @param val any potential JavaScript object
550 * @param options Custom settings
551 */
552
553function format(val, options) {
554 if (options) {
555 validateOptions(options);
556
557 if (options.plugins) {
558 const plugin = findPlugin(options.plugins, val);
559
560 if (plugin !== null) {
561 return printPlugin(plugin, val, getConfig(options), '', 0, []);
562 }
563 }
564 }
565
566 const basicResult = printBasicValue(
567 val,
568 getPrintFunctionName(options),
569 getEscapeRegex(options),
570 getEscapeString(options)
571 );
572
573 if (basicResult !== null) {
574 return basicResult;
575 }
576
577 return printComplexValue(val, getConfig(options), '', 0, []);
578}
579
580const plugins = {
581 AsymmetricMatcher: _AsymmetricMatcher.default,
582 ConvertAnsi: _ConvertAnsi.default,
583 DOMCollection: _DOMCollection.default,
584 DOMElement: _DOMElement.default,
585 Immutable: _Immutable.default,
586 ReactElement: _ReactElement.default,
587 ReactTestComponent: _ReactTestComponent.default
588};
589exports.plugins = plugins;
590var _default = format;
591exports.default = _default;